11#include <QTabletEvent>
26#define DEBUG_ACTION(text) qDebug() << __FUNCTION__ << "-" << text;
27#define DEBUG_SHORTCUT(text, shortcut) qDebug() << __FUNCTION__ << "-" << text << "act:" << shortcut->action()->name();
28#define DEBUG_KEY(text) qDebug() << __FUNCTION__ << "-" << text << "keys:" << m_d->keys;
29#define DEBUG_BUTTON_ACTION(text, button) qDebug() << __FUNCTION__ << "-" << text << "button:" << button << "btns:" << m_d->buttons << "keys:" << m_d->keys;
30#define DEBUG_EVENT_ACTION(text, event) if (event) {qDebug() << __FUNCTION__ << "-" << text << "type:" << event->type();}
31#define DEBUG_TOUCH_ACTION(text, event) \
33 qDebug() << __FUNCTION__ << "-" << text << "type:" << event->type() << "tps:" << event->touchPoints().size() \
34 << "maxTps:" << m_d->maxTouchPoints << "drag:" << m_d->isTouchDragDetected; \
37#define DEBUG_ACTION(text)
38#define DEBUG_KEY(text)
39#define DEBUG_SHORTCUT(text, shortcut)
40#define DEBUG_BUTTON_ACTION(text, button)
41#define DEBUG_EVENT_ACTION(text, event)
42#define DEBUG_TOUCH_ACTION(text, event)
53 , nativeGestureShortcut(0)
55 , suppressAllActions(
false)
56 , suppressAllKeyboardActions(
false)
57 , cursorEntered(
false)
62 qDeleteAll(singleActionShortcuts);
63 qDeleteAll(strokeShortcuts);
64 qDeleteAll(touchShortcuts);
86 int maxTouchPoints{0};
87 int matchingIteration{0};
88 bool isTouchDragDetected {
false};
89 bool isTouchHeld {
false};
97 int recursiveCounter = 0;
98 int brokenByRecursion = 0;
105 q->m_d->recursiveCounter++;
106 q->m_d->brokenByRecursion++;
110 q->m_d->recursiveCounter--;
114 return q->m_d->recursiveCounter > 1;
124 q->m_d->brokenByRecursion = 0;
131 return q->m_d->brokenByRecursion > 0;
139 return suppressAllActions || !cursorEntered;
147 return suppressAllActions;
151 return suppressAllKeyboardActions;
166 return m_d->runningShortcut ||
m_d->touchShortcut ||
m_d->nativeGestureShortcut;
172 if (shortcut->isHoldType() && shortcut->isAvailable(
m_d->actionGroupMask())) {
181 m_d->singleActionShortcuts.append(shortcut);
186 m_d->strokeShortcuts.append(shortcut);
191 m_d->touchShortcuts.append(shortcut);
195 m_d->nativeGestureShortcuts.append(shortcut);
200 return (
m_d->runningShortcut &&
m_d->runningShortcut->action()
201 &&
m_d->runningShortcut->action()->supportsHiResInputEvents(
m_d->runningShortcut->shortcutIndex()))
202 || (
m_d->touchShortcut &&
m_d->touchShortcut->action()
203 &&
m_d->touchShortcut->action()->supportsHiResInputEvents(
m_d->touchShortcut->shortcutIndex()))
204 || (
m_d->nativeGestureShortcut &&
m_d->nativeGestureShortcut->action()
205 &&
m_d->nativeGestureShortcut->action()->supportsHiResInputEvents(
m_d->nativeGestureShortcut->shortcutIndex()));
210 Private::RecursionNotifier notifier(
this);
214 if (
m_d->keys.contains(key)) {
DEBUG_ACTION(
"Peculiar, records show key was already pressed"); }
220 m_d->keys.insert(key);
223 if (notifier.isInRecursion()) {
235 Private::RecursionNotifier notifier(
this);
240 if (!
m_d->keys.contains(key)) {
DEBUG_ACTION(
"Peculiar, autorepeated key but can't remember it was pressed"); }
242 if (
m_d->polledKeys.contains(key)) {
243 m_d->polledKeys.remove(key);
246 if (notifier.isInRecursion()) {
250 QSet<Qt::Key> filteredKeys =
m_d->keys;
251 filteredKeys.remove(key);
260 Private::RecursionNotifier notifier(
this);
262 if (!
m_d->keys.contains(key)) {
DEBUG_ACTION(
"Peculiar, key released but can't remember it was pressed"); }
263 else m_d->keys.remove(key);
265 m_d->polledKeys.remove(key);
269 if (notifier.isInRecursion()) {
281 Private::RecursionNotifier notifier(
this);
295 if (notifier.isInRecursion()) {
307 Private::RecursionNotifier notifier(
this);
313 if (
m_d->runningShortcut) {
320 if (!
m_d->buttons.contains(
button))
reset(
"Peculiar, button released but we can't remember it was pressed");
323 if (notifier.isInRecursion()) {
335 Private::RecursionNotifier notifier(
this);
348 Private::RecursionNotifier notifier(
this);
350 if (notifier.isInRecursion()) {
356 if (
m_d->runningShortcut) {
358 m_d->runningShortcut->action()->inputEvent(event);
367 Private::RecursionNotifier notifier(
this);
369 m_d->cursorEntered =
true;
371 if (notifier.isInRecursion()) {
381 Private::RecursionNotifier notifier(
this);
383 m_d->cursorEntered =
false;
385 if (notifier.isInRecursion()) {
397 Private::RecursionNotifier notifier(
this);
399 m_d->lastTouchPoints =
event->touchPoints();
402 m_d->maxTouchPoints =
event->touchPoints().size();
403 m_d->matchingIteration = 1;
404 m_d->isTouchDragDetected =
false;
405 m_d->isTouchHeld =
false;
406#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
407 KoPointerEvent::copyQtPointerEvent(event,
m_d->bestCandidateTouchEvent);
409 m_d->bestCandidateTouchEvent.reset(event->clone());
412 return !notifier.isInRecursion();
419 if (
m_d->isTouchHeld) {
421 m_d->touchShortcut->action()->inputEvent(event);
427 const int touchPointCount =
event->touchPoints().size();
429 for (
int i = 0; i <
event->touchPoints().size() && !
m_d->isTouchDragDetected; ++i) {
430 const QTouchEvent::TouchPoint &touchPoint =
event->touchPoints().at(i);
431 const QPointF delta = touchPoint.pos() - touchPoint.startPos();
432 const qreal deltaSquared = delta.x() * delta.x() + delta.y() * delta.y();
441 const int numIterations = 10;
442 if (
m_d->matchingIteration <= numIterations && !
m_d->isTouchDragDetected) {
443 m_d->matchingIteration++;
449 if (
m_d->isTouchDragDetected) {
450 if (
m_d->touchShortcut && !
m_d->touchShortcut->matchDragType(event)) {
456 m_d->maxTouchPoints = touchPointCount;
459 }
else if (
m_d->touchShortcut) {
467 if (event->touchPointStates() & Qt::TouchPointPressed) {
468 m_d->touchShortcut->action()->begin(
m_d->touchShortcut->shortcutIndex(), event);
469 }
else if (event->touchPointStates() & Qt::TouchPointReleased) {
470 m_d->touchShortcut->action()->end(event);
472 m_d->touchShortcut->action()->inputEvent(event);
478 if ((event->touchPointStates() & Qt::TouchPointReleased) == Qt::TouchPointReleased && !
hasRunningShortcut()) {
480 if (
m_d->maxTouchPoints <= touchPointCount) {
481 m_d->maxTouchPoints = touchPointCount;
484 m_d->bestCandidateTouchEvent.reset();
494 Private::RecursionNotifier notifier(
this);
496 m_d->maxTouchPoints = 0;
497 m_d->isTouchHeld =
false;
507 if (notifier.isInRecursion()) {
519 Private::RecursionNotifier notifier(
this);
521 m_d->maxTouchPoints = 0;
522 m_d->isTouchHeld =
false;
527 if (
m_d->touchShortcut) {
529 m_d->touchShortcut = 0;
530 QScopedPointer<QEvent> dstEvent;
531#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
532 KoPointerEvent::copyQtPointerEvent(event, dstEvent);
534 dstEvent.reset(event->clone());
538 QTouchEvent* touchEvent =
dynamic_cast<QTouchEvent *
>(dstEvent.data());
540#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
541 touchEvent->setTouchPoints(
m_d->lastTouchPoints);
543 dstEvent.reset(
new QTouchEvent(event->type(),
544 event->pointingDevice(),
546 m_d->lastTouchPoints));
552 if (notifier.isInRecursion()) {
565 if (
m_d->runningShortcut) {
569 m_d->isTouchHeld =
true;
577 m_d->isTouchHeld =
false;
586 Private::RecursionNotifier notifier(
this);
588 return !notifier.isInRecursion();
597 else if (
m_d->nativeGestureShortcut) {
598 m_d->nativeGestureShortcut->action()->inputEvent( event );
607 Private::RecursionNotifier notifier(
this);
609 if (
m_d->nativeGestureShortcut && !
m_d->nativeGestureShortcut->match( event ) ) {
613 if (notifier.isInRecursion()) {
624 Qt::MouseButtons flags;
625 Q_FOREACH (Qt::MouseButton b, list) {
633 Private::RecursionNotifier notifier(
this);
636 reset(
"reinitialize");
638 if (notifier.isInRecursion()) {
648 Private::RecursionNotifier notifier(
this);
650 m_d->buttons.clear();
653 if (notifier.isInRecursion()) {
663 Q_FOREACH (Qt::Key key,
m_d->keys) {
664 if (!
keys.contains(key)) {
669 Q_FOREACH (Qt::Key key,
keys) {
670 if (!
m_d->keys.contains(key)) {
672 m_d->polledKeys << key;
676 Private::RecursionNotifier notifier(
this);
678 if (notifier.isInRecursion()) {
690 auto checkKey = [
this, modifiers] (Qt::Key key, Qt::KeyboardModifier modifier) {
691 return m_d->keys.contains(key) == bool(modifiers & modifier);
694 return checkKey(Qt::Key_Shift, Qt::ShiftModifier) &&
695 checkKey(Qt::Key_Control, Qt::ControlModifier) &&
696 checkKey(Qt::Key_Alt, Qt::AltModifier) &&
697 checkKey(Qt::Key_Meta, Qt::MetaModifier);
704 std::copy(
m_d->keys.begin(),
m_d->keys.end(), std::back_inserter(
keys));
710 return !
m_d->polledKeys.empty();
715 Private::RecursionNotifier notifier(
this);
719 if (
m_d->runningShortcut) {
736 Private::RecursionNotifier notifier(
this);
740 if (notifier.isInRecursion()) {
751 m_d->buttons.clear();
759 m_d->buttons.clear();
771 m_d->suppressedSingleActionShortcuts.clear();
774 Q_FOREACH (
const QKeySequence &seq, shortcuts) {
776 m_d->suppressedSingleActionShortcuts.insert(s);
784 m_d->suppressAllKeyboardActions =
value;
789 reset(
"Clearing shortcuts");
790 qDeleteAll(
m_d->singleActionShortcuts);
791 m_d->singleActionShortcuts.clear();
792 qDeleteAll(
m_d->strokeShortcuts);
793 qDeleteAll(
m_d->touchShortcuts);
794 m_d->strokeShortcuts.clear();
795 m_d->candidateShortcuts.clear();
796 m_d->touchShortcuts.clear();
797 m_d->runningShortcut = 0;
798 m_d->readyShortcut = 0;
803 m_d->actionGroupMask = func;
812template<
typename T,
typename U>
815 if (
m_d->actionsSuppressedIgnoreFocus() || (keyboard &&
m_d->KeyboardActionsSuppressed())) {
823 if (!
m_d->suppressedSingleActionShortcuts.contains(s) &&
825 s->
match(keysState, param) &&
840 return goodCandidate;
845 m_d->candidateShortcuts.clear();
846 if (
m_d->actionsSuppressed())
return;
849 bool containsOnlyModifiers = !
m_d->keys.isEmpty();
850 Q_FOREACH(
const Qt::Key k,
m_d->keys) {
851 if (k != Qt::Key_Shift && k != Qt::Key_Control && k != Qt::Key_Alt && k != Qt::Key_Meta) {
852 containsOnlyModifiers =
false;
856 if (
m_d->KeyboardActionsSuppressed()
857 && !containsOnlyModifiers && !
m_d->keys.isEmpty()
858 &&
m_d->buttons.isEmpty()) {
864 m_d->candidateShortcuts.append(s);
883 if (
m_d->readyShortcut) {
884 if (
m_d->readyShortcut != goodCandidate) {
885 m_d->readyShortcut->action()->deactivate(
m_d->readyShortcut->shortcutIndex());
888 m_d->readyShortcut = 0;
897 m_d->runningShortcut = goodCandidate;
898 Private::RecursionGuard guard(
this);
902 if (guard.brokenByRecursion()) {
904 m_d->runningShortcut = 0;
911 return m_d->runningShortcut;
925 if (
m_d->readyShortcut &&
m_d->readyShortcut != goodCandidate) {
927 m_d->readyShortcut->action()->deactivate(
m_d->readyShortcut->shortcutIndex());
928 m_d->readyShortcut = 0;
931 if (!
m_d->readyShortcut) {
942 m_d->readyShortcut = goodCandidate;
944 }
else if (
m_d->readyShortcut) {
946 m_d->readyShortcut->action()->deactivate(
m_d->readyShortcut->shortcutIndex());
947 m_d->readyShortcut = 0;
960 if (
m_d->runningShortcut &&
m_d->runningShortcut->matchBegin(
button)) {
964 m_d->runningShortcut = 0;
975 return !
m_d->runningShortcut;
989 m_d->runningShortcut = 0;
992 DEBUG_ACTION(
"Forced ending running shortcut at event");
1005 if (
m_d->readyShortcut) {
1007 m_d->readyShortcut->action()->deactivate(
m_d->readyShortcut->shortcutIndex());
1008 m_d->readyShortcut = 0;
1014 int touchPointCount =
event->touchPoints().size();
1015 if (touchPointCount >
m_d->maxTouchPoints) {
1016 m_d->maxTouchPoints = touchPointCount;
1017#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1018 KoPointerEvent::copyQtPointerEvent(event,
m_d->bestCandidateTouchEvent);
1020 m_d->bestCandidateTouchEvent.reset(event->clone());
1029 if (goodCandidate) {
1047 goodCandidate = shortcut;
1050 return goodCandidate;
1055 if (
m_d->isTouchHeld) {
1057 }
else if (
m_d->isTouchDragDetected) {
1068 if (
m_d->actionsSuppressed())
1071 if (goodCandidate) {
1078 m_d->touchShortcut = goodCandidate;
1080 Private::RecursionGuard guard(
this);
1087 if (guard.brokenByRecursion()) {
1089 m_d->touchShortcut = 0;
1095 return m_d->touchShortcut;
1100 if (
m_d->touchShortcut) {
1108 m_d->touchShortcut = 0;
1120 if (
m_d->actionsSuppressed())
1124 if (shortcut->
match(event) && (!goodCandidate || shortcut->
priority() > goodCandidate->
priority())) {
1125 goodCandidate = shortcut;
1129 if (goodCandidate) {
1136 m_d->nativeGestureShortcut = goodCandidate;
1138 Private::RecursionGuard guard(
this);
1143 if (guard.brokenByRecursion()) {
1145 m_d->nativeGestureShortcut = 0;
1151 return m_d->nativeGestureShortcut;
1156 Private::RecursionNotifier notifier(
this);
1158 if (
m_d->nativeGestureShortcut) {
1165 m_d->nativeGestureShortcut = 0;
1170 if (notifier.isInRecursion()) {
float value(const T *src, size_t ch)
KisAbstractInputAction * action
bool isAvailable(KisInputActionGroupsMask mask) const
bool match(QNativeGestureEvent *event)
int priority() const override
bool KeyboardActionsSuppressed() const
QSet< Qt::Key > polledKeys
void lostFocusEvent(const QPointF &localPos)
void tryActivateReadyShortcut()
QSet< Qt::MouseButton > buttons
QList< QTouchEvent::TouchPoint > lastTouchPoints
bool tryRunTouchShortcut(QTouchEvent *event)
void forceEndRunningShortcut(const QPointF &localPos)
bool actionsSuppressedIgnoreFocus() const
bool hasRunningShortcut() const
QList< KisNativeGestureShortcut * > nativeGestureShortcuts
bool autoRepeatedKeyPressed(Qt::Key key)
bool buttonReleased(Qt::MouseButton button, QEvent *event)
QList< KisStrokeShortcut * > strokeShortcuts
void addShortcut(KisSingleActionShortcut *shortcut)
QList< KisSingleActionShortcut * > singleActionShortcuts
bool tryRunNativeGestureShortcut(QNativeGestureEvent *event)
QList< KisStrokeShortcut * > candidateShortcuts
void toolHasBeenActivated()
bool tryRunSingleActionShortcutImpl(T param, U *event, const QSet< Qt::Key > &keysState, bool keyboard=true)
bool actionsSuppressed() const
bool nativeGestureEndEvent(QNativeGestureEvent *event)
bool tryEndTouchShortcut(QTouchEvent *event)
bool keyPressed(Qt::Key key)
bool touchBeginEvent(QTouchEvent *event)
void prepareReadyShortcuts()
bool suppressAllKeyboardActions
bool tryRunReadyShortcut(Qt::MouseButton button, QEvent *event)
void reinitializeButtons()
KisTouchShortcut * touchShortcut
void setInputActionGroupsMaskCallback(std::function< KisInputActionGroupsMask()> func)
KisStrokeShortcut * readyShortcut
QVector< Qt::Key > debugPressedKeys() const
KisTouchShortcut * matchTouchShortcut(QTouchEvent *event)
bool touchEndEvent(QTouchEvent *event)
bool touchUpdateEvent(QTouchEvent *event)
std::function< KisInputActionGroupsMask()> actionGroupMask
bool nativeGestureEvent(QNativeGestureEvent *event)
KisNativeGestureShortcut * nativeGestureShortcut
bool buttonPressed(Qt::MouseButton button, QEvent *event)
static constexpr int TOUCH_SLOP_SQUARED
QList< KisTouchShortcut * > touchShortcuts
void handlePolledKeys(const QVector< Qt::Key > &keys)
bool hasTouchHoldShortcut() const
bool tryEndRunningShortcut(Qt::MouseButton button, QEvent *event)
bool tryRunWheelShortcut(KisSingleActionShortcut::WheelAction wheelAction, QWheelEvent *event)
bool sanityCheckModifiersCorrectness(Qt::KeyboardModifiers modifiers) const
bool matchTouchShortcutBasedOnState(QTouchEvent *event, KisTouchShortcut *shortcut)
bool pointerMoved(QEvent *event)
bool supportsHiResInputEvents()
bool nativeGestureBeginEvent(QNativeGestureEvent *event)
void setMaxTouchPointEvent(QTouchEvent *event)
void suppressConflictingKeyActions(const QVector< QKeySequence > &shortcuts)
QScopedPointer< QEvent > bestCandidateTouchEvent
KisStrokeShortcut * runningShortcut
bool keyReleased(Qt::Key key)
void touchCancelEvent(QTouchEvent *event, const QPointF &localPos)
void fireReadyTouchShortcut(QTouchEvent *event)
QSet< KisSingleActionShortcut * > suppressedSingleActionShortcuts
bool wheelEvent(KisSingleActionShortcut::WheelAction wheelAction, QWheelEvent *event)
bool tryEndNativeGestureShortcut(QNativeGestureEvent *event)
bool touchHoldBeginEvent(QTouchEvent *event)
void forceDeactivateAllActions()
bool conflictsWith(const QKeySequence &seq)
bool match(const QSet< Qt::Key > &modifiers, Qt::Key key)
int priority() const override
int priority() const override
QMouseEvent fakeEndEvent(const QPointF &localPos) const
bool matchReady(const QSet< Qt::Key > &modifiers, const QSet< Qt::MouseButton > &buttons)
bool matchBegin(Qt::MouseButton button)
The KisTouchShortcut class only handles touch gestures it does not handle tool invocation i....
bool matchDragType(QTouchEvent *event)
bool matchTapType(QTouchEvent *event)
bool matchHoldType(QTouchEvent *event)
int priority() const override
#define KIS_SAFE_ASSERT_RECOVER(cond)
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
#define DEBUG_BUTTON_ACTION(text, button)
#define DEBUG_SHORTCUT(text, shortcut)
Qt::MouseButtons listToFlags(const QList< Qt::MouseButton > &list)
#define DEBUG_EVENT_ACTION(text, event)
#define DEBUG_TOUCH_ACTION(text, event)
QString button(const QWheelEvent &ev)
#define DEBUG_ACTION(action)
RecursionGuard(KisShortcutMatcher *_q)
bool brokenByRecursion() const
RecursionNotifier(KisShortcutMatcher *_q)
bool isInRecursion() const