Krita Source Code Documentation
Loading...
Searching...
No Matches
KisInputManager Class Reference

Central object to manage canvas input. More...

#include <kis_input_manager.h>

+ Inheritance diagram for KisInputManager:

Classes

class  Private
 

Public Member Functions

void addTrackedCanvas (KisCanvas2 *canvas)
 
void attachPriorityEventFilter (QObject *filter, int priority=0)
 attachPriorityEventFilter
 
KisCanvas2canvas () const
 
void detachPriorityEventFilter (QObject *filter)
 detachPriorityEventFilter
 
bool eventFilter (QObject *object, QEvent *event) override
 
 KisInputManager (QObject *parent)
 
void registerPopupWidget (KisPopupWidgetInterface *popupWidget)
 
void removeTrackedCanvas (KisCanvas2 *canvas)
 
void setupAsEventFilter (QObject *receiver)
 
void toggleTabletLogger ()
 
QPointer< KisToolProxytoolProxy () const
 
 ~KisInputManager () override
 

Private Slots

void deregisterPopupWidget ()
 
void profileChanged ()
 
void slotAboutToChangeTool ()
 
void slotCompressedMoveEvent ()
 
void slotConfigChanged ()
 
void slotTextModeChanged ()
 
void slotToolChanged ()
 
void slotTouchHoldTriggered ()
 

Private Member Functions

template<class Event >
bool compressMoveEventCommon (Event *event)
 
void endTouch ()
 
bool eventFilterImpl (QEvent *event)
 
bool handleTouchBegin (QTouchEvent *touchEvent)
 
bool handleTouchUpdate (QTouchEvent *touchEvent)
 
bool startTouch (bool &retval)
 
bool touchHoldBufferUpdate (QTouchEvent *touchEvent)
 

Private Attributes

Private *const d
 

Detailed Description

Central object to manage canvas input.

The Input Manager class manages all canvas input. It is created by KisCanvas2 and processes all events related to input sent to the canvas.

The Input Manager keeps track of a set of actions and a set of shortcuts. The actions are pre-defined while the shortcuts are set from configuration.

For each event, it will try to determine if there is a shortcut that matches the input. It will then activate this action and pass all consecutive events on to this action.

See also
KisAbstractInputAction

Definition at line 37 of file kis_input_manager.h.

Constructor & Destructor Documentation

◆ KisInputManager()

KisInputManager::KisInputManager ( QObject * parent)

Constructor.

Definition at line 63 of file kis_input_manager.cpp.

64 : QObject(parent), d(new Private(this))
65{
66 d->setupActions();
67
68 connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
70
71 connect(KoToolManager::instance(), SIGNAL(aboutToChangeTool(KoCanvasController*)), SLOT(slotAboutToChangeTool()));
72 connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*)), SLOT(slotToolChanged()));
73 connect(KoToolManager::instance(), SIGNAL(textModeChanged(bool)), SLOT(slotTextModeChanged()));
74 connect(&d->moveEventCompressor, SIGNAL(timeout()), SLOT(slotCompressedMoveEvent()));
75
76
77 QApplication::instance()->
78 installEventFilter(new Private::ProximityNotifier(d, this));
79
80 // on macos global Monitor listen to keypresses when krita is not in focus
81 // and local monitor listen presses when krita is in focus.
82#ifdef Q_OS_MACOS
83 KisExtendedModifiersMapper::setLocalMonitor(true, &d->matcher);
84#endif
85}
static KisConfigNotifier * instance()
KisSignalCompressor moveEventCompressor
static KoToolManager * instance()
Return the toolmanager singleton.

References d, KoToolManager::instance(), KisConfigNotifier::instance(), KisInputManager::Private::matcher, KisInputManager::Private::moveEventCompressor, KisInputManager::Private::setupActions(), slotAboutToChangeTool(), slotCompressedMoveEvent(), slotConfigChanged(), slotTextModeChanged(), and slotToolChanged().

◆ ~KisInputManager()

KisInputManager::~KisInputManager ( )
override

Destructor.

Definition at line 87 of file kis_input_manager.cpp.

88{
89#ifdef Q_OS_MACOS
90 KisExtendedModifiersMapper::setLocalMonitor(false);
91#endif
92 delete d;
93}

References d.

Member Function Documentation

◆ addTrackedCanvas()

void KisInputManager::addTrackedCanvas ( KisCanvas2 * canvas)

◆ attachPriorityEventFilter()

void KisInputManager::attachPriorityEventFilter ( QObject * filter,
int priority = 0 )

attachPriorityEventFilter

Parameters
filter
priority

Definition at line 153 of file kis_input_manager.cpp.

154{
155 Private::PriorityList::iterator begin = d->priorityEventFilter.begin();
156 Private::PriorityList::iterator it = begin;
157 Private::PriorityList::iterator end = d->priorityEventFilter.end();
158
159 it = std::find_if(begin, end,
160 kismpl::mem_equal_to(&Private::PriorityPair::second, filter));
161
162 if (it != end) return;
163
164 it = std::find_if(begin, end,
165 kismpl::mem_greater(&Private::PriorityPair::first, priority));
166
167 d->priorityEventFilter.insert(it, qMakePair(priority, filter));
169}
auto mem_equal_to(MemTypeNoRef Class::*ptr, MemType &&value)
mem_equal_to is an unary functor that compares a member of the object to a given value
Definition KisMpl.h:233
auto mem_greater(MemTypeNoRef Class::*ptr, MemType &&value)
mem_greater is an unary functor that compares a member of the object to a given value
Definition KisMpl.h:341

References d, kismpl::mem_equal_to(), kismpl::mem_greater(), KisInputManager::Private::priorityEventFilter, and KisInputManager::Private::priorityEventFilterSeqNo.

◆ canvas()

KisCanvas2 * KisInputManager::canvas ( ) const

Return the canvas this input manager is associated with.

Definition at line 994 of file kis_input_manager.cpp.

995{
996 return d->canvas;
997}
QPointer< KisCanvas2 > canvas

References KisInputManager::Private::canvas, and d.

◆ compressMoveEventCommon()

template<class Event >
bool KisInputManager::compressMoveEventCommon ( Event * event)
private

We construct a copy of this event object, so we must ensure it has a correct type.

Compress the events if the tool doesn't need high resolution input

On Linux Qt eats the rest of unneeded events if we ignore the first of the chunk of tablet events. So generally we should never activate this feature. Only for testing purposes!

Definition at line 249 of file kis_input_manager.cpp.

250{
255 static_assert(std::is_same<Event, QMouseEvent>::value ||
256 std::is_same<Event, QTabletEvent>::value ||
257 std::is_same<Event, QTouchEvent>::value,
258 "event should be a mouse or a tablet event");
259
260
261 bool retval = false;
262
266 if ((event->type() == QEvent::MouseMove ||
267 event->type() == QEvent::TabletMove ||
268 event->type() == QEvent::TouchUpdate) &&
271#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
272 KoPointerEvent::copyQtPointerEvent(event, d->compressedMoveEvent);
273#else
274 d->compressedMoveEvent.reset(event->clone());
275#endif
277
285 event->setAccepted(true);
286 }
287
288 retval = true;
289 } else {
291 retval = d->handleCompressedTabletEvent(event);
292 }
293
294 return retval;
295}
bool handleCompressedTabletEvent(QEvent *event)
QScopedPointer< QEvent > compressedMoveEvent

References KisInputManager::Private::compressedMoveEvent, d, KisInputManager::Private::handleCompressedTabletEvent(), KisInputManager::Private::matcher, KisInputManager::Private::moveEventCompressor, slotCompressedMoveEvent(), KisSignalCompressor::start(), KisShortcutMatcher::supportsHiResInputEvents(), KisInputManager::Private::testingAcceptCompressedTabletEvents, and KisInputManager::Private::testingCompressBrushEvents.

◆ deregisterPopupWidget

void KisInputManager::deregisterPopupWidget ( )
privateslot

Definition at line 115 of file kis_input_manager.cpp.

116{
117 if (d->popupWidget->onScreen()) {
119 }
120
121 // FUNKY!
122 auto popupObject = dynamic_cast<QObject*>(d->popupWidget);
123 KIS_ASSERT(popupObject);
124 disconnect(popupObject, nullptr, this, nullptr); // Disconnect all.
125
126 d->popupWidget = nullptr;
127}
KisPopupWidgetInterface * popupWidget
virtual void dismiss()=0
Called when you want to dismiss a popup widget.
virtual bool onScreen()=0
Returns whether the widget is active (on screen) or not.
#define KIS_ASSERT(cond)
Definition kis_assert.h:33

References d, KisPopupWidgetInterface::dismiss(), KIS_ASSERT, KisPopupWidgetInterface::onScreen(), and KisInputManager::Private::popupWidget.

◆ detachPriorityEventFilter()

void KisInputManager::detachPriorityEventFilter ( QObject * filter)

detachPriorityEventFilter

Parameters
filter

Definition at line 171 of file kis_input_manager.cpp.

172{
173 Private::PriorityList::iterator it = d->priorityEventFilter.begin();
174 Private::PriorityList::iterator end = d->priorityEventFilter.end();
175
176 it = std::find_if(it, end,
177 kismpl::mem_equal_to(&Private::PriorityPair::second, filter));
178
179 if (it != end) {
180 d->priorityEventFilter.erase(it);
181 }
182}

References d, kismpl::mem_equal_to(), and KisInputManager::Private::priorityEventFilter.

◆ endTouch()

void KisInputManager::endTouch ( )
private

◆ eventFilter()

bool KisInputManager::eventFilter ( QObject * object,
QEvent * event )
override

Event filter method. Overridden from QObject.

If the filter removed itself from the filters list or added something there, just exit the loop

Definition at line 201 of file kis_input_manager.cpp.

202{
203 if (object != d->eventsReceiver) return false;
204
205 if (d->eventEater.eventFilter(object, event)) return false;
206
207 if (!d->matcher.hasRunningShortcut()) {
208
209 int savedPriorityEventFilterSeqNo = d->priorityEventFilterSeqNo;
210
211 for (auto it = d->priorityEventFilter.begin(); it != d->priorityEventFilter.end(); /*noop*/) {
212 const QPointer<QObject> &filter = it->second;
213
214 if (filter.isNull()) {
215 it = d->priorityEventFilter.erase(it);
216
218 savedPriorityEventFilterSeqNo++;
219 continue;
220 }
221
222 if (filter->eventFilter(object, event)) return true;
223
228 if (d->priorityEventFilterSeqNo != savedPriorityEventFilterSeqNo) {
229 return true;
230 }
231
232 ++it;
233 }
234
235 // KoToolProxy needs to pre-process some events to ensure the
236 // global shortcuts (not the input manager's ones) are not
237 // executed, in particular, this line will accept events when the
238 // tool is in text editing, preventing shortcut triggering
239 if (d->toolProxy) {
240 d->toolProxy->processEvent(event);
241 }
242 }
243
244 // Continue with the actual switch statement...
245 return eventFilterImpl(event);
246}
QPointer< KisToolProxy > toolProxy
bool eventFilterImpl(QEvent *event)
bool eventFilter(QObject *target, QEvent *event)

References d, KisInputManager::Private::eventEater, KisInputManager::Private::EventEater::eventFilter(), eventFilterImpl(), KisInputManager::Private::eventsReceiver, KisShortcutMatcher::hasRunningShortcut(), KisInputManager::Private::matcher, KisInputManager::Private::priorityEventFilter, KisInputManager::Private::priorityEventFilterSeqNo, and KisInputManager::Private::toolProxy.

◆ eventFilterImpl()

bool KisInputManager::eventFilterImpl ( QEvent * event)
private

When Krita (as an application) has no input focus, we cannot handle key events. But at the same time, when the user hovers Krita canvas, we should still show him the correct cursor.

So here we just add a simple workaround to resync shortcut matcher's state at least against the basic modifiers, like Shift, Control and Alt.

On Windows, when the user presses some global window manager shortcuts, e.g. Alt+Space (to show window title menu), events for these key presses and releases are not delivered (see bug 424319). This code is a workaround for this problem. It checks consistency of standard modifiers and resets shortcut's matcher state in case of a trouble.

Re-check the native platform key API against keys we are unsure about, and fix them in case they now show as released.

The other part of the fix is placed in the handler of ShortcutOverride, because it needs a custom set of the presset keys.

See a comment in the handler of KeyRelease event for shouldSynchronizeOnNextKeyPress explanation

There is also a case when Krita gets focus via Win+1 key, then the polled key '1' gets into the matcher, but OS does not deliver any signals for it (see bug 451424)

On some systems Qt fails to generate a correct sequence of events when the user releases a modifier key while some other key is pressed.

In such cases Qt doesn't understand that the key has changed its name in the meantime and sends incorrect key-release event for it (or an auto- repeated key-release/key-press pair).

Example (on en-US keyboard):

1) Press Shift (Key_Shift-press is delivered) 2) Press '2' (Key_At-press is delivered) 3) Release Shift (Key_Shift-release is delivered) 4) Release '2' (Key_2-release is delivered, which is unbalanced)

The same issue happens with non-latin keyboards, where Qt does auto-key-replace routines when Control modifier is pressed.

https://bugs.kde.org/show_bug.cgi?id=454256 https://bugreports.qt.io/browse/QTBUG-103868

Ignore delta 0 events on OSX, since they are triggered by tablet proximity when using Wacom devices.

We won't get a TabletProximityLeave event when the tablet is hovering above some other widget, so restore cursor events processing right now.

The flow of tablet events means the tablet is in the proximity area, so activate it even when the TabletEnterProximity event was missed (may happen when changing focus of the window with tablet in the proximity area)

Definition at line 316 of file kis_input_manager.cpp.

317{
318 bool retval = false;
319
320 // Try closing any open popup widget and
321 // consume input if possible.
322 if (d->popupWidget) {
323 QEvent::Type type = event->type();
324
325 if (type == QEvent::MouseButtonPress
326 || type == QEvent::MouseButtonDblClick
327 || type == QEvent::TabletPress
328 || type == QEvent::TouchBegin
329 || type == QEvent::NativeGesture) {
330 bool wasVisible = d->popupWidget->onScreen();
332
333 if (wasVisible) {
334 d->popupWasActive = true;
335 event->setAccepted(true);
336 return true; // Event consumed.
337 }
338 }
339 }
340
341 if (shouldResetWheelDelta(event)) {
343 }
344
345 if (event->type() == QEvent::MouseMove ||
346 event->type() == QEvent::MouseButtonPress ||
347 event->type() == QEvent::MouseButtonRelease ||
348 event->type() == QEvent::TabletMove ||
349 event->type() == QEvent::TabletPress ||
350 event->type() == QEvent::TabletRelease ||
351 event->type() == QEvent::Wheel) {
352
362 QWidget *receivingWidget = dynamic_cast<QWidget*>(d->eventsReceiver);
363 if (receivingWidget && !receivingWidget->hasFocus()) {
365 } else {
373 QInputEvent *inputEvent = static_cast<QInputEvent*>(event);
374 if (event->type() != QEvent::ShortcutOverride &&
375 !d->matcher.sanityCheckModifiersCorrectness(inputEvent->modifiers())) {
376
378 } else if (d->matcher.hasPolledKeys()) {
387 }
388 }
389 }
390
391 switch (event->type()) {
392 case QEvent::MouseButtonPress:
393 case QEvent::MouseButtonDblClick: {
394 d->debugEvent<QMouseEvent, true>(event);
395 if (d->touchHasBlockedPressEvents) break;
396
397 QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
398
399 if (d->popupWidget) {
400 retval = true;
401 } else {
402 //Make sure the input actions know we are active.
404 retval = d->matcher.buttonPressed(mouseEvent->button(), mouseEvent);
405 }
406 //Reset signal compressor to prevent processing events before press late
408 event->setAccepted(retval);
409 break;
410 }
411 case QEvent::MouseButtonRelease: {
412 d->debugEvent<QMouseEvent, true>(event);
413 if (d->touchHasBlockedPressEvents) break;
414
415 QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
416 retval = d->matcher.buttonReleased(mouseEvent->button(), mouseEvent);
417 event->setAccepted(retval);
418 break;
419 }
420 case QEvent::ShortcutOverride: {
421 d->debugEvent<QKeyEvent, false>(event);
422 QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
423
425
434 QVector<Qt::Key> guessedKeys;
436 Qt::KeyboardModifiers modifiers = mapper.queryStandardModifiers();
437 Q_FOREACH (Qt::Key key, mapper.queryExtendedModifiers()) {
438 QKeyEvent kevent(QEvent::ShortcutOverride, key, modifiers);
440 }
441
442 if (!d->matcher.debugPressedKeys().contains(key)) {
443 guessedKeys.removeOne(key);
444 }
445
446 d->fixShortcutMatcherModifiersState(guessedKeys, modifiers);
448 }
449
450 if (!keyEvent->isAutoRepeat()) {
451 retval = d->matcher.keyPressed(key);
452 } else {
453 retval = d->matcher.autoRepeatedKeyPressed(key);
454 }
455
456 // In case we matched a shortcut we should accept the event to
457 // notify Qt that it shouldn't try to trigger its partially matched
458 // shortcuts.
459 if (retval) {
460 keyEvent->setAccepted(true);
461 }
462
463 break;
464 }
465 case QEvent::KeyRelease: {
466 d->debugEvent<QKeyEvent, false>(event);
467 QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
468
469 if (!keyEvent->isAutoRepeat()) {
471 retval = d->matcher.keyReleased(key);
472
500 !d->matcher.debugPressedKeys().isEmpty() &&
501 (key == Qt::Key_Shift || key == Qt::Key_Alt ||
502 key == Qt::Key_Control || key == Qt::Key_Meta)) {
503
505 }
506 }
507 break;
508 }
509 case QEvent::MouseMove: {
510 d->debugEvent<QMouseEvent, true>(event);
511
512 QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
513 retval = compressMoveEventCommon(mouseEvent);
514
515 break;
516 }
517 case QEvent::Wheel: {
518 d->debugEvent<QWheelEvent, false>(event);
519 QWheelEvent *wheelEvent = static_cast<QWheelEvent*>(event);
520
521#ifdef Q_OS_MACOS
522 // Some QT wheel events are actually touch pad pan events. From the QT docs:
523 // "Wheel events are generated for both mouse wheels and trackpad scroll gestures."
524
525 // We differentiate between touchpad events and real mouse wheels by inspecting the
526 // event source.
527
528 if (wheelEvent->source() == Qt::MouseEventSource::MouseEventSynthesizedBySystem) {
531 break;
532 }
533#endif
534
535 d->accumulatedScrollDelta += wheelEvent->angleDelta().y();
537
542#ifdef Q_OS_MACOS
543 if (wheelEvent->angleDelta().isNull()) {
544 retval = true;
545 break;
546 }
547#endif
548
549 if (wheelEvent->angleDelta().x() < 0) {
551 } else if (wheelEvent->angleDelta().x() >0) {
553 }
554
555 if (wheelEvent->angleDelta().y() < 0) {
557 } else if (wheelEvent->angleDelta().y() > 0) {
559 }
560
561 bool wasScrolled = false;
562
563 while (qAbs(d->accumulatedScrollDelta) >= QWheelEvent::DefaultDeltasPerStep) {
564 //Make sure the input actions know we are active.
566 retval = d->matcher.wheelEvent(action, wheelEvent);
569 QWheelEvent::DefaultDeltasPerStep;
570 wasScrolled = true;
571 }
572
573 if (wasScrolled) {
575 }
576
577 retval = !wasScrolled;
578 break;
579 }
580#ifndef Q_OS_ANDROID
581 case QEvent::Enter:
582 d->debugEvent<QEvent, false>(event);
583 //Make sure the input actions know we are active.
585 if (!d->containsPointer) {
586 d->containsPointer = true;
587
590 }
592 break;
593 case QEvent::Leave:
594 d->debugEvent<QEvent, false>(event);
595 d->containsPointer = false;
603
605 break;
606#endif
607 case QEvent::FocusIn:
608 d->debugEvent<QEvent, false>(event);
610
613
615 break;
616
617 case QEvent::FocusOut: {
618 d->debugEvent<QEvent, false>(event);
620
621 QPointF currentLocalPos =
622 canvas()->canvasWidget()->mapFromGlobal(QCursor::pos());
623
624 d->matcher.lostFocusEvent(currentLocalPos);
625
626 break;
627 }
628 case QEvent::TabletPress: {
629 d->debugEvent<QTabletEvent, false>(event);
630 QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(event);
631
632 {
633 //Make sure the input actions know we are active.
635 retval = d->matcher.buttonPressed(tabletEvent->button(), tabletEvent);
636 if (!d->containsPointer) {
637 d->containsPointer = true;
639 }
640 }
641
642 event->setAccepted(true);
643 retval = true;
646 //Reset signal compressor to prevent processing events before press late
648
649
650#if defined Q_OS_LINUX && !KRITA_QT_HAS_ENTER_LEAVE_PATCH
651 // remove this hack when this patch is integrated:
652 // https://codereview.qt-project.org/#/c/255384/
653 event->setAccepted(false);
655#elif defined Q_OS_WIN32
661#endif
662
663 break;
664 }
665 case QEvent::TabletMove: {
666 d->debugEvent<QTabletEvent, false>(event);
667
668 QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(event);
669 retval = compressMoveEventCommon(tabletEvent);
670
671 if (d->tabletLatencyTracker) {
672 d->tabletLatencyTracker->push(tabletEvent->timestamp());
673 }
674
683
684#if defined Q_OS_LINUX && !KRITA_QT_HAS_ENTER_LEAVE_PATCH
685 // remove this hack when this patch is integrated:
686 // https://codereview.qt-project.org/#/c/255384/
687 event->setAccepted(false);
688#endif
689
690 break;
691 }
692 case QEvent::TabletRelease: {
693#if defined(Q_OS_MAC) || defined(Q_OS_ANDROID)
695#endif
697 d->debugEvent<QTabletEvent, false>(event);
698
699 QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(event);
700 retval = d->matcher.buttonReleased(tabletEvent->button(), tabletEvent);
701 retval = true;
702 event->setAccepted(true);
703
704#if defined Q_OS_LINUX && !KRITA_QT_HAS_ENTER_LEAVE_PATCH
705 // remove this hack when this patch is integrated:
706 // https://codereview.qt-project.org/#/c/255384/
707 event->setAccepted(false);
708#endif
709
710 break;
711 }
712
713 case QEvent::TouchBegin:
714 {
715 d->debugEvent<QTouchEvent, false>(event);
716 // The popup was dismissed in previous TouchBegin->TouchEnd sequence. We now have a new TouchBegin.
717 d->popupWasActive = false;
718 if (startTouch(retval)) {
719 QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event);
721 d->lastPointCount = touchEvent->touchPoints().size();
722 d->startingPos = touchEvent->touchPoints().at(0).pos();
724 // we don't want to lose this event
725#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
726 KoPointerEvent::copyQtPointerEvent(touchEvent, d->originatingTouchBeginEvent);
727#else
728 d->originatingTouchBeginEvent.reset(touchEvent->clone());
729#endif
730
731 // In the face of a touch and hold shortcut being present, we need
732 // to disambiguate whether this is a touch being held or the user
733 // is doing something else. For that purpose, we start buffering
734 // received touch events until we can actually make a decision.
736 if (d->matcher.hasTouchHoldShortcut() && touchEvent->touchPoints().length() == 1) {
737 d->bufferTouchEvent(touchEvent);
739 retval = true;
740 } else {
742 retval = handleTouchBegin(touchEvent);
743 }
744
746 d->touchStrokeStarted = false;
747 }
749 event->accept();
750 }
751 break;
752 }
753
754 case QEvent::TouchUpdate:
755 {
756 if (d->popupWasActive) {
757 event->setAccepted(true);
758 return true;
759 }
760 QTouchEvent *touchEvent = static_cast<QTouchEvent*>(event);
761 d->debugEvent<QTouchEvent, false>(event);
762
763 int eventPointCount = touchEvent->touchPoints().size();
764 d->lastPointCount = eventPointCount;
765
766#ifdef Q_OS_MAC
767 int count = 0;
768 Q_FOREACH (const QTouchEvent::TouchPoint &point, touchEvent->touchPoints()) {
769 if (point.state() != Qt::TouchPointReleased) {
770 count++;
771 }
772 }
773
774 if (count < 2 && eventPointCount > count) {
777 retval = d->matcher.touchEndEvent(touchEvent);
778 } else {
779#endif
780 // Touch hold shortcuts need to buffer events, see TouchBegin.
781 if (touchHoldBufferUpdate(touchEvent)) {
782 retval = true; // Event was buffered.
783 } else {
784 retval = handleTouchUpdate(touchEvent);
785 }
786#ifdef Q_OS_MACOS
787 }
788#endif
789 // if the event isn't handled, Qt starts to send MouseEvents
790 if (!KisConfig(true).disableTouchOnCanvas())
791 retval = true;
792
793 event->accept();
794 break;
795 }
796
797 case QEvent::TouchEnd:
798 {
801
802 if (d->popupWasActive) {
803 event->setAccepted(true);
804 return true;
805 }
806 d->debugEvent<QTouchEvent, false>(event);
807 QTouchEvent *touchEvent = static_cast<QTouchEvent*>(event);
808
809 retval = d->matcher.touchEndEvent(touchEvent);
810 if (d->touchStrokeStarted) {
811 retval = d->matcher.buttonReleased(Qt::LeftButton, touchEvent);
812 d->startingPos = {0, 0};
813 d->previousPos = {0, 0};
814 d->touchStrokeStarted = false; // stroke ended
816 && touchEvent->touchPoints().count() == 1) {
817 // If no stroke has been started while touch painting is enabled,
818 // the user tapped with one finger, but didn't make any motion that
819 // caused us to start a stroke. We produce a press and release in
820 // response so that the tool responds to their input.
821 d->matcher.buttonPressed(Qt::LeftButton, d->originatingTouchBeginEvent.data());
822 d->matcher.buttonReleased(Qt::LeftButton, touchEvent);
823 }
824
825 endTouch();
827
828 // if the event isn't handled, Qt starts to send MouseEvents
829 if (!KisConfig(true).disableTouchOnCanvas())
830 retval = true;
831
832 event->accept();
833 break;
834 }
835 case QEvent::TouchCancel:
836 {
837 // On some Android devices, such as Xiaomi Pads, the system always eats
838 // multitouch inputs with more than two fingers, even if the user
839 // disables all gestures related to them in their system settings or
840 // uses the game boost mode that is supposed to disable gestures. So we
841 // handle those inputs even when they are cancelled, if the user wants
842 // to use it for a system gesture, they can disable the Krita shortcut.
843#ifdef Q_OS_ANDROID
844 bool ignoreCancel = d->lastPointCount > 2;
845#else
846 bool ignoreCancel = false;
847#endif
848
850 if (ignoreCancel) {
852 } else {
854 }
855
856 if (d->popupWasActive) {
857 event->setAccepted(true);
858 return true;
859 }
860 d->debugEvent<QTouchEvent, false>(event);
861 endTouch();
863 QTouchEvent *touchEvent = static_cast<QTouchEvent*>(event);
864 if (ignoreCancel) {
865 d->matcher.touchEndEvent(touchEvent);
866 } else {
867 d->matcher.touchCancelEvent(touchEvent, d->previousPos);
868 }
869 // reset state
870 d->lastPointCount = 0;
871 d->startingPos = {0, 0};
872 d->previousPos = {0, 0};
873 d->touchStrokeStarted = false;
874 retval = true;
875 event->accept();
876 break;
877 }
878
879 case QEvent::NativeGesture:
880 {
881 QNativeGestureEvent *gevent = static_cast<QNativeGestureEvent*>(event);
882 switch (gevent->gestureType()) {
883 case Qt::BeginNativeGesture:
884 {
885 if (startTouch(retval)) {
887 retval = d->matcher.nativeGestureBeginEvent(gevent);
888 event->accept();
889 }
890 break;
891 }
892 case Qt::EndNativeGesture:
893 {
894 endTouch();
895 retval = d->matcher.nativeGestureEndEvent(gevent);
896 event->accept();
897 break;
898 }
899 default:
900 {
902 retval = d->matcher.nativeGestureEvent(gevent);
903 event->accept();
904 break;
905 }
906 }
907 break;
908 }
909
910 default:
911 break;
912 }
913
914 return !retval ? d->processUnhandledEvent(event) : true;
915}
static void setInputManager(KisInputManager *manager)
KisAbstractCanvasWidget * canvasWidget
bool disableTouchOnCanvas() const
static Qt::Key workaroundShiftAltMetaHell(const QKeyEvent *keyEvent)
void debugEvent(QEvent *event)
QScopedPointer< QEvent > originatingTouchBeginEvent
void bufferTouchEvent(QTouchEvent *event)
bool processUnhandledEvent(QEvent *event)
KisSharedPtr< TabletLatencyTracker > tabletLatencyTracker
bool handleTouchBegin(QTouchEvent *touchEvent)
bool compressMoveEventCommon(Event *event)
bool touchHoldBufferUpdate(QTouchEvent *touchEvent)
bool handleTouchUpdate(QTouchEvent *touchEvent)
bool startTouch(bool &retval)
void lostFocusEvent(const QPointF &localPos)
bool autoRepeatedKeyPressed(Qt::Key key)
bool buttonReleased(Qt::MouseButton button, QEvent *event)
bool nativeGestureEndEvent(QNativeGestureEvent *event)
bool keyPressed(Qt::Key key)
QVector< Qt::Key > debugPressedKeys() const
bool touchEndEvent(QTouchEvent *event)
bool nativeGestureEvent(QNativeGestureEvent *event)
bool buttonPressed(Qt::MouseButton button, QEvent *event)
bool sanityCheckModifiersCorrectness(Qt::KeyboardModifiers modifiers) const
bool nativeGestureBeginEvent(QNativeGestureEvent *event)
bool keyReleased(Qt::Key key)
void touchCancelEvent(QTouchEvent *event, const QPointF &localPos)
bool wheelEvent(KisSingleActionShortcut::WheelAction wheelAction, QWheelEvent *event)
@ WheelDown
Mouse wheel moves down.
@ WheelTrackpad
A pan movement on a trackpad.
@ WheelRight
Mouse wheel moves right.
@ WheelLeft
Mouse wheel moves left.
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
bool shouldResetWheelDelta(QEvent *event)

References KisInputManager::Private::accumulatedScrollDelta, KisInputManager::Private::allowMouseEvents(), KisShortcutMatcher::autoRepeatedKeyPressed(), KisInputManager::Private::blockMouseEvents(), KisInputManager::Private::bufferTouchEvent(), KisShortcutMatcher::buttonPressed(), KisShortcutMatcher::buttonReleased(), KisInputManager::Private::cancelTouchHoldTimer(), canvas(), KisCanvas2::canvasWidget, KisInputManager::Private::clearBufferedTouchEvents(), compressMoveEventCommon(), KisInputManager::Private::containsPointer, d, KisInputManager::Private::debugEvent(), KisShortcutMatcher::debugPressedKeys(), deregisterPopupWidget(), KisConfig::disableTouchOnCanvas(), KisInputManager::Private::eatOneMousePress(), endTouch(), KisShortcutMatcher::enterEvent(), KisInputManager::Private::eventsReceiver, KisInputManager::Private::fixShortcutMatcherModifiersState(), KisInputManager::Private::flushBufferedTouchEvents(), handleTouchBegin(), handleTouchUpdate(), KisShortcutMatcher::hasPolledKeys(), KisShortcutMatcher::hasTouchHoldShortcut(), KisShortcutMatcher::keyPressed(), KisShortcutMatcher::keyReleased(), KIS_SAFE_ASSERT_RECOVER, KisInputManager::Private::lastPointCount, KisShortcutMatcher::leaveEvent(), KisShortcutMatcher::lostFocusEvent(), KisInputManager::Private::matcher, KisShortcutMatcher::nativeGestureBeginEvent(), KisShortcutMatcher::nativeGestureEndEvent(), KisShortcutMatcher::nativeGestureEvent(), KisPopupWidgetInterface::onScreen(), KisInputManager::Private::originatingTouchBeginEvent, KisInputManager::Private::popupWasActive, KisInputManager::Private::popupWidget, KisInputManager::Private::previousPos, KisInputManager::Private::processUnhandledEvent(), KisExtendedModifiersMapper::queryExtendedModifiers(), KisExtendedModifiersMapper::queryStandardModifiers(), KisShortcutMatcher::reinitializeButtons(), KisInputManager::Private::resetCompressor(), KisInputManager::Private::restartTouchHoldTimer(), KisShortcutMatcher::sanityCheckModifiersCorrectness(), KisAbstractInputAction::setInputManager(), shouldResetWheelDelta(), KisInputManager::Private::shouldSynchronizeOnNextKeyPress, KisAlgebra2D::signPZ(), KisInputManager::Private::startBlockingTouch(), KisInputManager::Private::startingPos, startTouch(), KisInputManager::Private::stopBlockingTouch(), KisInputManager::Private::tabletLatencyTracker, KisShortcutMatcher::touchCancelEvent(), KisShortcutMatcher::touchEndEvent(), KisInputManager::Private::touchHasBlockedPressEvents, touchHoldBufferUpdate(), KisInputManager::Private::touchStrokeStarted, KisInputManager::Private::useUnbalancedKeyPressEventWorkaround, KisSingleActionShortcut::WheelDown, KisShortcutMatcher::wheelEvent(), KisSingleActionShortcut::WheelLeft, KisSingleActionShortcut::WheelRight, KisSingleActionShortcut::WheelTrackpad, KisSingleActionShortcut::WheelUp, and KisExtendedModifiersMapper::workaroundShiftAltMetaHell().

◆ handleTouchBegin()

bool KisInputManager::handleTouchBegin ( QTouchEvent * touchEvent)
private

Definition at line 948 of file kis_input_manager.cpp.

949{
950 return d->matcher.touchBeginEvent(touchEvent);
951}
bool touchBeginEvent(QTouchEvent *event)

References d, KisInputManager::Private::matcher, and KisShortcutMatcher::touchBeginEvent().

◆ handleTouchUpdate()

bool KisInputManager::handleTouchUpdate ( QTouchEvent * touchEvent)
private

Definition at line 953 of file kis_input_manager.cpp.

954{
955 QPointF currentPos = touchEvent->touchPoints().at(0).pos();
957 || (!KisConfig(true).disableTouchOnCanvas() && !d->touchHasBlockedPressEvents
958 && touchEvent->touchPoints().count() == 1 && touchEvent->touchPointStates() != Qt::TouchPointStationary
959 && (qAbs(currentPos.x() - d->previousPos.x()) > 1 // stop wobbliness which Qt sends us
960 || qAbs(currentPos.y() - d->previousPos.y()) > 1))) {
961 d->previousPos = currentPos;
962 if (!d->touchStrokeStarted) {
963 // we start it here not in TouchBegin, because Qt::TouchPointStationary doesn't work with hpdi devices.
964 bool retval = d->matcher.buttonPressed(Qt::LeftButton, d->originatingTouchBeginEvent.data());
965 d->touchStrokeStarted = retval;
966 return retval;
967 } else {
968 // if it is a full-fledged stroke, then ignore (currentPos.x - previousPos.x)
969 bool retval = compressMoveEventCommon(touchEvent);
971 return retval;
972 }
973 } else {
975 bool retval = d->matcher.touchUpdateEvent(touchEvent);
977 return retval;
978 }
979}
bool touchUpdateEvent(QTouchEvent *event)

References KisInputManager::Private::blockMouseEvents(), KisShortcutMatcher::buttonPressed(), compressMoveEventCommon(), d, KisInputManager::Private::matcher, KisInputManager::Private::originatingTouchBeginEvent, KisInputManager::Private::previousPos, KisAbstractInputAction::setInputManager(), KisInputManager::Private::touchHasBlockedPressEvents, KisInputManager::Private::touchStrokeStarted, and KisShortcutMatcher::touchUpdateEvent().

◆ profileChanged

void KisInputManager::profileChanged ( )
privateslot

Definition at line 1056 of file kis_input_manager.cpp.

1057{
1059
1061 if (profile) {
1062 const QList<KisShortcutConfiguration*> shortcuts = profile->allShortcuts();
1063
1064 for (KisShortcutConfiguration * const shortcut : shortcuts) {
1065 dbgUI << "Adding shortcut" << shortcut->keys() << "for action" << shortcut->action()->name();
1066 switch(shortcut->type()) {
1068 d->addKeyShortcut(shortcut->action(), shortcut->mode(), shortcut->keys());
1069 break;
1071 d->addStrokeShortcut(shortcut->action(), shortcut->mode(), shortcut->keys(), shortcut->buttons());
1072 break;
1074 d->addWheelShortcut(shortcut->action(), shortcut->mode(), shortcut->keys(), shortcut->wheel());
1075 break;
1077 if (!d->addNativeGestureShortcut(shortcut->action(), shortcut->mode(), shortcut->gesture())) {
1078 d->addTouchShortcut(shortcut->action(), shortcut->mode(), shortcut->gesture());
1079 }
1080 break;
1081 default:
1082 break;
1083 }
1084 }
1085 }
1086 else {
1087 dbgInput << "No Input Profile Found: canvas interaction will be impossible";
1088 }
1089}
bool addNativeGestureShortcut(KisAbstractInputAction *action, int index, KisShortcutConfiguration::GestureAction gesture)
void addKeyShortcut(KisAbstractInputAction *action, int index, const QList< Qt::Key > &keys)
void addWheelShortcut(KisAbstractInputAction *action, int index, const QList< Qt::Key > &modifiers, KisShortcutConfiguration::MouseWheelMovement wheelAction)
void addStrokeShortcut(KisAbstractInputAction *action, int index, const QList< Qt::Key > &modifiers, Qt::MouseButtons buttons)
void addTouchShortcut(KisAbstractInputAction *action, int index, KisShortcutConfiguration::GestureAction gesture)
static KisInputProfileManager * instance()
A container class for sets of shortcuts associated with an action.
QList< KisShortcutConfiguration * > allShortcuts() const
A class encapsulating all settings for a single shortcut.
@ MouseButtonType
A mouse button, possibly with key modifiers.
@ MouseWheelType
Mouse wheel movement, possibly with key modifiers.
@ KeyCombinationType
A list of keys that should be pressed.
#define dbgUI
Definition kis_debug.h:52
#define dbgInput
Definition kis_debug.h:57

References KisInputManager::Private::addKeyShortcut(), KisInputManager::Private::addNativeGestureShortcut(), KisInputManager::Private::addStrokeShortcut(), KisInputManager::Private::addTouchShortcut(), KisInputManager::Private::addWheelShortcut(), KisInputProfile::allShortcuts(), KisShortcutMatcher::clearShortcuts(), KisInputProfileManager::currentProfile, d, dbgInput, dbgUI, KisShortcutConfiguration::GestureType, KisInputProfileManager::instance(), KisShortcutConfiguration::KeyCombinationType, KisInputManager::Private::matcher, KisShortcutConfiguration::MouseButtonType, and KisShortcutConfiguration::MouseWheelType.

◆ registerPopupWidget()

void KisInputManager::registerPopupWidget ( KisPopupWidgetInterface * popupWidget)

Definition at line 105 of file kis_input_manager.cpp.

106{
107 d->popupWidget = popupWidget;
108
109 // FUNKY!
110 auto popupObject = dynamic_cast<QObject*>(d->popupWidget);
111 KIS_ASSERT(popupObject);
112 connect(popupObject, SIGNAL(finished()), this, SLOT(deregisterPopupWidget()));
113}

References d, deregisterPopupWidget(), KIS_ASSERT, and KisInputManager::Private::popupWidget.

◆ removeTrackedCanvas()

void KisInputManager::removeTrackedCanvas ( KisCanvas2 * canvas)

◆ setupAsEventFilter()

void KisInputManager::setupAsEventFilter ( QObject * receiver)

Installs the input manager as an event filter for receiver. Please note that KisInputManager is supposed to handle events for a single receiver only. This is defined by the fact that it resends some of the events back through the Qt's queue to the receiver. That is why the input manager will assert when it gets an event with wrong destination.

Definition at line 184 of file kis_input_manager.cpp.

185{
186 if (d->eventsReceiver) {
187 d->eventsReceiver->removeEventFilter(this);
188 }
189
190 d->eventsReceiver = receiver;
191
192 if (d->eventsReceiver) {
193 d->eventsReceiver->installEventFilter(this);
194 }
195}

References d, and KisInputManager::Private::eventsReceiver.

◆ slotAboutToChangeTool

void KisInputManager::slotAboutToChangeTool ( )
privateslot

Definition at line 1004 of file kis_input_manager.cpp.

1005{
1006 QPointF currentLocalPos;
1007 if (canvas() && canvas()->canvasWidget()) {
1008 currentLocalPos = canvas()->canvasWidget()->mapFromGlobal(QCursor::pos());
1009 }
1010 // once more, don't forget the global state whenever matcher may trigger a KisAbstractInputAction
1012
1013 d->matcher.lostFocusEvent(currentLocalPos);
1014}

References canvas(), KisCanvas2::canvasWidget, d, KisShortcutMatcher::lostFocusEvent(), KisInputManager::Private::matcher, and KisAbstractInputAction::setInputManager().

◆ slotCompressedMoveEvent

void KisInputManager::slotCompressedMoveEvent ( )
privateslot

Definition at line 981 of file kis_input_manager.cpp.

982{
983 if (d->compressedMoveEvent) {
984 // d->touchHasBlockedPressEvents = false;
985
987 d->compressedMoveEvent.reset();
988 //dbgInput << "Compressed move event received.";
989 } else {
990 //dbgInput << "Unexpected empty move event";
991 }
992}
typedef void(QOPENGLF_APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC)(GLuint buffer)

References KisInputManager::Private::compressedMoveEvent, d, KisInputManager::Private::handleCompressedTabletEvent(), and void().

◆ slotConfigChanged

void KisInputManager::slotConfigChanged ( )
privateslot

Definition at line 129 of file kis_input_manager.cpp.

130{
131#ifdef Q_OS_WIN
132 d->ignoreHighFunctionKeys = KisConfig(true).ignoreHighFunctionKeys();
134#endif
135}
bool ignoreHighFunctionKeys(bool defaultValue=false) const

References d, KisInputManager::Private::fixShortcutMatcherModifiersState(), and KisConfig::ignoreHighFunctionKeys().

◆ slotTextModeChanged

void KisInputManager::slotTextModeChanged ( )
privateslot

Definition at line 1039 of file kis_input_manager.cpp.

1040{
1041 if (!d->canvas) return;
1042 KoToolManager *toolManager = KoToolManager::instance();
1043 KoToolBase *tool = toolManager->toolById(canvas(), toolManager->activeToolId());
1044 if (tool) {
1045 if (tool->isInTextMode()) {
1046 d->forwardAllEventsToTool = true;
1048 } else {
1049 d->forwardAllEventsToTool = false;
1051 }
1052 }
1053}
bool isInTextMode() const
KoToolBase * toolById(KoCanvasBase *canvas, const QString &id) const
QString activeToolId() const
Returns the toolId of the currently active tool.

References KoToolManager::activeToolId(), canvas(), KisInputManager::Private::canvas, d, KisInputManager::Private::forwardAllEventsToTool, KoToolManager::instance(), KoToolBase::isInTextMode(), KisInputManager::Private::matcher, KisShortcutMatcher::suppressAllKeyboardActions, and KoToolManager::toolById().

◆ slotToolChanged

void KisInputManager::slotToolChanged ( )
privateslot

Definition at line 1016 of file kis_input_manager.cpp.

1017{
1018 if (!d->canvas) return;
1019 KoToolManager *toolManager = KoToolManager::instance();
1020 KoToolBase *tool = toolManager->toolById(canvas(), toolManager->activeToolId());
1021 if (tool) {
1022 // once more, don't forget the global state whenever matcher may trigger a KisAbstractInputAction
1024
1026 if (tool->isInTextMode()) {
1027 d->forwardAllEventsToTool = true;
1029 } else {
1030 d->forwardAllEventsToTool = false;
1032 }
1033
1034 d->matcher.suppressConflictingKeyActions(toolProxy()->toolPriorityShortcuts());
1036 }
1037}
void setMaskSyntheticEvents(bool value)
QPointer< KisToolProxy > toolProxy() const
void suppressConflictingKeyActions(const QVector< QKeySequence > &shortcuts)
bool maskSyntheticEvents() const

References KoToolManager::activeToolId(), canvas(), KisInputManager::Private::canvas, d, KisInputManager::Private::forwardAllEventsToTool, KoToolManager::instance(), KoToolBase::isInTextMode(), KoToolBase::maskSyntheticEvents(), KisInputManager::Private::matcher, KisAbstractInputAction::setInputManager(), KisInputManager::Private::setMaskSyntheticEvents(), KisShortcutMatcher::suppressAllKeyboardActions, KisShortcutMatcher::suppressConflictingKeyActions(), KoToolManager::toolById(), KisShortcutMatcher::toolHasBeenActivated(), and toolProxy().

◆ slotTouchHoldTriggered

◆ startTouch()

bool KisInputManager::startTouch ( bool & retval)
private

Definition at line 917 of file kis_input_manager.cpp.

918{
919 Q_UNUSED(retval);
920
921 // Touch rejection: if touch is disabled on canvas, no need to block mouse press events
922 if (KisConfig(true).disableTouchOnCanvas()) {
924 }
925
926 return true;
927}

References d, and KisInputManager::Private::eatOneMousePress().

◆ toggleTabletLogger()

void KisInputManager::toggleTabletLogger ( )

◆ toolProxy()

QPointer< KisToolProxy > KisInputManager::toolProxy ( ) const

The tool proxy of the current application.

Definition at line 999 of file kis_input_manager.cpp.

1000{
1001 return d->toolProxy;
1002}

References d, and KisInputManager::Private::toolProxy.

◆ touchHoldBufferUpdate()

bool KisInputManager::touchHoldBufferUpdate ( QTouchEvent * touchEvent)
private

Definition at line 934 of file kis_input_manager.cpp.

935{
936 if (d->isPendingTouchHold()) {
937 if (touchEvent->touchPoints().length() == 1 && d->isWithinTouchHoldSlopRange(touchEvent->touchPoints().at(0).pos())) {
938 d->bufferTouchEvent(touchEvent);
939 return true;
940 } else {
943 }
944 }
945 return false;
946}
bool isWithinTouchHoldSlopRange(const QPointF &currentPos) const

References KisInputManager::Private::bufferTouchEvent(), KisInputManager::Private::cancelTouchHoldTimer(), d, KisInputManager::Private::flushBufferedTouchEvents(), KisInputManager::Private::isPendingTouchHold(), and KisInputManager::Private::isWithinTouchHoldSlopRange().

Member Data Documentation

◆ d

Private* const KisInputManager::d
private

Definition at line 122 of file kis_input_manager.h.


The documentation for this class was generated from the following files: