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 ()
 

Private Member Functions

template<class Event >
bool compressMoveEventCommon (Event *event)
 
void endTouch ()
 
bool eventFilterImpl (QEvent *event)
 
bool startTouch (bool &retval)
 

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}
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
static KisConfigNotifier * instance()
KisSignalCompressor moveEventCompressor
static KoToolManager * instance()
Return the toolmanager singleton.

References connect(), 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 143 of file kis_input_manager.cpp.

144{
145 Private::PriorityList::iterator begin = d->priorityEventFilter.begin();
146 Private::PriorityList::iterator it = begin;
147 Private::PriorityList::iterator end = d->priorityEventFilter.end();
148
149 it = std::find_if(begin, end,
150 kismpl::mem_equal_to(&Private::PriorityPair::second, filter));
151
152 if (it != end) return;
153
154 it = std::find_if(begin, end,
155 kismpl::mem_greater(&Private::PriorityPair::first, priority));
156
157 d->priorityEventFilter.insert(it, qMakePair(priority, filter));
159}
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 908 of file kis_input_manager.cpp.

909{
910 return d->canvas;
911}
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 239 of file kis_input_manager.cpp.

240{
245 static_assert(std::is_same<Event, QMouseEvent>::value ||
246 std::is_same<Event, QTabletEvent>::value ||
247 std::is_same<Event, QTouchEvent>::value,
248 "event should be a mouse or a tablet event");
249
250
251 bool retval = false;
252
256 if ((event->type() == QEvent::MouseMove ||
257 event->type() == QEvent::TabletMove ||
258 event->type() == QEvent::TouchUpdate) &&
261#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
262 KoPointerEvent::copyQtPointerEvent(event, d->compressedMoveEvent);
263#else
264 d->compressedMoveEvent.reset(event->clone());
265#endif
267
275 event->setAccepted(true);
276 }
277
278 retval = true;
279 } else {
281 retval = d->handleCompressedTabletEvent(event);
282 }
283
284 return retval;
285}
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 161 of file kis_input_manager.cpp.

162{
163 Private::PriorityList::iterator it = d->priorityEventFilter.begin();
164 Private::PriorityList::iterator end = d->priorityEventFilter.end();
165
166 it = std::find_if(it, end,
167 kismpl::mem_equal_to(&Private::PriorityPair::second, filter));
168
169 if (it != end) {
170 d->priorityEventFilter.erase(it);
171 }
172}

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 191 of file kis_input_manager.cpp.

192{
193 if (object != d->eventsReceiver) return false;
194
195 if (d->eventEater.eventFilter(object, event)) return false;
196
197 if (!d->matcher.hasRunningShortcut()) {
198
199 int savedPriorityEventFilterSeqNo = d->priorityEventFilterSeqNo;
200
201 for (auto it = d->priorityEventFilter.begin(); it != d->priorityEventFilter.end(); /*noop*/) {
202 const QPointer<QObject> &filter = it->second;
203
204 if (filter.isNull()) {
205 it = d->priorityEventFilter.erase(it);
206
208 savedPriorityEventFilterSeqNo++;
209 continue;
210 }
211
212 if (filter->eventFilter(object, event)) return true;
213
218 if (d->priorityEventFilterSeqNo != savedPriorityEventFilterSeqNo) {
219 return true;
220 }
221
222 ++it;
223 }
224
225 // KoToolProxy needs to pre-process some events to ensure the
226 // global shortcuts (not the input manager's ones) are not
227 // executed, in particular, this line will accept events when the
228 // tool is in text editing, preventing shortcut triggering
229 if (d->toolProxy) {
230 d->toolProxy->processEvent(event);
231 }
232 }
233
234 // Continue with the actual switch statement...
235 return eventFilterImpl(event);
236}
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 306 of file kis_input_manager.cpp.

307{
308 bool retval = false;
309
310 // Try closing any open popup widget and
311 // consume input if possible.
312 if (d->popupWidget) {
313 QEvent::Type type = event->type();
314
315 if (type == QEvent::MouseButtonPress
316 || type == QEvent::MouseButtonDblClick
317 || type == QEvent::TabletPress
318 || type == QEvent::TouchBegin
319 || type == QEvent::NativeGesture) {
320 bool wasVisible = d->popupWidget->onScreen();
322
323 if (wasVisible) {
324 d->popupWasActive = true;
325 event->setAccepted(true);
326 return true; // Event consumed.
327 }
328 }
329 }
330
331 if (shouldResetWheelDelta(event)) {
333 }
334
335 if (event->type() == QEvent::MouseMove ||
336 event->type() == QEvent::MouseButtonPress ||
337 event->type() == QEvent::MouseButtonRelease ||
338 event->type() == QEvent::TabletMove ||
339 event->type() == QEvent::TabletPress ||
340 event->type() == QEvent::TabletRelease ||
341 event->type() == QEvent::Wheel) {
342
352 QWidget *receivingWidget = dynamic_cast<QWidget*>(d->eventsReceiver);
353 if (receivingWidget && !receivingWidget->hasFocus()) {
355 } else {
363 QInputEvent *inputEvent = static_cast<QInputEvent*>(event);
364 if (event->type() != QEvent::ShortcutOverride &&
365 !d->matcher.sanityCheckModifiersCorrectness(inputEvent->modifiers())) {
366
368 } else if (d->matcher.hasPolledKeys()) {
377 }
378 }
379 }
380
381 switch (event->type()) {
382 case QEvent::MouseButtonPress:
383 case QEvent::MouseButtonDblClick: {
384 d->debugEvent<QMouseEvent, true>(event);
385 if (d->touchHasBlockedPressEvents) break;
386
387 QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
388
389 if (d->popupWidget) {
390 retval = true;
391 } else {
392 //Make sure the input actions know we are active.
394 retval = d->matcher.buttonPressed(mouseEvent->button(), mouseEvent);
395 }
396 //Reset signal compressor to prevent processing events before press late
398 event->setAccepted(retval);
399 break;
400 }
401 case QEvent::MouseButtonRelease: {
402 d->debugEvent<QMouseEvent, true>(event);
403 if (d->touchHasBlockedPressEvents) break;
404
405 QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
406 retval = d->matcher.buttonReleased(mouseEvent->button(), mouseEvent);
407 event->setAccepted(retval);
408 break;
409 }
410 case QEvent::ShortcutOverride: {
411 d->debugEvent<QKeyEvent, false>(event);
412 QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
413
415
424 QVector<Qt::Key> guessedKeys;
426 Qt::KeyboardModifiers modifiers = mapper.queryStandardModifiers();
427 Q_FOREACH (Qt::Key key, mapper.queryExtendedModifiers()) {
428 QKeyEvent kevent(QEvent::ShortcutOverride, key, modifiers);
430 }
431
432 if (!d->matcher.debugPressedKeys().contains(key)) {
433 guessedKeys.removeOne(key);
434 }
435
436 d->fixShortcutMatcherModifiersState(guessedKeys, modifiers);
438 }
439
440 if (!keyEvent->isAutoRepeat()) {
441 retval = d->matcher.keyPressed(key);
442 } else {
443 retval = d->matcher.autoRepeatedKeyPressed(key);
444 }
445
446 // In case we matched a shortcut we should accept the event to
447 // notify Qt that it shouldn't try to trigger its partially matched
448 // shortcuts.
449 if (retval) {
450 keyEvent->setAccepted(true);
451 }
452
453 break;
454 }
455 case QEvent::KeyRelease: {
456 d->debugEvent<QKeyEvent, false>(event);
457 QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
458
459 if (!keyEvent->isAutoRepeat()) {
461 retval = d->matcher.keyReleased(key);
462
490 !d->matcher.debugPressedKeys().isEmpty() &&
491 (key == Qt::Key_Shift || key == Qt::Key_Alt ||
492 key == Qt::Key_Control || key == Qt::Key_Meta)) {
493
495 }
496 }
497 break;
498 }
499 case QEvent::MouseMove: {
500 d->debugEvent<QMouseEvent, true>(event);
501
502 QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
503 retval = compressMoveEventCommon(mouseEvent);
504
505 break;
506 }
507 case QEvent::Wheel: {
508 d->debugEvent<QWheelEvent, false>(event);
509 QWheelEvent *wheelEvent = static_cast<QWheelEvent*>(event);
510
511#ifdef Q_OS_MACOS
512 // Some QT wheel events are actually touch pad pan events. From the QT docs:
513 // "Wheel events are generated for both mouse wheels and trackpad scroll gestures."
514
515 // We differentiate between touchpad events and real mouse wheels by inspecting the
516 // event source.
517
518 if (wheelEvent->source() == Qt::MouseEventSource::MouseEventSynthesizedBySystem) {
521 break;
522 }
523#endif
524
525 d->accumulatedScrollDelta += wheelEvent->angleDelta().y();
527
532#ifdef Q_OS_MACOS
533 if (wheelEvent->angleDelta().isNull()) {
534 retval = true;
535 break;
536 }
537#endif
538
539 if (wheelEvent->angleDelta().x() < 0) {
541 } else if (wheelEvent->angleDelta().x() >0) {
543 }
544
545 if (wheelEvent->angleDelta().y() < 0) {
547 } else if (wheelEvent->angleDelta().y() > 0) {
549 }
550
551 bool wasScrolled = false;
552
553 while (qAbs(d->accumulatedScrollDelta) >= QWheelEvent::DefaultDeltasPerStep) {
554 //Make sure the input actions know we are active.
556 retval = d->matcher.wheelEvent(action, wheelEvent);
559 QWheelEvent::DefaultDeltasPerStep;
560 wasScrolled = true;
561 }
562
563 if (wasScrolled) {
565 }
566
567 retval = !wasScrolled;
568 break;
569 }
570#ifndef Q_OS_ANDROID
571 case QEvent::Enter:
572 d->debugEvent<QEvent, false>(event);
573 //Make sure the input actions know we are active.
575 if (!d->containsPointer) {
576 d->containsPointer = true;
577
580 }
582 break;
583 case QEvent::Leave:
584 d->debugEvent<QEvent, false>(event);
585 d->containsPointer = false;
593
595 break;
596#endif
597 case QEvent::FocusIn:
598 d->debugEvent<QEvent, false>(event);
600
603
605 break;
606
607 case QEvent::FocusOut: {
608 d->debugEvent<QEvent, false>(event);
610
611 QPointF currentLocalPos =
612 canvas()->canvasWidget()->mapFromGlobal(QCursor::pos());
613
614 d->matcher.lostFocusEvent(currentLocalPos);
615
616 break;
617 }
618 case QEvent::TabletPress: {
619 d->debugEvent<QTabletEvent, false>(event);
620 QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(event);
621
622 {
623 //Make sure the input actions know we are active.
625 retval = d->matcher.buttonPressed(tabletEvent->button(), tabletEvent);
626 if (!d->containsPointer) {
627 d->containsPointer = true;
629 }
630 }
631
632 event->setAccepted(true);
633 retval = true;
636 //Reset signal compressor to prevent processing events before press late
638
639
640#if defined Q_OS_LINUX && !KRITA_QT_HAS_ENTER_LEAVE_PATCH
641 // remove this hack when this patch is integrated:
642 // https://codereview.qt-project.org/#/c/255384/
643 event->setAccepted(false);
645#elif defined Q_OS_WIN32
651#endif
652
653 break;
654 }
655 case QEvent::TabletMove: {
656 d->debugEvent<QTabletEvent, false>(event);
657
658 QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(event);
659 retval = compressMoveEventCommon(tabletEvent);
660
661 if (d->tabletLatencyTracker) {
662 d->tabletLatencyTracker->push(tabletEvent->timestamp());
663 }
664
673
674#if defined Q_OS_LINUX && !KRITA_QT_HAS_ENTER_LEAVE_PATCH
675 // remove this hack when this patch is integrated:
676 // https://codereview.qt-project.org/#/c/255384/
677 event->setAccepted(false);
678#endif
679
680 break;
681 }
682 case QEvent::TabletRelease: {
683#if defined(Q_OS_MAC) || defined(Q_OS_ANDROID)
685#endif
687 d->debugEvent<QTabletEvent, false>(event);
688
689 QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(event);
690 retval = d->matcher.buttonReleased(tabletEvent->button(), tabletEvent);
691 retval = true;
692 event->setAccepted(true);
693
694#if defined Q_OS_LINUX && !KRITA_QT_HAS_ENTER_LEAVE_PATCH
695 // remove this hack when this patch is integrated:
696 // https://codereview.qt-project.org/#/c/255384/
697 event->setAccepted(false);
698#endif
699
700 break;
701 }
702
703 case QEvent::TouchBegin:
704 {
705 d->debugEvent<QTouchEvent, false>(event);
706 // The popup was dismissed in previous TouchBegin->TouchEnd sequence. We now have a new TouchBegin.
707 d->popupWasActive = false;
708 if (startTouch(retval)) {
709 QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event);
711 d->previousPos = touchEvent->touchPoints().at(0).pos();
712 // we don't want to lose this event
713#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
714 KoPointerEvent::copyQtPointerEvent(touchEvent, d->originatingTouchBeginEvent);
715#else
716 d->originatingTouchBeginEvent.reset(touchEvent->clone());
717#endif
718 retval = d->matcher.touchBeginEvent(touchEvent);
720 d->touchStrokeStarted = false;
721 }
723 event->accept();
724 }
725 break;
726 }
727
728 case QEvent::TouchUpdate:
729 {
730 if (d->popupWasActive) {
731 event->setAccepted(true);
732 return true;
733 }
734 QTouchEvent *touchEvent = static_cast<QTouchEvent*>(event);
735 d->debugEvent<QTouchEvent, false>(event);
736
737#ifdef Q_OS_MAC
738 int count = 0;
739 Q_FOREACH (const QTouchEvent::TouchPoint &point, touchEvent->touchPoints()) {
740 if (point.state() != Qt::TouchPointReleased) {
741 count++;
742 }
743 }
744
745 if (count < 2 && touchEvent->touchPoints().length() > count) {
747 retval = d->matcher.touchEndEvent(touchEvent);
748 } else {
749#endif
750 QPointF currentPos = touchEvent->touchPoints().at(0).pos();
751 if (d->touchStrokeStarted || (!KisConfig(true).disableTouchOnCanvas()
753 && touchEvent->touchPoints().count() == 1
754 && touchEvent->touchPointStates() != Qt::TouchPointStationary
755 && (qAbs(currentPos.x() - d->previousPos.x()) > 1 // stop wobbliness which Qt sends us
756 || qAbs(currentPos.y() - d->previousPos.y()) > 1)))
757 {
758 d->previousPos = currentPos;
759 if (!d->touchStrokeStarted) {
760 // we start it here not in TouchBegin, because Qt::TouchPointStationary doesn't work with hpdi devices.
761 retval = d->matcher.buttonPressed(Qt::LeftButton, d->originatingTouchBeginEvent.data());
762 d->touchStrokeStarted = retval;
763 } else {
764 // if it is a full-fledged stroke, then ignore (currentPos.x - previousPos.x)
765 retval = compressMoveEventCommon(touchEvent);
767 }
768 } else {
771 retval = compressMoveEventCommon(touchEvent);
772 } else {
773 retval = d->matcher.touchUpdateEvent(touchEvent);
775 }
776 }
777#ifdef Q_OS_MACOS
778 }
779#endif
780 // if the event isn't handled, Qt starts to send MouseEvents
781 if (!KisConfig(true).disableTouchOnCanvas())
782 retval = true;
783
784 event->accept();
785 break;
786 }
787
788 case QEvent::TouchEnd:
789 {
790 if (d->popupWasActive) {
791 event->setAccepted(true);
792 return true;
793 }
794 d->debugEvent<QTouchEvent, false>(event);
795 QTouchEvent *touchEvent = static_cast<QTouchEvent*>(event);
796 retval = d->matcher.touchEndEvent(touchEvent);
797 if (d->touchStrokeStarted) {
798 retval = d->matcher.buttonReleased(Qt::LeftButton, touchEvent);
799 d->previousPos = {0, 0};
800 d->touchStrokeStarted = false; // stroke ended
802 && touchEvent->touchPoints().count() == 1) {
803 // If no stroke has been started while touch painting is enabled,
804 // the user tapped with one finger, but didn't make any motion that
805 // caused us to start a stroke. We produce a press and release in
806 // response so that the tool responds to their input.
807 d->matcher.buttonPressed(Qt::LeftButton, d->originatingTouchBeginEvent.data());
808 d->matcher.buttonReleased(Qt::LeftButton, touchEvent);
809 }
810
811 endTouch();
813
814 // if the event isn't handled, Qt starts to send MouseEvents
815 if (!KisConfig(true).disableTouchOnCanvas())
816 retval = true;
817
818 event->accept();
819 break;
820 }
821 case QEvent::TouchCancel:
822 {
823 if (d->popupWasActive) {
824 event->setAccepted(true);
825 return true;
826 }
827 d->debugEvent<QTouchEvent, false>(event);
828 endTouch();
830 QTouchEvent *touchEvent = static_cast<QTouchEvent*>(event);
831 d->matcher.touchCancelEvent(touchEvent, d->previousPos);
832 // reset state
833 d->previousPos = {0, 0};
834 d->touchStrokeStarted = false;
835 retval = true;
836 event->accept();
837 break;
838 }
839
840 case QEvent::NativeGesture:
841 {
842 QNativeGestureEvent *gevent = static_cast<QNativeGestureEvent*>(event);
843 switch (gevent->gestureType()) {
844 case Qt::BeginNativeGesture:
845 {
846 if (startTouch(retval)) {
848 retval = d->matcher.nativeGestureBeginEvent(gevent);
849 event->accept();
850 }
851 break;
852 }
853 case Qt::EndNativeGesture:
854 {
855 endTouch();
856 retval = d->matcher.nativeGestureEndEvent(gevent);
857 event->accept();
858 break;
859 }
860 default:
861 {
863 retval = d->matcher.nativeGestureEvent(gevent);
864 event->accept();
865 break;
866 }
867 }
868 break;
869 }
870
871 default:
872 break;
873 }
874
875 return !retval ? d->processUnhandledEvent(event) : true;
876}
qreal length(const QPointF &vec)
Definition Ellipse.cc:82
static void setInputManager(KisInputManager *manager)
KisAbstractCanvasWidget * canvasWidget
bool disableTouchOnCanvas() const
static Qt::Key workaroundShiftAltMetaHell(const QKeyEvent *keyEvent)
void debugEvent(QEvent *event)
QScopedPointer< QEvent > originatingTouchBeginEvent
bool processUnhandledEvent(QEvent *event)
KisSharedPtr< TabletLatencyTracker > tabletLatencyTracker
bool compressMoveEventCommon(Event *event)
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)
bool touchBeginEvent(QTouchEvent *event)
QVector< Qt::Key > debugPressedKeys() const
bool touchEndEvent(QTouchEvent *event)
bool touchUpdateEvent(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(), KisShortcutMatcher::buttonPressed(), KisShortcutMatcher::buttonReleased(), canvas(), KisCanvas2::canvasWidget, 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(), KisShortcutMatcher::hasPolledKeys(), KisShortcutMatcher::keyPressed(), KisShortcutMatcher::keyReleased(), KIS_SAFE_ASSERT_RECOVER, KisShortcutMatcher::leaveEvent(), length(), 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(), KisShortcutMatcher::sanityCheckModifiersCorrectness(), KisAbstractInputAction::setInputManager(), shouldResetWheelDelta(), KisInputManager::Private::shouldSynchronizeOnNextKeyPress, KisAlgebra2D::signPZ(), KisInputManager::Private::startBlockingTouch(), startTouch(), KisInputManager::Private::stopBlockingTouch(), KisInputManager::Private::tabletLatencyTracker, KisShortcutMatcher::touchBeginEvent(), KisShortcutMatcher::touchCancelEvent(), KisShortcutMatcher::touchEndEvent(), KisInputManager::Private::touchHasBlockedPressEvents, KisInputManager::Private::touchStrokeStarted, KisShortcutMatcher::touchUpdateEvent(), KisInputManager::Private::useUnbalancedKeyPressEventWorkaround, KisSingleActionShortcut::WheelDown, KisShortcutMatcher::wheelEvent(), KisSingleActionShortcut::WheelLeft, KisSingleActionShortcut::WheelRight, KisSingleActionShortcut::WheelTrackpad, KisSingleActionShortcut::WheelUp, and KisExtendedModifiersMapper::workaroundShiftAltMetaHell().

◆ profileChanged

void KisInputManager::profileChanged ( )
privateslot

Definition at line 970 of file kis_input_manager.cpp.

971{
973
975 if (profile) {
976 const QList<KisShortcutConfiguration*> shortcuts = profile->allShortcuts();
977
978 for (KisShortcutConfiguration * const shortcut : shortcuts) {
979 dbgUI << "Adding shortcut" << shortcut->keys() << "for action" << shortcut->action()->name();
980 switch(shortcut->type()) {
982 d->addKeyShortcut(shortcut->action(), shortcut->mode(), shortcut->keys());
983 break;
985 d->addStrokeShortcut(shortcut->action(), shortcut->mode(), shortcut->keys(), shortcut->buttons());
986 break;
988 d->addWheelShortcut(shortcut->action(), shortcut->mode(), shortcut->keys(), shortcut->wheel());
989 break;
991 if (!d->addNativeGestureShortcut(shortcut->action(), shortcut->mode(), shortcut->gesture())) {
992 d->addTouchShortcut(shortcut->action(), shortcut->mode(), shortcut->gesture());
993 }
994 break;
995 default:
996 break;
997 }
998 }
999 }
1000 else {
1001 dbgInput << "No Input Profile Found: canvas interaction will be impossible";
1002 }
1003}
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 connect(), 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 174 of file kis_input_manager.cpp.

175{
176 if (d->eventsReceiver) {
177 d->eventsReceiver->removeEventFilter(this);
178 }
179
180 d->eventsReceiver = receiver;
181
182 if (d->eventsReceiver) {
183 d->eventsReceiver->installEventFilter(this);
184 }
185}

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

◆ slotAboutToChangeTool

void KisInputManager::slotAboutToChangeTool ( )
privateslot

Definition at line 918 of file kis_input_manager.cpp.

919{
920 QPointF currentLocalPos;
921 if (canvas() && canvas()->canvasWidget()) {
922 currentLocalPos = canvas()->canvasWidget()->mapFromGlobal(QCursor::pos());
923 }
924 // once more, don't forget the global state whenever matcher may trigger a KisAbstractInputAction
926
927 d->matcher.lostFocusEvent(currentLocalPos);
928}

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

◆ slotCompressedMoveEvent

void KisInputManager::slotCompressedMoveEvent ( )
privateslot

Definition at line 895 of file kis_input_manager.cpp.

896{
897 if (d->compressedMoveEvent) {
898 // d->touchHasBlockedPressEvents = false;
899
901 d->compressedMoveEvent.reset();
902 //dbgInput << "Compressed move event received.";
903 } else {
904 //dbgInput << "Unexpected empty move event";
905 }
906}
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 953 of file kis_input_manager.cpp.

954{
955 if (!d->canvas) return;
956 KoToolManager *toolManager = KoToolManager::instance();
957 KoToolBase *tool = toolManager->toolById(canvas(), toolManager->activeToolId());
958 if (tool) {
959 if (tool->isInTextMode()) {
962 } else {
963 d->forwardAllEventsToTool = false;
965 }
966 }
967}
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 930 of file kis_input_manager.cpp.

931{
932 if (!d->canvas) return;
933 KoToolManager *toolManager = KoToolManager::instance();
934 KoToolBase *tool = toolManager->toolById(canvas(), toolManager->activeToolId());
935 if (tool) {
936 // once more, don't forget the global state whenever matcher may trigger a KisAbstractInputAction
938
940 if (tool->isInTextMode()) {
943 } else {
944 d->forwardAllEventsToTool = false;
946 }
947
948 d->matcher.suppressConflictingKeyActions(toolProxy()->toolPriorityShortcuts());
950 }
951}
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().

◆ startTouch()

bool KisInputManager::startTouch ( bool & retval)
private

Definition at line 878 of file kis_input_manager.cpp.

879{
880 Q_UNUSED(retval);
881
882 // Touch rejection: if touch is disabled on canvas, no need to block mouse press events
883 if (KisConfig(true).disableTouchOnCanvas()) {
885 }
886
887 return true;
888}

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 913 of file kis_input_manager.cpp.

914{
915 return d->toolProxy;
916}

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

Member Data Documentation

◆ d

Private* const KisInputManager::d
private

Definition at line 116 of file kis_input_manager.h.


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