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};
96 int recursiveCounter = 0;
97 int brokenByRecursion = 0;
104 q->m_d->recursiveCounter++;
105 q->m_d->brokenByRecursion++;
109 q->m_d->recursiveCounter--;
113 return q->m_d->recursiveCounter > 1;
123 q->m_d->brokenByRecursion = 0;
130 return q->m_d->brokenByRecursion > 0;
138 return suppressAllActions || !cursorEntered;
146 return suppressAllActions;
150 return suppressAllKeyboardActions;
165 return m_d->runningShortcut ||
m_d->touchShortcut ||
m_d->nativeGestureShortcut;
170 m_d->singleActionShortcuts.append(shortcut);
175 m_d->strokeShortcuts.append(shortcut);
180 m_d->touchShortcuts.append(shortcut);
184 m_d->nativeGestureShortcuts.append(shortcut);
189 return (
m_d->runningShortcut &&
m_d->runningShortcut->action()
190 &&
m_d->runningShortcut->action()->supportsHiResInputEvents(
m_d->runningShortcut->shortcutIndex()))
191 || (
m_d->touchShortcut &&
m_d->touchShortcut->action()
192 &&
m_d->touchShortcut->action()->supportsHiResInputEvents(
m_d->touchShortcut->shortcutIndex()))
193 || (
m_d->nativeGestureShortcut &&
m_d->nativeGestureShortcut->action()
194 &&
m_d->nativeGestureShortcut->action()->supportsHiResInputEvents(
m_d->nativeGestureShortcut->shortcutIndex()));
199 Private::RecursionNotifier notifier(
this);
203 if (
m_d->keys.contains(key)) {
DEBUG_ACTION(
"Peculiar, records show key was already pressed"); }
209 m_d->keys.insert(key);
212 if (notifier.isInRecursion()) {
224 Private::RecursionNotifier notifier(
this);
229 if (!
m_d->keys.contains(key)) {
DEBUG_ACTION(
"Peculiar, autorepeated key but can't remember it was pressed"); }
231 if (
m_d->polledKeys.contains(key)) {
232 m_d->polledKeys.remove(key);
235 if (notifier.isInRecursion()) {
239 QSet<Qt::Key> filteredKeys =
m_d->keys;
240 filteredKeys.remove(key);
249 Private::RecursionNotifier notifier(
this);
251 if (!
m_d->keys.contains(key)) {
DEBUG_ACTION(
"Peculiar, key released but can't remember it was pressed"); }
252 else m_d->keys.remove(key);
254 m_d->polledKeys.remove(key);
258 if (notifier.isInRecursion()) {
270 Private::RecursionNotifier notifier(
this);
284 if (notifier.isInRecursion()) {
296 Private::RecursionNotifier notifier(
this);
302 if (
m_d->runningShortcut) {
309 if (!
m_d->buttons.contains(
button))
reset(
"Peculiar, button released but we can't remember it was pressed");
312 if (notifier.isInRecursion()) {
324 Private::RecursionNotifier notifier(
this);
337 Private::RecursionNotifier notifier(
this);
339 if (notifier.isInRecursion()) {
345 if (
m_d->runningShortcut) {
347 m_d->runningShortcut->action()->inputEvent(event);
356 Private::RecursionNotifier notifier(
this);
358 m_d->cursorEntered =
true;
360 if (notifier.isInRecursion()) {
370 Private::RecursionNotifier notifier(
this);
372 m_d->cursorEntered =
false;
374 if (notifier.isInRecursion()) {
386 Private::RecursionNotifier notifier(
this);
388 m_d->lastTouchPoints =
event->touchPoints();
391 m_d->maxTouchPoints =
event->touchPoints().size();
392 m_d->matchingIteration = 1;
393 m_d->isTouchDragDetected =
false;
394#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
395 KoPointerEvent::copyQtPointerEvent(event,
m_d->bestCandidateTouchEvent);
397 m_d->bestCandidateTouchEvent.reset(event->clone());
400 return !notifier.isInRecursion();
409 const int touchPointCount =
event->touchPoints().size();
410 const int touchSlopSquared = 16 * 16;
412 for (
int i = 0; i <
event->touchPoints().size() && !
m_d->isTouchDragDetected; ++i) {
413 const QTouchEvent::TouchPoint &touchPoint =
event->touchPoints().at(i);
414 const QPointF delta = touchPoint.pos() - touchPoint.startPos();
415 const qreal deltaSquared = delta.x() * delta.x() + delta.y() * delta.y();
418 m_d->isTouchDragDetected = deltaSquared > touchSlopSquared;
424 const int numIterations = 10;
425 if (
m_d->matchingIteration <= numIterations && !
m_d->isTouchDragDetected) {
426 m_d->matchingIteration++;
432 if (
m_d->isTouchDragDetected) {
433 if (
m_d->touchShortcut && !
m_d->touchShortcut->matchDragType(event)) {
439 m_d->maxTouchPoints = touchPointCount;
442 }
else if (
m_d->touchShortcut) {
450 if (event->touchPointStates() & Qt::TouchPointPressed) {
451 m_d->touchShortcut->action()->begin(
m_d->touchShortcut->shortcutIndex(), event);
452 }
else if (event->touchPointStates() & Qt::TouchPointReleased) {
453 m_d->touchShortcut->action()->end(event);
455 m_d->touchShortcut->action()->inputEvent(event);
461 if ((event->touchPointStates() & Qt::TouchPointReleased) == Qt::TouchPointReleased && !
hasRunningShortcut()) {
463 if (
m_d->maxTouchPoints <= touchPointCount) {
464 m_d->maxTouchPoints = touchPointCount;
467 m_d->bestCandidateTouchEvent.reset();
477 Private::RecursionNotifier notifier(
this);
479 m_d->maxTouchPoints = 0;
489 if (notifier.isInRecursion()) {
501 Private::RecursionNotifier notifier(
this);
503 m_d->maxTouchPoints = 0;
508 if (
m_d->touchShortcut) {
510 m_d->touchShortcut = 0;
511 QScopedPointer<QEvent> dstEvent;
512#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
513 KoPointerEvent::copyQtPointerEvent(event, dstEvent);
515 dstEvent.reset(event->clone());
519 QTouchEvent* touchEvent =
dynamic_cast<QTouchEvent *
>(dstEvent.data());
521#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
522 touchEvent->setTouchPoints(
m_d->lastTouchPoints);
524 dstEvent.reset(
new QTouchEvent(event->type(),
525 event->pointingDevice(),
527 m_d->lastTouchPoints));
533 if (notifier.isInRecursion()) {
545 Private::RecursionNotifier notifier(
this);
547 return !notifier.isInRecursion();
556 else if (
m_d->nativeGestureShortcut) {
557 m_d->nativeGestureShortcut->action()->inputEvent( event );
566 Private::RecursionNotifier notifier(
this);
568 if (
m_d->nativeGestureShortcut && !
m_d->nativeGestureShortcut->match( event ) ) {
572 if (notifier.isInRecursion()) {
583 Qt::MouseButtons flags;
584 Q_FOREACH (Qt::MouseButton b, list) {
592 Private::RecursionNotifier notifier(
this);
595 reset(
"reinitialize");
597 if (notifier.isInRecursion()) {
607 Private::RecursionNotifier notifier(
this);
609 m_d->buttons.clear();
612 if (notifier.isInRecursion()) {
622 Q_FOREACH (Qt::Key key,
m_d->keys) {
623 if (!
keys.contains(key)) {
628 Q_FOREACH (Qt::Key key,
keys) {
629 if (!
m_d->keys.contains(key)) {
631 m_d->polledKeys << key;
635 Private::RecursionNotifier notifier(
this);
637 if (notifier.isInRecursion()) {
649 auto checkKey = [
this, modifiers] (Qt::Key key, Qt::KeyboardModifier modifier) {
650 return m_d->keys.contains(key) == bool(modifiers & modifier);
653 return checkKey(Qt::Key_Shift, Qt::ShiftModifier) &&
654 checkKey(Qt::Key_Control, Qt::ControlModifier) &&
655 checkKey(Qt::Key_Alt, Qt::AltModifier) &&
656 checkKey(Qt::Key_Meta, Qt::MetaModifier);
663 std::copy(
m_d->keys.begin(),
m_d->keys.end(), std::back_inserter(
keys));
669 return !
m_d->polledKeys.empty();
674 Private::RecursionNotifier notifier(
this);
678 if (
m_d->runningShortcut) {
695 Private::RecursionNotifier notifier(
this);
699 if (notifier.isInRecursion()) {
710 m_d->buttons.clear();
718 m_d->buttons.clear();
730 m_d->suppressedSingleActionShortcuts.clear();
733 Q_FOREACH (
const QKeySequence &seq, shortcuts) {
735 m_d->suppressedSingleActionShortcuts.insert(s);
743 m_d->suppressAllKeyboardActions =
value;
748 reset(
"Clearing shortcuts");
749 qDeleteAll(
m_d->singleActionShortcuts);
750 m_d->singleActionShortcuts.clear();
751 qDeleteAll(
m_d->strokeShortcuts);
752 qDeleteAll(
m_d->touchShortcuts);
753 m_d->strokeShortcuts.clear();
754 m_d->candidateShortcuts.clear();
755 m_d->touchShortcuts.clear();
756 m_d->runningShortcut = 0;
757 m_d->readyShortcut = 0;
762 m_d->actionGroupMask = func;
771template<
typename T,
typename U>
774 if (
m_d->actionsSuppressedIgnoreFocus() || (keyboard &&
m_d->KeyboardActionsSuppressed())) {
782 if (!
m_d->suppressedSingleActionShortcuts.contains(s) &&
784 s->
match(keysState, param) &&
799 return goodCandidate;
804 m_d->candidateShortcuts.clear();
805 if (
m_d->actionsSuppressed())
return;
808 bool containsOnlyModifiers = !
m_d->keys.isEmpty();
809 Q_FOREACH(
const Qt::Key k,
m_d->keys) {
810 if (k != Qt::Key_Shift && k != Qt::Key_Control && k != Qt::Key_Alt && k != Qt::Key_Meta) {
811 containsOnlyModifiers =
false;
815 if (
m_d->KeyboardActionsSuppressed()
816 && !containsOnlyModifiers && !
m_d->keys.isEmpty()
817 &&
m_d->buttons.isEmpty()) {
823 m_d->candidateShortcuts.append(s);
842 if (
m_d->readyShortcut) {
843 if (
m_d->readyShortcut != goodCandidate) {
844 m_d->readyShortcut->action()->deactivate(
m_d->readyShortcut->shortcutIndex());
847 m_d->readyShortcut = 0;
856 m_d->runningShortcut = goodCandidate;
857 Private::RecursionGuard guard(
this);
861 if (guard.brokenByRecursion()) {
863 m_d->runningShortcut = 0;
870 return m_d->runningShortcut;
884 if (
m_d->readyShortcut &&
m_d->readyShortcut != goodCandidate) {
886 m_d->readyShortcut->action()->deactivate(
m_d->readyShortcut->shortcutIndex());
887 m_d->readyShortcut = 0;
890 if (!
m_d->readyShortcut) {
901 m_d->readyShortcut = goodCandidate;
903 }
else if (
m_d->readyShortcut) {
905 m_d->readyShortcut->action()->deactivate(
m_d->readyShortcut->shortcutIndex());
906 m_d->readyShortcut = 0;
919 if (
m_d->runningShortcut &&
m_d->runningShortcut->matchBegin(
button)) {
923 m_d->runningShortcut = 0;
934 return !
m_d->runningShortcut;
948 m_d->runningShortcut = 0;
951 DEBUG_ACTION(
"Forced ending running shortcut at event");
964 if (
m_d->readyShortcut) {
966 m_d->readyShortcut->action()->deactivate(
m_d->readyShortcut->shortcutIndex());
967 m_d->readyShortcut = 0;
973 int touchPointCount =
event->touchPoints().size();
974 if (touchPointCount >
m_d->maxTouchPoints) {
975 m_d->maxTouchPoints = touchPointCount;
976#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
977 KoPointerEvent::copyQtPointerEvent(event,
m_d->bestCandidateTouchEvent);
979 m_d->bestCandidateTouchEvent.reset(event->clone());
1009 goodCandidate = shortcut;
1012 return goodCandidate;
1019 if (
m_d->actionsSuppressed())
1022 if (goodCandidate) {
1029 m_d->touchShortcut = goodCandidate;
1031 Private::RecursionGuard guard(
this);
1038 if (guard.brokenByRecursion()) {
1040 m_d->touchShortcut = 0;
1046 return m_d->touchShortcut;
1051 if (
m_d->touchShortcut) {
1059 m_d->touchShortcut = 0;
1071 if (
m_d->actionsSuppressed())
1075 if (shortcut->
match(event) && (!goodCandidate || shortcut->
priority() > goodCandidate->
priority())) {
1076 goodCandidate = shortcut;
1080 if (goodCandidate) {
1087 m_d->nativeGestureShortcut = goodCandidate;
1089 Private::RecursionGuard guard(
this);
1094 if (guard.brokenByRecursion()) {
1096 m_d->nativeGestureShortcut = 0;
1102 return m_d->nativeGestureShortcut;
1107 Private::RecursionNotifier notifier(
this);
1109 if (
m_d->nativeGestureShortcut) {
1116 m_d->nativeGestureShortcut = 0;
1121 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)
QList< KisTouchShortcut * > touchShortcuts
void handlePolledKeys(const QVector< Qt::Key > &keys)
bool tryEndRunningShortcut(Qt::MouseButton button, QEvent *event)
bool tryRunWheelShortcut(KisSingleActionShortcut::WheelAction wheelAction, QWheelEvent *event)
bool sanityCheckModifiersCorrectness(Qt::KeyboardModifiers modifiers) const
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)
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)
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