Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_input_manager.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 *
3 * SPDX-FileCopyrightText: 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
4 * SPDX-FileCopyrightText: 2015 Michael Abrahams <miabraha@gmail.com>
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9#include "kis_input_manager.h"
10
11#include <kis_debug.h>
12#include <QQueue>
13#include <klocalizedstring.h>
14#include <QApplication>
15#include <QTouchEvent>
16#include <QWidget>
17
18#include <KoToolManager.h>
19#include <KoPointerEvent.h>
20
21#include "kis_tool_proxy.h"
22
23#include <kis_config.h>
24#include <kis_config_notifier.h>
25#include <kis_canvas2.h>
26#include <KisViewManager.h>
27#include <kis_image.h>
30
33#include "kis_pan_action.h"
36#include "kis_zoom_action.h"
39
41#include "kis_stroke_shortcut.h"
43#include "kis_touch_shortcut.h"
44
45#include "kis_input_profile.h"
48
51
53#include "kis_input_manager_p.h"
54#include "kis_algebra_2d.h"
55#include "config-qt-patches-present.h"
56
57
58template <typename T>
60 return reinterpret_cast<quintptr>(value.data());
61}
62
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}
86
88{
89#ifdef Q_OS_MACOS
90 KisExtendedModifiersMapper::setLocalMonitor(false);
91#endif
92 delete d;
93}
94
99
104
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}
114
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}
128
130{
131#ifdef Q_OS_WIN
132 d->ignoreHighFunctionKeys = KisConfig(true).ignoreHighFunctionKeys();
134#endif
135}
136
146
147
152
153void KisInputManager::attachPriorityEventFilter(QObject *filter, int priority)
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}
170
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}
183
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}
196
197#if defined (__clang__)
198#pragma GCC diagnostic ignored "-Wswitch"
199#endif
200
201bool KisInputManager::eventFilter(QObject* object, QEvent* event)
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}
247
248template <class Event>
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}
296
297bool shouldResetWheelDelta(QEvent * event)
298{
299 return
300 event->type() == QEvent::FocusIn ||
301 event->type() == QEvent::FocusOut ||
302 event->type() == QEvent::MouseButtonPress ||
303 event->type() == QEvent::MouseButtonRelease ||
304 event->type() == QEvent::MouseButtonDblClick ||
305 event->type() == QEvent::TabletPress ||
306 event->type() == QEvent::TabletRelease ||
307 event->type() == QEvent::Enter ||
308 event->type() == QEvent::Leave ||
309 event->type() == QEvent::TouchBegin ||
310 event->type() == QEvent::TouchEnd ||
311 event->type() == QEvent::TouchCancel ||
312 event->type() == QEvent::NativeGesture;
313
314}
315
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}
916
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}
928
933
934bool KisInputManager::touchHoldBufferUpdate(QTouchEvent *touchEvent)
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}
947
948bool KisInputManager::handleTouchBegin(QTouchEvent *touchEvent)
949{
950 return d->matcher.touchBeginEvent(touchEvent);
951}
952
953bool KisInputManager::handleTouchUpdate(QTouchEvent *touchEvent)
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}
980
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}
993
995{
996 return d->canvas;
997}
998
1003
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}
1015
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}
1038
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}
1054
1055
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}
float value(const T *src, size_t ch)
unsigned int uint
static void setInputManager(KisInputManager *manager)
KisAbstractCanvasWidget * canvasWidget
static KisConfigNotifier * instance()
bool disableTouchOnCanvas() const
bool ignoreHighFunctionKeys(bool defaultValue=false) const
static Qt::Key workaroundShiftAltMetaHell(const QKeyEvent *keyEvent)
void debugEvent(QEvent *event)
void setMaskSyntheticEvents(bool value)
bool addNativeGestureShortcut(KisAbstractInputAction *action, int index, KisShortcutConfiguration::GestureAction gesture)
bool handleCompressedTabletEvent(QEvent *event)
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)
QScopedPointer< QEvent > originatingTouchBeginEvent
bool isWithinTouchHoldSlopRange(const QPointF &currentPos) const
QPointer< KisToolProxy > toolProxy
KisSignalCompressor moveEventCompressor
void addStrokeShortcut(KisAbstractInputAction *action, int index, const QList< Qt::Key > &modifiers, Qt::MouseButtons buttons)
QScopedPointer< QEvent > compressedMoveEvent
void bufferTouchEvent(QTouchEvent *event)
QPointer< KisCanvas2 > canvas
bool processUnhandledEvent(QEvent *event)
KisSharedPtr< TabletLatencyTracker > tabletLatencyTracker
void addTouchShortcut(KisAbstractInputAction *action, int index, KisShortcutConfiguration::GestureAction gesture)
KisPopupWidgetInterface * popupWidget
bool handleTouchBegin(QTouchEvent *touchEvent)
KisCanvas2 * canvas() const
bool compressMoveEventCommon(Event *event)
~KisInputManager() override
bool touchHoldBufferUpdate(QTouchEvent *touchEvent)
void detachPriorityEventFilter(QObject *filter)
detachPriorityEventFilter
void setupAsEventFilter(QObject *receiver)
QPointer< KisToolProxy > toolProxy() const
void removeTrackedCanvas(KisCanvas2 *canvas)
void addTrackedCanvas(KisCanvas2 *canvas)
KisInputManager(QObject *parent)
void attachPriorityEventFilter(QObject *filter, int priority=0)
attachPriorityEventFilter
bool handleTouchUpdate(QTouchEvent *touchEvent)
bool eventFilter(QObject *object, QEvent *event) override
bool startTouch(bool &retval)
void registerPopupWidget(KisPopupWidgetInterface *popupWidget)
bool eventFilterImpl(QEvent *event)
static KisInputProfileManager * instance()
A container class for sets of shortcuts associated with an action.
QList< KisShortcutConfiguration * > allShortcuts() const
The PopupWidgetInterface abstract class defines the basic interface that will be used by all popup wi...
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.
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.
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)
void suppressConflictingKeyActions(const QVector< QKeySequence > &shortcuts)
bool keyReleased(Qt::Key key)
void touchCancelEvent(QTouchEvent *event, const QPointF &localPos)
bool wheelEvent(KisSingleActionShortcut::WheelAction wheelAction, QWheelEvent *event)
bool touchHoldBeginEvent(QTouchEvent *event)
@ WheelDown
Mouse wheel moves down.
@ WheelTrackpad
A pan movement on a trackpad.
@ WheelRight
Mouse wheel moves right.
@ WheelLeft
Mouse wheel moves left.
static KisTabletDebugger * instance()
bool maskSyntheticEvents() const
bool isInTextMode() const
KoToolBase * toolById(KoCanvasBase *canvas, const QString &id) const
QString activeToolId() const
Returns the toolId of the currently active tool.
static KoToolManager * instance()
Return the toolmanager singleton.
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
#define dbgUI
Definition kis_debug.h:52
#define dbgInput
Definition kis_debug.h:57
bool shouldResetWheelDelta(QEvent *event)
uint qHash(QPointer< T > value)
typedef void(QOPENGLF_APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC)(GLuint buffer)
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
bool eventFilter(QObject *target, QEvent *event)