diff --git a/krita/main.cc b/krita/main.cc --- a/krita/main.cc +++ b/krita/main.cc @@ -145,16 +145,6 @@ } } -#if defined Q_OS_WIN - KisTabletSupportWin tabletSupportWin; - tabletSupportWin.init(); - app.installNativeEventFilter(&tabletSupportWin); -#elif defined HAVE_X11 - KisTabletSupportX11 tabletSupportX11; - tabletSupportX11.init(); - app.installNativeEventFilter(&tabletSupportX11); -#endif - #if defined HAVE_OPENGL KisOpenGL::initialize(); diff --git a/krita/ui/CMakeLists.txt b/krita/ui/CMakeLists.txt --- a/krita/ui/CMakeLists.txt +++ b/krita/ui/CMakeLists.txt @@ -238,6 +238,7 @@ widgets/kis_tool_button.cpp widgets/kis_floating_message.cpp input/kis_input_manager.cpp + input/kis_input_manager_p.cpp input/kis_extended_modifiers_mapper.cpp input/kis_abstract_input_action.cpp input/kis_tool_invocation_action.cpp diff --git a/krita/ui/canvas/kis_canvas_controller.cpp b/krita/ui/canvas/kis_canvas_controller.cpp --- a/krita/ui/canvas/kis_canvas_controller.cpp +++ b/krita/ui/canvas/kis_canvas_controller.cpp @@ -61,8 +61,6 @@ pointerPos = mouseEvent->pos(); } else if (QTabletEvent *tabletEvent = dynamic_cast(event)) { pointerPos = tabletEvent->pos(); - } else if (KisTabletEvent *kisTabletEvent = dynamic_cast(event)) { - pointerPos = kisTabletEvent->pos(); } QPointF documentPos = coordinatesConverter->widgetToDocument(pointerPos); @@ -144,9 +142,9 @@ { KoCanvasBase *canvas = this->canvas(); if (canvas && canvas->canvasWidget() && (watched == canvas->canvasWidget())) { - if (event->type() == QEvent::MouseMove || event->type() == QEvent::TabletMove || event->type() == (QEvent::Type)KisTabletEvent::TabletMoveEx) { + if (event->type() == QEvent::MouseMove || event->type() == QEvent::TabletMove) { m_d->emitPointerPositionChangedSignals(event); - return false; + return true; } } diff --git a/krita/ui/canvas/kis_tool_proxy.h b/krita/ui/canvas/kis_tool_proxy.h --- a/krita/ui/canvas/kis_tool_proxy.h +++ b/krita/ui/canvas/kis_tool_proxy.h @@ -35,19 +35,19 @@ public: KisToolProxy(KoCanvasBase *canvas, QObject *parent = 0); - void forwardMouseHoverEvent(QMouseEvent *mouseEvent, QTabletEvent *lastTabletEvent); + void forwardHoverEvent(QEvent *event); + /** * Forwards the event to the active tool and returns true if the - * event 'was not ignored'. That is by default the event is + * event was not ignored. That is by default the event is * considered accepted, but the tool can explicitly ignore it. * @param state beginning, continuing, or ending the action. * @param action alternate tool action requested. * @param event the event being sent to the tool by the AbstractInputAction. * @param originalEvent the original event received by the AbstractInputAction. - * @param lastTabletEvent The event object for the last tablet event. */ - bool forwardEvent(ActionState state, KisTool::ToolAction action, QEvent *event, QEvent *originalEvent, QTabletEvent *lastTabletEvent); + bool forwardEvent(ActionState state, KisTool::ToolAction action, QEvent *event, QEvent *originalEvent); bool primaryActionSupportsHiResEvents() const; void setActiveTool(KoToolBase *tool); diff --git a/krita/ui/canvas/kis_tool_proxy.cpp b/krita/ui/canvas/kis_tool_proxy.cpp --- a/krita/ui/canvas/kis_tool_proxy.cpp +++ b/krita/ui/canvas/kis_tool_proxy.cpp @@ -18,6 +18,7 @@ #include "kis_tool_proxy.h" #include "kis_canvas2.h" +#include "input/kis_tablet_debugger.h" #include @@ -77,31 +78,45 @@ return KoPointerEvent(&fakeEvent, QPointF()); } -void KisToolProxy::forwardMouseHoverEvent(QMouseEvent *mouseEvent, QTabletEvent *lastTabletEvent) +void KisToolProxy::forwardHoverEvent(QEvent *event) { - if (lastTabletEvent) { - QPointF docPoint = tabletToDocument(lastTabletEvent->hiResGlobalPos()); - this->tabletEvent(lastTabletEvent, docPoint); - } else { - KIS_ASSERT_RECOVER_RETURN(mouseEvent->type() == QEvent::MouseMove); + switch (event->type()) { + case QEvent::TabletMove: { + QTabletEvent *tabletEvent = static_cast(event); + QPointF docPoint = widgetToDocument(tabletEvent->posF()); + this->tabletEvent(tabletEvent, docPoint); + return; + } + + case QEvent::MouseMove: { + QMouseEvent *mouseEvent = static_cast(event); QPointF docPoint = widgetToDocument(mouseEvent->posF()); mouseMoveEvent(mouseEvent, docPoint); + return; + } + + default: { + qWarning() << "forwardHoverEvent encountered unknown event type."; + return; + } } } -bool KisToolProxy::forwardEvent(ActionState state, KisTool::ToolAction action, QEvent *event, QEvent *originalEvent, QTabletEvent *lastTabletEvent) +bool KisToolProxy::forwardEvent(ActionState state, KisTool::ToolAction action, QEvent *event, QEvent *originalEvent) { bool retval = true; - QTabletEvent *tabletEvent = dynamic_cast(event); - QTouchEvent *touchEvent = dynamic_cast(event); - QMouseEvent *mouseEvent = dynamic_cast(event); + // Bad smell. Convert it to a switch statement? Should this be happening somewhere else? + QTabletEvent *tabletEvent = dynamic_cast(event); + QTouchEvent *touchEvent = dynamic_cast(event); + QMouseEvent *mouseEvent = dynamic_cast(event); if (tabletEvent) { - QPointF docPoint = tabletToDocument(tabletEvent->hiResGlobalPos()); - tabletEvent->accept(); + QPointF docPoint = tabletToDocument(tabletEvent->globalPosF()); + // QPointF docPoint = tabletToDocument(tabletEvent->posF()); this->tabletEvent(tabletEvent, docPoint); forwardToTool(state, action, tabletEvent, docPoint); + tabletEvent->accept(); retval = tabletEvent->isAccepted(); } else if (touchEvent) { if (state == END && touchEvent->type() != QEvent::TouchEnd) { @@ -114,35 +129,28 @@ this->touchEvent(touchEvent); } } else if (mouseEvent) { - if (lastTabletEvent) { - QPointF docPoint = tabletToDocument(lastTabletEvent->hiResGlobalPos()); - lastTabletEvent->accept(); - this->tabletEvent(lastTabletEvent, docPoint); - forwardToTool(state, action, lastTabletEvent, docPoint); - retval = lastTabletEvent->isAccepted(); - } else { - QPointF docPoint = widgetToDocument(mouseEvent->posF()); - mouseEvent->accept(); - if (mouseEvent->type() == QEvent::MouseButtonPress) { - mousePressEvent(mouseEvent, docPoint); - } else if (mouseEvent->type() == QEvent::MouseButtonDblClick) { - mouseDoubleClickEvent(mouseEvent, docPoint); - } else if (mouseEvent->type() == QEvent::MouseButtonRelease) { - mouseReleaseEvent(mouseEvent, docPoint); - } else if (mouseEvent->type() == QEvent::MouseMove) { - mouseMoveEvent(mouseEvent, docPoint); - } - forwardToTool(state, action, originalEvent, docPoint); - retval = mouseEvent->isAccepted(); + QPointF docPoint = widgetToDocument(mouseEvent->posF()); + mouseEvent->accept(); + if (mouseEvent->type() == QEvent::MouseButtonPress) { + mousePressEvent(mouseEvent, docPoint); + } else if (mouseEvent->type() == QEvent::MouseButtonDblClick) { + mouseDoubleClickEvent(mouseEvent, docPoint); + } else if (mouseEvent->type() == QEvent::MouseButtonRelease) { + mouseReleaseEvent(mouseEvent, docPoint); + } else if (mouseEvent->type() == QEvent::MouseMove) { + mouseMoveEvent(mouseEvent, docPoint); } + forwardToTool(state, action, originalEvent, docPoint); + retval = mouseEvent->isAccepted(); } else if(event->type() == QEvent::KeyPress) { QKeyEvent* kevent = static_cast(event); keyPressEvent(kevent); } else if(event->type() == QEvent::KeyRelease) { QKeyEvent* kevent = static_cast(event); keyReleaseEvent(kevent); } + // qDebug() << "Event" << KisTabletDebugger::exTypeToString(event->type()) << "was" << (retval ? QString("accepted") : QString("rejected")); return retval; } diff --git a/krita/ui/input/kis_abstract_input_action.h b/krita/ui/input/kis_abstract_input_action.h --- a/krita/ui/input/kis_abstract_input_action.h +++ b/krita/ui/input/kis_abstract_input_action.h @@ -20,9 +20,11 @@ #define KIS_ABSTRACT_INPUT_ACTION_H #include +#include #include "kritaui_export.h" class QPointF; +class QPoint; class QEvent; class KisInputManager; @@ -47,9 +49,6 @@ * will be of QMouseEvent type, representing the event happened. * All the mouse move events between begin() and end() will be * redirected to the inputEvent() method. - * - * You can fetch the QTabletEvent data for the current mouse event - * with inputManager()->lastTabletEvent(). */ class KRITAUI_EXPORT KisAbstractInputAction { @@ -186,10 +185,25 @@ void setShortcutIndexes(const QHash &indexes); /** - * Convenience method for handling the mouse moves. It is - * called by the default implementation of inputEvent + * Convenience method for handling cursor movement for tablet, mouse and touch. + * The default implementation of inputEvent calls this function. + */ + virtual void cursorMoved(const QPointF &lastPos, const QPointF &pos); + + /** + * Convenience method to extract the position from a cursor movement event. + * + * \param event A mouse or tablet event. + */ + static QPoint eventPos(const QEvent *event); + + /** + * Convenience method to extract the floating point position from a + * cursor movement event. + * + * \param event A mouse or tablet event. */ - virtual void mouseMoved(const QPointF &lastPos, const QPointF &pos); + static QPointF eventPosF(const QEvent *event); private: friend class KisInputManager; diff --git a/krita/ui/input/kis_abstract_input_action.cpp b/krita/ui/input/kis_abstract_input_action.cpp --- a/krita/ui/input/kis_abstract_input_action.cpp +++ b/krita/ui/input/kis_abstract_input_action.cpp @@ -21,6 +21,7 @@ #include #include #include +#include class Q_DECL_HIDDEN KisAbstractInputAction::Private { @@ -30,7 +31,7 @@ QString description; QHash indexes; - QPointF lastMousePosition; + QPointF lastCursorPosition; static KisInputManager* inputManager; }; @@ -63,29 +64,26 @@ { Q_UNUSED(shortcut); - QMouseEvent *mouseEvent; - if (event && (mouseEvent = dynamic_cast(event))) { - d->lastMousePosition = mouseEvent->posF(); + if (event) { + d->lastCursorPosition = eventPosF(event); } } void KisAbstractInputAction::inputEvent(QEvent* event) { - QMouseEvent *mouseEvent; - if (event && (mouseEvent = dynamic_cast(event))) { - if (mouseEvent->type() == QEvent::MouseMove) { - mouseMoved(d->lastMousePosition, mouseEvent->posF()); - } - d->lastMousePosition = mouseEvent->posF(); + if (event) { + QPointF newPosition = eventPosF(event); + cursorMoved(d->lastCursorPosition, newPosition); + d->lastCursorPosition = newPosition; } } void KisAbstractInputAction::end(QEvent *event) { Q_UNUSED(event); } -void KisAbstractInputAction::mouseMoved(const QPointF &lastPos, const QPointF &pos) +void KisAbstractInputAction::cursorMoved(const QPointF &lastPos, const QPointF &pos) { Q_UNUSED(lastPos); Q_UNUSED(pos); @@ -156,3 +154,49 @@ Q_UNUSED(shortcut); return false; } + + +QPoint KisAbstractInputAction::eventPos(const QEvent *event) { + + switch (event->type()) { + case QEvent::MouseMove: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + return static_cast(event)->pos(); + + case QEvent::TabletMove: + case QEvent::TabletPress: + case QEvent::TabletRelease: + return static_cast(event)->pos(); + + case QEvent::Wheel: + return static_cast(event)->pos(); + + default: + qCritical() << "KisAbstractInputAction tried to process event data from an unhandled event type" << event->type(); + return QPoint(); + } +} + + +QPointF KisAbstractInputAction::eventPosF(const QEvent *event) { + + switch (event->type()) { + case QEvent::MouseMove: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + return static_cast(event)->posF(); + + case QEvent::TabletMove: + case QEvent::TabletPress: + case QEvent::TabletRelease: + return static_cast(event)->posF(); + + case QEvent::Wheel: + return static_cast(event)->posF(); + + default: + qCritical() << "KisAbstractInputAction tried to process event data from an unhandled event type" << event->type(); + return QPoint(); + } +} diff --git a/krita/ui/input/kis_alternate_invocation_action.cpp b/krita/ui/input/kis_alternate_invocation_action.cpp --- a/krita/ui/input/kis_alternate_invocation_action.cpp +++ b/krita/ui/input/kis_alternate_invocation_action.cpp @@ -102,68 +102,62 @@ return 9; } + + void KisAlternateInvocationAction::begin(int shortcut, QEvent *event) { if (!event) return; KisAbstractInputAction::begin(shortcut, event); - QMouseEvent *mouseEvent = dynamic_cast(event); - QMouseEvent targetEvent(QEvent::MouseButtonPress, mouseEvent->pos(), Qt::LeftButton, Qt::LeftButton, Qt::ControlModifier); + QMouseEvent targetEvent(QEvent::MouseButtonPress, eventPos(event), Qt::LeftButton, Qt::LeftButton, Qt::ControlModifier); // There must be a better way m_d->savedAction = shortcutToToolAction(shortcut); - inputManager()->toolProxy()->forwardEvent( - KisToolProxy::BEGIN, m_d->savedAction, &targetEvent, event, - inputManager()->lastTabletEvent()); + inputManager()->toolProxy()->forwardEvent(KisToolProxy::BEGIN, m_d->savedAction, &targetEvent, event); } void KisAlternateInvocationAction::end(QEvent *event) { if (!event) return; - QMouseEvent *mouseEvent = dynamic_cast(event); - - QMouseEvent targetEvent(*mouseEvent); + Qt::KeyboardModifiers modifiers; switch (m_d->savedAction) { case KisTool::AlternatePickFgNode: - targetEvent = QMouseEvent(QEvent::MouseButtonRelease, mouseEvent->pos(), Qt::LeftButton, Qt::LeftButton, Qt::ControlModifier); + modifiers = Qt::ControlModifier; break; case KisTool::AlternateThird: - targetEvent = QMouseEvent(QEvent::MouseButtonRelease, mouseEvent->pos(), Qt::LeftButton, Qt::LeftButton, Qt::ControlModifier | Qt::AltModifier); + modifiers = Qt::ControlModifier | Qt::AltModifier; break; default: ; } - inputManager()->toolProxy()->forwardEvent( - KisToolProxy::END, m_d->savedAction, &targetEvent, event, - inputManager()->lastTabletEvent()); + QMouseEvent targetEvent = QMouseEvent(QEvent::MouseButtonRelease, eventPos(event), Qt::LeftButton, Qt::LeftButton, modifiers); + inputManager()->toolProxy()->forwardEvent(KisToolProxy::END, m_d->savedAction, &targetEvent, event); KisAbstractInputAction::end(event); } void KisAlternateInvocationAction::inputEvent(QEvent* event) { - if (event && event->type() == QEvent::MouseMove) { - QMouseEvent *mouseEvent = static_cast(event); - - QMouseEvent targetEvent(QEvent::MouseMove, mouseEvent->pos(), Qt::NoButton, Qt::LeftButton, Qt::ShiftModifier); - - switch (m_d->savedAction) { - case KisTool::AlternatePickFgNode: - targetEvent = QMouseEvent(QEvent::MouseMove, mouseEvent->pos(), Qt::LeftButton, Qt::LeftButton, Qt::ControlModifier); - break; - case KisTool::AlternateThird: - targetEvent = QMouseEvent(QEvent::MouseMove, mouseEvent->pos(), Qt::LeftButton, Qt::LeftButton, Qt::ControlModifier | Qt::AltModifier); - break; - default: - ; - } - - inputManager()->toolProxy()->forwardEvent( - KisToolProxy::CONTINUE, m_d->savedAction, &targetEvent, event, - inputManager()->lastTabletEvent()); + if (!event || (event->type() != QEvent::MouseMove && event->type() != QEvent::TabletMove)) return; + + Qt::KeyboardModifiers modifiers; + switch (m_d->savedAction) { + case KisTool::AlternatePickFgNode: + modifiers = Qt::ControlModifier; + break; + case KisTool::AlternateThird: + modifiers = Qt::ControlModifier | Qt::AltModifier; + break; + default: + modifiers = Qt::ShiftModifier; } + + QMouseEvent targetEvent(QEvent::MouseMove, eventPos(event), Qt::LeftButton, Qt::LeftButton, modifiers); + inputManager()->toolProxy()->forwardEvent( + KisToolProxy::CONTINUE, m_d->savedAction, &targetEvent, event); + } diff --git a/krita/ui/input/kis_change_primary_setting_action.cpp b/krita/ui/input/kis_change_primary_setting_action.cpp --- a/krita/ui/input/kis_change_primary_setting_action.cpp +++ b/krita/ui/input/kis_change_primary_setting_action.cpp @@ -61,39 +61,28 @@ { KisAbstractInputAction::begin(shortcut, event); - QMouseEvent *mouseEvent = dynamic_cast(event); - if (mouseEvent) { - QMouseEvent targetEvent(QEvent::MouseButtonPress, mouseEvent->pos(), Qt::LeftButton, Qt::LeftButton, Qt::ShiftModifier); + if (event) { + QMouseEvent targetEvent(QEvent::MouseButtonPress, eventPos(event), Qt::LeftButton, Qt::LeftButton, Qt::ShiftModifier); - inputManager()->toolProxy()->forwardEvent( - KisToolProxy::BEGIN, KisTool::AlternateChangeSize, &targetEvent, event, - inputManager()->lastTabletEvent()); + inputManager()->toolProxy()->forwardEvent(KisToolProxy::BEGIN, KisTool::AlternateChangeSize, &targetEvent, event); } } void KisChangePrimarySettingAction::end(QEvent *event) { - QMouseEvent *mouseEvent = dynamic_cast(event); - if (mouseEvent) { - QMouseEvent targetEvent(QEvent::MouseButtonRelease, mouseEvent->pos(), Qt::LeftButton, Qt::LeftButton, Qt::ShiftModifier); - - inputManager()->toolProxy()->forwardEvent( - KisToolProxy::END, KisTool::AlternateChangeSize, &targetEvent, event, - inputManager()->lastTabletEvent()); + if (event) { + QMouseEvent targetEvent(QEvent::MouseButtonRelease, eventPos(event), Qt::LeftButton, Qt::LeftButton, Qt::ShiftModifier); + inputManager()->toolProxy()->forwardEvent(KisToolProxy::END, KisTool::AlternateChangeSize, &targetEvent, event); } KisAbstractInputAction::end(event); } void KisChangePrimarySettingAction::inputEvent(QEvent* event) { - if (event && event->type() == QEvent::MouseMove) { - QMouseEvent *mouseEvent = static_cast(event); - - QMouseEvent targetEvent(QEvent::MouseButtonRelease, mouseEvent->pos(), Qt::NoButton, Qt::LeftButton, Qt::ShiftModifier); + // Is there a reason to restrict to only mouseMove events? + if (!event || (event->type() != QEvent::MouseMove && event->type() != QEvent::TabletMove)) return; - inputManager()->toolProxy()->forwardEvent( - KisToolProxy::CONTINUE, KisTool::AlternateChangeSize, &targetEvent, event, - inputManager()->lastTabletEvent()); - } + QMouseEvent targetEvent(QEvent::MouseButtonRelease, eventPos(event), Qt::NoButton, Qt::LeftButton, Qt::ShiftModifier); + inputManager()->toolProxy()->forwardEvent(KisToolProxy::CONTINUE, KisTool::AlternateChangeSize, &targetEvent, event); } diff --git a/krita/ui/input/kis_gamma_exposure_action.h b/krita/ui/input/kis_gamma_exposure_action.h --- a/krita/ui/input/kis_gamma_exposure_action.h +++ b/krita/ui/input/kis_gamma_exposure_action.h @@ -50,7 +50,7 @@ void deactivate(int shortcut); void begin(int shortcut, QEvent *event = 0); - void mouseMoved(const QPointF &lastPos, const QPointF &pos); + void cursorMoved(const QPointF &lastPos, const QPointF &pos); bool isShortcutRequired(int shortcut) const; diff --git a/krita/ui/input/kis_gamma_exposure_action.cpp b/krita/ui/input/kis_gamma_exposure_action.cpp --- a/krita/ui/input/kis_gamma_exposure_action.cpp +++ b/krita/ui/input/kis_gamma_exposure_action.cpp @@ -171,7 +171,7 @@ } } -void KisGammaExposureAction::mouseMoved(const QPointF &lastPos, const QPointF &pos) +void KisGammaExposureAction::cursorMoved(const QPointF &lastPos, const QPointF &pos) { QPointF diff = -(pos - lastPos); diff --git a/krita/ui/input/kis_input_manager.h b/krita/ui/input/kis_input_manager.h --- a/krita/ui/input/kis_input_manager.h +++ b/krita/ui/input/kis_input_manager.h @@ -97,12 +97,6 @@ KisToolProxy *toolProxy() const; /** - * Returns the event object for the last tablet event - * happened. Returns null if there was no tablet event recently - */ - QTabletEvent *lastTabletEvent() const; - - /** * Touch events are special, too. * * \return a touch event if there was one, otherwise 0 diff --git a/krita/ui/input/kis_input_manager.cpp b/krita/ui/input/kis_input_manager.cpp --- a/krita/ui/input/kis_input_manager.cpp +++ b/krita/ui/input/kis_input_manager.cpp @@ -1,4 +1,7 @@ -/* This file is part of the KDE project * Copyright (C) 2012 Arjen Hiemstra +/* This file is part of the KDE project + * + * Copyright (C) 2012 Arjen Hiemstra + * Copyright (C) 2015 Michael Abrahams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,8 +22,6 @@ #include #include -#include - #include #include @@ -36,9 +37,7 @@ #include #include "kis_abstract_input_action.h" -#include "kis_tool_invocation_action.h" #include "kis_pan_action.h" -#include "kis_alternate_invocation_action.h" #include "kis_rotate_canvas_action.h" #include "kis_zoom_action.h" #include "kis_show_palette_action.h" @@ -54,473 +53,40 @@ #include "kis_shortcut_configuration.h" #include -#include #include #include "kis_extended_modifiers_mapper.h" -template -uint qHash(QPointer value) { - return reinterpret_cast(value.data()); -} - -class Q_DECL_HIDDEN KisInputManager::Private -{ -public: - Private(KisInputManager *qq) - : q(qq) - , toolProxy(0) - , forwardAllEventsToTool(false) - , ignoreQtCursorEvents(false) - , disableTouchOnCanvas(false) - , touchHasBlockedPressEvents(false) - #ifdef HAVE_X11 - , hiResEventsWorkaroundCoeff(1.0, 1.0) - #endif - , lastTabletEvent(0) - , lastTouchEvent(0) - , defaultInputAction(0) - , eventsReceiver(0) - , moveEventCompressor(10 /* ms */, KisSignalCompressor::FIRST_ACTIVE) - , testingAcceptCompressedTabletEvents(false) - , testingCompressBrushEvents(false) - , focusOnEnter(true) - , containsPointer(true) - { - KisConfig cfg; - disableTouchOnCanvas = cfg.disableTouchOnCanvas(); - - moveEventCompressor.setDelay(cfg.tabletEventsDelay()); - testingAcceptCompressedTabletEvents = cfg.testingAcceptCompressedTabletEvents(); - testingCompressBrushEvents = cfg.testingCompressBrushEvents(); - } - - bool tryHidePopupPalette(); - void saveTabletEvent(const QTabletEvent *event); - void resetSavedTabletEvent(QEvent::Type type); - void addStrokeShortcut(KisAbstractInputAction* action, int index, const QList< Qt::Key >& modifiers, Qt::MouseButtons buttons); - void addKeyShortcut(KisAbstractInputAction* action, int index,const QList &keys); - void addTouchShortcut( KisAbstractInputAction* action, int index, KisShortcutConfiguration::GestureAction gesture ); - void addWheelShortcut(KisAbstractInputAction* action, int index, const QList< Qt::Key >& modifiers, KisShortcutConfiguration::MouseWheelMovement wheelAction); - bool processUnhandledEvent(QEvent *event); - Qt::Key workaroundShiftAltMetaHell(const QKeyEvent *keyEvent); - void setupActions(); - void saveTouchEvent( QTouchEvent* event ); - bool handleKisTabletEvent(QObject *object, KisTabletEvent *tevent); - - KisInputManager *q; - - KisCanvas2 *canvas; - KisToolProxy *toolProxy; - - bool forwardAllEventsToTool; - bool ignoreQtCursorEvents; - - bool disableTouchOnCanvas; - bool touchHasBlockedPressEvents; - - KisShortcutMatcher matcher; -#ifdef HAVE_X11 - QPointF hiResEventsWorkaroundCoeff; -#endif - QTabletEvent *lastTabletEvent; - QTouchEvent *lastTouchEvent; - - KisToolInvocationAction *defaultInputAction; - - QObject *eventsReceiver; - KisSignalCompressor moveEventCompressor; - QScopedPointer compressedMoveEvent; - bool testingAcceptCompressedTabletEvents; - bool testingCompressBrushEvents; - - - QSet > priorityEventFilter; - - template - void debugEvent(QEvent *event); +#include "kis_input_manager_p.h" - class ProximityNotifier; +template uint qHash(QPointer value) { return reinterpret_cast(value.data()); } - class CanvasSwitcher; - QPointer canvasSwitcher; +// #define touch_start_block_press_events() d->touchHasBlockedPressEvents = d->disableTouchOnCanvas +// #define touch_stop_block_press_events() d->touchHasBlockedPressEvents = false +// #define break_if_touch_blocked_press_events() if (d->touchHasBlockedPressEvents) break; - bool focusOnEnter; - bool containsPointer; -}; -template -void KisInputManager::Private::debugEvent(QEvent *event) +template void debugEvent(QEvent *event) { if (!KisTabletDebugger::instance()->debugEnabled()) return; - QString msg1 = useBlocking && ignoreQtCursorEvents ? "[BLOCKED] " : "[ ]"; Event *specificEvent = static_cast(event); - qDebug() << KisTabletDebugger::instance()->eventToString(*specificEvent, msg1); -} - -#define start_ignore_cursor_events() d->ignoreQtCursorEvents = true -#define stop_ignore_cursor_events() d->ignoreQtCursorEvents = false -#define break_if_should_ignore_cursor_events() if (d->ignoreQtCursorEvents) break; - -#define push_and_stop_ignore_cursor_events() bool __saved_ignore_events = d->ignoreQtCursorEvents; d->ignoreQtCursorEvents = false -#define pop_ignore_cursor_events() d->ignoreQtCursorEvents = __saved_ignore_events - -#define touch_start_block_press_events() d->touchHasBlockedPressEvents = d->disableTouchOnCanvas -#define touch_stop_block_press_events() d->touchHasBlockedPressEvents = false -#define break_if_touch_blocked_press_events() if (d->touchHasBlockedPressEvents) break; - -class KisInputManager::Private::CanvasSwitcher : public QObject { -public: - CanvasSwitcher(Private *_d, QObject *p) - : QObject(p), - d(_d), - eatOneMouseStroke(false) - { - } - - void addCanvas(KisCanvas2 *canvas) { - QObject *widget = canvas->canvasWidget(); - - if (!canvasResolver.contains(widget)) { - canvasResolver.insert(widget, canvas); - d->q->setupAsEventFilter(widget); - widget->installEventFilter(this); - - d->canvas = canvas; - d->toolProxy = dynamic_cast(canvas->toolProxy()); - } else { - KIS_ASSERT_RECOVER_RETURN(d->canvas == canvas); - } - } - - void removeCanvas(KisCanvas2 *canvas) { - QObject *widget = canvas->canvasWidget(); - - canvasResolver.remove(widget); - - if (d->eventsReceiver == widget) { - d->q->setupAsEventFilter(0); - } - - widget->removeEventFilter(this); - } - - bool eventFilter(QObject* object, QEvent* event ) { - if (canvasResolver.contains(object)) { - switch (event->type()) { - case QEvent::FocusIn: { - QFocusEvent *fevent = static_cast(event); - eatOneMouseStroke = 2 * (fevent->reason() == Qt::MouseFocusReason); - - KisCanvas2 *canvas = canvasResolver.value(object); - d->canvas = canvas; - d->toolProxy = dynamic_cast(canvas->toolProxy()); - - d->q->setupAsEventFilter(object); - - object->removeEventFilter(this); - object->installEventFilter(this); - - QEvent event(QEvent::Enter); - d->q->eventFilter(object, &event); - break; - } - - case QEvent::Wheel: { - QWidget *widget = static_cast(object); - widget->setFocus(); - break; - } - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::TabletPress: - case QEvent::TabletRelease: - if (eatOneMouseStroke) { - eatOneMouseStroke--; - return true; - } - break; - case QEvent::MouseButtonDblClick: - if (eatOneMouseStroke) { - return true; - } - break; - default: - break; - } - } - return QObject::eventFilter(object, event); - } - -private: - KisInputManager::Private *d; - QMap canvasResolver; - int eatOneMouseStroke; -}; - -class KisInputManager::Private::ProximityNotifier : public QObject { -public: - ProximityNotifier(Private *_d, QObject *p) : QObject(p), d(_d) {} - - bool eventFilter(QObject* object, QEvent* event ) { - switch (event->type()) { - case QEvent::TabletEnterProximity: - d->debugEvent(event); - start_ignore_cursor_events(); - break; - case QEvent::TabletLeaveProximity: - d->debugEvent(event); - stop_ignore_cursor_events(); - break; - default: - break; - } - return QObject::eventFilter(object, event); - } - -private: - KisInputManager::Private *d; -}; - -void KisInputManager::Private::addStrokeShortcut(KisAbstractInputAction* action, int index, - const QList &modifiers, - Qt::MouseButtons buttons) -{ - KisStrokeShortcut *strokeShortcut = - new KisStrokeShortcut(action, index); - - QList buttonList; - if(buttons & Qt::LeftButton) { - buttonList << Qt::LeftButton; - } - if(buttons & Qt::RightButton) { - buttonList << Qt::RightButton; - } - if(buttons & Qt::MidButton) { - buttonList << Qt::MidButton; - } - if(buttons & Qt::XButton1) { - buttonList << Qt::XButton1; - } - if(buttons & Qt::XButton2) { - buttonList << Qt::XButton2; - } - - if (buttonList.size() > 0) { - strokeShortcut->setButtons(modifiers, buttonList); - matcher.addShortcut(strokeShortcut); - } -} - -void KisInputManager::Private::addKeyShortcut(KisAbstractInputAction* action, int index, - const QList &keys) -{ - if (keys.size() == 0) return; - - KisSingleActionShortcut *keyShortcut = - new KisSingleActionShortcut(action, index); - - //Note: Ordering is important here, Shift + V is different from V + Shift, - //which is the reason we use the last key here since most users will enter - //shortcuts as "Shift + V". Ideally this should not happen, but this is - //the way the shortcut matcher is currently implemented. - QList modifiers = keys; - Qt::Key key = modifiers.takeLast(); - keyShortcut->setKey(modifiers, key); - matcher.addShortcut(keyShortcut); -} - -void KisInputManager::Private::addWheelShortcut(KisAbstractInputAction* action, int index, - const QList &modifiers, - KisShortcutConfiguration::MouseWheelMovement wheelAction) -{ - KisSingleActionShortcut *keyShortcut = - new KisSingleActionShortcut(action, index); - - KisSingleActionShortcut::WheelAction a; - switch(wheelAction) { - case KisShortcutConfiguration::WheelUp: - a = KisSingleActionShortcut::WheelUp; - break; - case KisShortcutConfiguration::WheelDown: - a = KisSingleActionShortcut::WheelDown; - break; - case KisShortcutConfiguration::WheelLeft: - a = KisSingleActionShortcut::WheelLeft; - break; - case KisShortcutConfiguration::WheelRight: - a = KisSingleActionShortcut::WheelRight; - break; - default: - return; - } - - keyShortcut->setWheel(modifiers, a); - matcher.addShortcut(keyShortcut); -} - -void KisInputManager::Private::addTouchShortcut( KisAbstractInputAction* action, int index, KisShortcutConfiguration::GestureAction gesture) -{ - KisTouchShortcut *shortcut = new KisTouchShortcut(action, index); - switch(gesture) { - case KisShortcutConfiguration::PinchGesture: - shortcut->setMinimumTouchPoints(2); - shortcut->setMaximumTouchPoints(2); - break; - case KisShortcutConfiguration::PanGesture: - shortcut->setMinimumTouchPoints(3); - shortcut->setMaximumTouchPoints(10); - break; - default: - break; - } - matcher.addShortcut(shortcut); -} - -void KisInputManager::Private::setupActions() -{ - QList actions = KisInputProfileManager::instance()->actions(); - foreach(KisAbstractInputAction *action, actions) { - KisToolInvocationAction *toolAction = - dynamic_cast(action); - - if(toolAction) { - defaultInputAction = toolAction; - } - } - - connect(KisInputProfileManager::instance(), SIGNAL(currentProfileChanged()), q, SLOT(profileChanged())); - if(KisInputProfileManager::instance()->currentProfile()) { - q->profileChanged(); - } -} - -bool KisInputManager::Private::processUnhandledEvent(QEvent *event) -{ - bool retval = false; - - if (forwardAllEventsToTool || - event->type() == QEvent::KeyPress || - event->type() == QEvent::KeyRelease) { - - defaultInputAction->processUnhandledEvent(event); - retval = true; - } - - return retval && !forwardAllEventsToTool; + qDebug() << KisTabletDebugger::instance()->eventToString(*specificEvent, + QString::fromLatin1("")); } -Qt::Key KisInputManager::Private::workaroundShiftAltMetaHell(const QKeyEvent *keyEvent) -{ - Qt::Key key = (Qt::Key)keyEvent->key(); - - if (keyEvent->key() == Qt::Key_Meta && - keyEvent->modifiers().testFlag(Qt::ShiftModifier)) { - - key = Qt::Key_Alt; - } - return key; -} - -bool KisInputManager::Private::tryHidePopupPalette() -{ - if (canvas->isPopupPaletteVisible()) { - canvas->slotShowPopupPalette(); - return true; - } - return false; -} - -#ifdef HAVE_X11 -inline QPointF dividePoints(const QPointF &pt1, const QPointF &pt2) { - return QPointF(pt1.x() / pt2.x(), pt1.y() / pt2.y()); -} - -inline QPointF multiplyPoints(const QPointF &pt1, const QPointF &pt2) { - return QPointF(pt1.x() * pt2.x(), pt1.y() * pt2.y()); -} -#endif - -void KisInputManager::Private::saveTabletEvent(const QTabletEvent *event) -{ - delete lastTabletEvent; - -#ifdef HAVE_X11 - /** - * There is a bug in Qt-x11 when working in 2 tablets + 2 monitors - * setup. The hiResGlobalPos() value gets scaled wrongly somehow. - * Happily, the error is linear (without the offset) so we can simply - * scale it a bit. - */ - if (event->type() == QEvent::TabletPress) { - if ((event->globalPos() - event->hiResGlobalPos()).manhattanLength() > 4) { - hiResEventsWorkaroundCoeff = dividePoints(event->globalPos(), event->hiResGlobalPos()); - } else { - hiResEventsWorkaroundCoeff = QPointF(1.0, 1.0); - } - } -#endif - - lastTabletEvent = - new QTabletEvent(event->type(), - event->pos(), - event->globalPos(), -// QT5TODO: Qt5's QTabletEvent no longer has a hiResGlobalPos, and we don't know whether this bug still happens in Qt5. -// #ifdef HAVE_X11 -// multiplyPoints(event->hiResGlobalPos(), hiResEventsWorkaroundCoeff), -// #else -// event->hiResGlobalPos(), -// #endif - event->device(), - event->pointerType(), - event->pressure(), - event->xTilt(), - event->yTilt(), - event->tangentialPressure(), - event->rotation(), - event->z(), - event->modifiers(), - event->uniqueId()); -} - -void KisInputManager::Private::saveTouchEvent( QTouchEvent* event ) -{ - delete lastTouchEvent; - lastTouchEvent = new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints()); -} - -void KisInputManager::Private::resetSavedTabletEvent(QEvent::Type /*type*/) -{ - /** - * On both Windows and Linux each mouse event corresponds to a - * single tablet event, so the saved event must be reset after - * every mouse-related event - */ - - delete lastTabletEvent; - lastTabletEvent = 0; -} KisInputManager::KisInputManager(QObject *parent) - : QObject(parent), d(new Private(this)) + : QObject(parent) + , d(new Private(this)) { - d->canvas = 0; - d->toolProxy = 0; - - d->setupActions(); - connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*,int)), SLOT(slotToolChanged())); connect(&d->moveEventCompressor, SIGNAL(timeout()), SLOT(slotCompressedMoveEvent())); - #ifndef Q_OS_MAC - QApplication::instance()-> - installEventFilter(new Private::ProximityNotifier(d, this)); + QApplication::instance()->installEventFilter(new Private::ProximityNotifier(d, this)); #endif - - d->canvasSwitcher = new Private::CanvasSwitcher(d, this); } KisInputManager::~KisInputManager() @@ -541,17 +107,6 @@ void KisInputManager::toggleTabletLogger() { KisTabletDebugger::instance()->toggleDebugging(); - - bool enabled = KisTabletDebugger::instance()->debugEnabled(); - QMessageBox::information(0, i18nc("@title:window", "Krita"), enabled ? - i18n("Tablet Event Logging Enabled") : - i18n("Tablet Event Logging Disabled")); - if (enabled) { - qDebug() << "vvvvvvvvvvvvvvvvvvvvvvv START TABLET EVENT LOG vvvvvvvvvvvvvvvvvvvvvvv"; - } - else { - qDebug() << "^^^^^^^^^^^^^^^^^^^^^^^ START TABLET EVENT LOG ^^^^^^^^^^^^^^^^^^^^^^^"; - } } void KisInputManager::attachPriorityEventFilter(QObject *filter) @@ -579,7 +134,7 @@ void KisInputManager::stopIgnoringEvents() { - stop_ignore_cursor_events(); + d->allowMouseEvents(); } void KisInputManager::slotFocusOnEnter(bool value) @@ -606,15 +161,7 @@ bool retval = false; if (object != d->eventsReceiver) return retval; - if (!d->ignoreQtCursorEvents || - (event->type() != QEvent::MouseButtonPress && - event->type() != QEvent::MouseButtonDblClick && - event->type() != QEvent::MouseButtonRelease && - event->type() != QEvent::MouseMove && - event->type() != QEvent::TabletPress && - event->type() != QEvent::TabletMove && - event->type() != QEvent::TabletRelease)) { - + if (true) { foreach (QPointer filter, d->priorityEventFilter) { if (filter.isNull()) { d->priorityEventFilter.remove(filter); @@ -631,13 +178,11 @@ // tool is in text editing, preventing shortcut triggering d->toolProxy->processEvent(event); - // because we have fake enums in here... switch (event->type()) { case QEvent::MouseButtonPress: case QEvent::MouseButtonDblClick: { - d->debugEvent(event); - break_if_should_ignore_cursor_events(); - break_if_touch_blocked_press_events(); + debugEvent(event); + // break_if_touch_blocked_press_events(); QMouseEvent *mouseEvent = static_cast(event); @@ -648,23 +193,33 @@ KisAbstractInputAction::setInputManager(this); retval = d->matcher.buttonPressed(mouseEvent->button(), mouseEvent); } - d->resetSavedTabletEvent(event->type()); event->setAccepted(retval); break; } + + case QEvent::MouseMove: { + debugEvent(event); + + if (!d->matcher.pointerMoved(event)) { + //Update the current tool so things like the brush outline gets updated. + d->toolProxy->forwardHoverEvent(event); + } + event->setAccepted(true); + break; + } + case QEvent::MouseButtonRelease: { - d->debugEvent(event); - break_if_should_ignore_cursor_events(); - break_if_touch_blocked_press_events(); + debugEvent(event); + // break_if_touch_blocked_press_events(); QMouseEvent *mouseEvent = static_cast(event); retval = d->matcher.buttonReleased(mouseEvent->button(), mouseEvent); - d->resetSavedTabletEvent(event->type()); event->setAccepted(retval); break; } + case QEvent::ShortcutOverride: { - d->debugEvent(event); + debugEvent(event); QKeyEvent *keyEvent = static_cast(event); Qt::Key key = d->workaroundShiftAltMetaHell(keyEvent); @@ -686,32 +241,20 @@ break; } + case QEvent::KeyRelease: { - d->debugEvent(event); + debugEvent(event); QKeyEvent *keyEvent = static_cast(event); if (!keyEvent->isAutoRepeat()) { Qt::Key key = d->workaroundShiftAltMetaHell(keyEvent); retval = d->matcher.keyReleased(key); } break; } - case QEvent::MouseMove: { - d->debugEvent(event); - break_if_should_ignore_cursor_events(); - QMouseEvent *mouseEvent = static_cast(event); - if (!d->matcher.mouseMoved(mouseEvent)) { - //Update the current tool so things like the brush outline gets updated. - d->toolProxy->forwardMouseHoverEvent(mouseEvent, lastTabletEvent()); - } - retval = true; - event->setAccepted(retval); - d->resetSavedTabletEvent(event->type()); - break; - } case QEvent::Wheel: { - d->debugEvent(event); + debugEvent(event); QWheelEvent *wheelEvent = static_cast(event); KisSingleActionShortcut::WheelAction action; @@ -737,35 +280,40 @@ retval = d->matcher.wheelEvent(action, wheelEvent); break; } - case QEvent::Enter: - d->debugEvent(event); + + case QEvent::Enter: { + debugEvent(event); d->containsPointer = true; //Make sure the input actions know we are active. KisAbstractInputAction::setInputManager(this); //Ensure we have focus so we get key events. if (d->focusOnEnter) { d->canvas->canvasWidget()->setFocus(); } - stop_ignore_cursor_events(); - touch_stop_block_press_events(); + d->allowMouseEvents(); + // touch_stop_block_press_events(); d->matcher.enterEvent(); break; - case QEvent::Leave: - d->debugEvent(event); + } + + case QEvent::Leave: { + debugEvent(event); d->containsPointer = false; /** * We won't get a TabletProximityLeave event when the tablet * is hovering above some other widget, so restore cursor * events processing right now. */ - stop_ignore_cursor_events(); - touch_stop_block_press_events(); + d->allowMouseEvents(); + // touch_stop_block_press_events(); d->matcher.leaveEvent(); break; - case QEvent::FocusIn: - d->debugEvent(event); + } + + case QEvent::FocusIn: { + debugEvent(event); KisAbstractInputAction::setInputManager(this); //Clear all state so we don't have half-matched shortcuts dangling around. @@ -780,142 +328,91 @@ eventFilter(object, &kevent); } } - - stop_ignore_cursor_events(); - break; - case QEvent::TabletPress: - case QEvent::TabletMove: - case QEvent::TabletRelease: { - d->debugEvent(event); - break_if_should_ignore_cursor_events(); - break_if_touch_blocked_press_events(); - - //We want both the tablet information and the mouse button state. - //Since QTabletEvent only provides the tablet information, we - //save that and then ignore the event so it will generate a mouse - //event. - QTabletEvent* tabletEvent = static_cast(event); - d->saveTabletEvent(tabletEvent); - event->ignore(); - + d->allowMouseEvents(); break; } - case KisTabletEvent::TabletPressEx: - case KisTabletEvent::TabletMoveEx: - case KisTabletEvent::TabletReleaseEx: { - d->debugEvent(event); - stop_ignore_cursor_events(); - touch_stop_block_press_events(); - - KisTabletEvent *tevent = static_cast(event); - - if (tevent->type() == (QEvent::Type)KisTabletEvent::TabletMoveEx && - (!d->matcher.supportsHiResInputEvents() || - d->testingCompressBrushEvents)) { - - d->compressedMoveEvent.reset(new KisTabletEvent(*tevent)); - d->moveEventCompressor.start(); - - /** - * 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! - */ - if (d->testingAcceptCompressedTabletEvents) { - tevent->setAccepted(true); - } + case QEvent::TabletPress: { + debugEvent(event); + QTabletEvent *tabletEvent = static_cast(event); + if (d->tryHidePopupPalette()) { retval = true; } else { - slotCompressedMoveEvent(); - retval = d->handleKisTabletEvent(object, tevent); + //Make sure the input actions know we are active. + KisAbstractInputAction::setInputManager(this); + retval = d->matcher.buttonPressed(tabletEvent->button(), tabletEvent); } + event->setAccepted(true); + retval = true; + d->blockMouseEvents(); + break; + } + + case QEvent::TabletMove: { + debugEvent(event); + QTabletEvent *tabletEvent = static_cast(event); + + if (!d->matcher.pointerMoved(tabletEvent)) { + d->toolProxy->forwardHoverEvent(tabletEvent); + } + retval = true; + event->setAccepted(true); /** * 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) */ - start_ignore_cursor_events(); - - break; - } - case KisTabletEvent::TouchProximityInEx: { - touch_start_block_press_events(); + d->blockMouseEvents(); break; } - case KisTabletEvent::TouchProximityOutEx: { - touch_stop_block_press_events(); + + case QEvent::TabletRelease: { + debugEvent(event); + + QTabletEvent *tabletEvent = static_cast(event); + retval = d->matcher.buttonReleased(tabletEvent->button(), tabletEvent); + retval = true; + event->setAccepted(true); + d->allowMouseEvents(); break; } case QEvent::TouchBegin: - touch_start_block_press_events(); + // touch_start_block_press_events(); KisAbstractInputAction::setInputManager(this); retval = d->matcher.touchBeginEvent(static_cast(event)); event->accept(); - d->resetSavedTabletEvent(event->type()); break; case QEvent::TouchUpdate: - touch_start_block_press_events(); + // touch_start_block_press_events(); KisAbstractInputAction::setInputManager(this); retval = d->matcher.touchUpdateEvent(static_cast(event)); event->accept(); - d->resetSavedTabletEvent(event->type()); break; case QEvent::TouchEnd: - touch_stop_block_press_events(); + // touch_stop_block_press_events(); d->saveTouchEvent(static_cast(event)); retval = d->matcher.touchEndEvent(static_cast(event)); event->accept(); - d->resetSavedTabletEvent(event->type()); delete d->lastTouchEvent; d->lastTouchEvent = 0; break; default: break; } - return !retval ? d->processUnhandledEvent(event) : true; -} - -bool KisInputManager::Private::handleKisTabletEvent(QObject *object, KisTabletEvent *tevent) -{ - if(object == 0) return false; - - bool retval = false; - - QTabletEvent qte = tevent->toQTabletEvent(); - qte.ignore(); - retval = q->eventFilter(object, &qte); - tevent->setAccepted(qte.isAccepted()); - - if (!retval && !qte.isAccepted()) { - QMouseEvent qme = tevent->toQMouseEvent(); - qme.ignore(); - retval = q->eventFilter(object, &qme); - tevent->setAccepted(qme.isAccepted()); - } - return retval; } void KisInputManager::slotCompressedMoveEvent() { if (d->compressedMoveEvent) { - - push_and_stop_ignore_cursor_events(); - touch_stop_block_press_events(); - - (void) d->handleKisTabletEvent(d->eventsReceiver, d->compressedMoveEvent.data()); - - pop_ignore_cursor_events(); - + eventFilter(d->eventsReceiver, d->compressedMoveEvent.data()); d->compressedMoveEvent.reset(); } } @@ -930,11 +427,6 @@ return d->toolProxy; } -QTabletEvent* KisInputManager::lastTabletEvent() const -{ - return d->lastTabletEvent; -} - QTouchEvent *KisInputManager::lastTouchEvent() const { return d->lastTouchEvent; diff --git a/krita/ui/input/kis_input_manager_p.h b/krita/ui/input/kis_input_manager_p.h new file mode 100644 --- /dev/null +++ b/krita/ui/input/kis_input_manager_p.h @@ -0,0 +1,119 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2015 Michael Abrahams + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include "kis_input_manager.h" +#include "kis_shortcut_matcher.h" +#include "kis_tool_invocation_action.h" +#include "kis_alternate_invocation_action.h" +#include "kis_shortcut_configuration.h" +#include "kis_canvas2.h" +#include "kis_tool_proxy.h" +#include "kis_signal_compressor.h" + + + + +#include "kis_abstract_input_action.h" + + +class Q_DECL_HIDDEN KisInputManager::Private +{ + +public: + Private(KisInputManager *qq); + ~Private(); + + /// Shortcuts + void addStrokeShortcut(KisAbstractInputAction* action, int index, const QList< Qt::Key >& modifiers, Qt::MouseButtons buttons); + void addKeyShortcut (KisAbstractInputAction* action, int index,const QList &keys); + void addTouchShortcut( KisAbstractInputAction* action, int index, + KisShortcutConfiguration::GestureAction gesture ); + void addWheelShortcut( KisAbstractInputAction* action, int index, const QList< Qt::Key >& modifiers, + KisShortcutConfiguration::MouseWheelMovement wheelAction); + + /// Event modification + bool processUnhandledEvent(QEvent *event); + Qt::Key workaroundShiftAltMetaHell(const QKeyEvent *keyEvent); + void setupActions(); + + /// Mouse event masking + void blockMouseEvents(); + void allowMouseEvents(); + void saveTouchEvent( QTouchEvent* event ); + + /// Other + bool tryHidePopupPalette(); + + + class ProximityNotifier : public QObject + { + public: + ProximityNotifier(Private *_d, QObject *p); + bool eventFilter(QObject* object, QEvent* event ); + private: + KisInputManager::Private *d; + }; + + class CanvasSwitcher : public QObject + { + public: + CanvasSwitcher(Private *_d, QObject *p); + void addCanvas(KisCanvas2 *canvas); + void removeCanvas(KisCanvas2 *canvas); + bool eventFilter(QObject* object, QEvent* event ); + + private: + KisInputManager::Private *d; + QMap canvasResolver; + int eatOneMouseStroke; + }; + + /// Initializer list variables + KisInputManager *q; + KisCanvas2 *canvas; + KisToolProxy *toolProxy; + + bool forwardAllEventsToTool; + bool disableTouchOnCanvas; + bool touchHasBlockedPressEvents; + QTouchEvent *lastTouchEvent; + KisToolInvocationAction *defaultInputAction; + QObject *eventsReceiver; + KisSignalCompressor moveEventCompressor; + bool testingAcceptCompressedTabletEvents; + bool testingCompressBrushEvents; + bool focusOnEnter; + bool containsPointer; + + /// Other members + QPointer canvasSwitcher; + QScopedPointer compressedMoveEvent; + + + + QSet > priorityEventFilter; + KisShortcutMatcher matcher; +}; diff --git a/krita/ui/input/kis_input_manager_p.cpp b/krita/ui/input/kis_input_manager_p.cpp new file mode 100644 --- /dev/null +++ b/krita/ui/input/kis_input_manager_p.cpp @@ -0,0 +1,404 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2015 Michael Abrahams + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include "kis_input_manager_p.h" + +#include +#include +#include + +#include "kis_config.h" +#include "kis_abstract_input_action.h" +#include "kis_stroke_shortcut.h" +#include "kis_touch_shortcut.h" +#include "kis_input_profile_manager.h" +#include "kis_input_profile_manager.h" +#include "input/kis_tablet_debugger.h" + + +template void debugEvent(QEvent *event) +{ + if (!KisTabletDebugger::instance()->debugEnabled()) return; + Event *specificEvent = static_cast(event); + qDebug() << KisTabletDebugger::instance()->eventToString(*specificEvent, + QString::fromLatin1("")); +} + + + + +class MouseEventEater : public QObject +{ +public: + MouseEventEater(QObject *p) : QObject(p), hungry(false) {} + + bool eventFilter(QObject* object, QEvent* event ) + { + if (hungry && (event->type() == QEvent::MouseMove || + event->type() == QEvent::MouseButtonPress || + event->type() == QEvent::MouseButtonRelease)) { + return true; + } + return false; + } + + void activate() + { + if (!hungry) { + qDebug() << "Ignoring mouse events."; + } + hungry = true; + } + + void deactivate() + { + if (hungry) { + qDebug() << "Accepting mouse events."; + } + hungry = false; + } + + +private: + bool hungry; +}; + + +MouseEventEater *globalEventEater; + + +KisInputManager::Private::Private(KisInputManager *qq) + : q(qq) + , canvas(0) + , toolProxy(0) + , forwardAllEventsToTool(false) + , disableTouchOnCanvas(false) + , touchHasBlockedPressEvents(false) + , lastTouchEvent(0) + , defaultInputAction(0) + , eventsReceiver(0) + , moveEventCompressor(10 /* ms */, KisSignalCompressor::FIRST_ACTIVE) + , testingAcceptCompressedTabletEvents(false) + , testingCompressBrushEvents(false) + , focusOnEnter(true) + , containsPointer(true) +{ + KisConfig cfg; + disableTouchOnCanvas = cfg.disableTouchOnCanvas(); + moveEventCompressor.setDelay(cfg.tabletEventsDelay()); + testingAcceptCompressedTabletEvents = cfg.testingAcceptCompressedTabletEvents(); + testingCompressBrushEvents = cfg.testingCompressBrushEvents(); + setupActions(); + canvasSwitcher = new CanvasSwitcher(this, q); + globalEventEater = new MouseEventEater(qApp); + qApp->installEventFilter(globalEventEater); +} + + + + + +KisInputManager::Private::~Private() +{ + delete canvasSwitcher; + delete globalEventEater; +} + + +KisInputManager::Private::CanvasSwitcher::CanvasSwitcher(Private *_d, QObject *p) + : QObject(p), + d(_d), + eatOneMouseStroke(false) +{ +} + +void KisInputManager::Private::CanvasSwitcher::addCanvas(KisCanvas2 *canvas) { + QObject *widget = canvas->canvasWidget(); + + if (!canvasResolver.contains(widget)) { + canvasResolver.insert(widget, canvas); + d->q->setupAsEventFilter(widget); + widget->installEventFilter(this); + + d->canvas = canvas; + d->toolProxy = dynamic_cast(canvas->toolProxy()); + } else { + KIS_ASSERT_RECOVER_RETURN(d->canvas == canvas); + } +} + +void KisInputManager::Private::CanvasSwitcher::removeCanvas(KisCanvas2 *canvas) { + QObject *widget = canvas->canvasWidget(); + + canvasResolver.remove(widget); + + if (d->eventsReceiver == widget) { + d->q->setupAsEventFilter(0); + } + + widget->removeEventFilter(this); +} + +bool KisInputManager::Private::CanvasSwitcher::eventFilter(QObject* object, QEvent* event ) { + if (canvasResolver.contains(object)) { + switch (event->type()) { + case QEvent::FocusIn: { + QFocusEvent *fevent = static_cast(event); + eatOneMouseStroke = 2 * (fevent->reason() == Qt::MouseFocusReason); + + KisCanvas2 *canvas = canvasResolver.value(object); + d->canvas = canvas; + d->toolProxy = dynamic_cast(canvas->toolProxy()); + + d->q->setupAsEventFilter(object); + + object->removeEventFilter(this); + object->installEventFilter(this); + + QEvent event(QEvent::Enter); + d->q->eventFilter(object, &event); + break; + } + + case QEvent::Wheel: { + QWidget *widget = static_cast(object); + widget->setFocus(); + break; + } + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::TabletPress: + case QEvent::TabletRelease: + if (eatOneMouseStroke) { + eatOneMouseStroke--; + return true; + } + break; + case QEvent::MouseButtonDblClick: + if (eatOneMouseStroke) { + return true; + } + break; + default: + break; + } + } + return QObject::eventFilter(object, event); +} + + +KisInputManager::Private::ProximityNotifier::ProximityNotifier(Private *_d, QObject *p) + : QObject(p), d(_d) +{}; + + +bool KisInputManager::Private::ProximityNotifier::eventFilter(QObject* object, QEvent* event ) +{ + switch (event->type()) { + case QEvent::TabletEnterProximity: + // d->blockMouseEvents(); Qt sends fake mouse events instead of hover events, so disable this for now. + debugEvent(event); + break; + case QEvent::TabletLeaveProximity: + d->allowMouseEvents(); + debugEvent(event); + break; + default: + break; + } + return QObject::eventFilter(object, event); +} + +void KisInputManager::Private::addStrokeShortcut(KisAbstractInputAction* action, int index, + const QList &modifiers, + Qt::MouseButtons buttons) +{ + KisStrokeShortcut *strokeShortcut = + new KisStrokeShortcut(action, index); + + QList buttonList; + if(buttons & Qt::LeftButton) { + buttonList << Qt::LeftButton; + } + if(buttons & Qt::RightButton) { + buttonList << Qt::RightButton; + } + if(buttons & Qt::MidButton) { + buttonList << Qt::MidButton; + } + if(buttons & Qt::XButton1) { + buttonList << Qt::XButton1; + } + if(buttons & Qt::XButton2) { + buttonList << Qt::XButton2; + } + + if (buttonList.size() > 0) { + strokeShortcut->setButtons(modifiers, buttonList); + matcher.addShortcut(strokeShortcut); + } +} + +void KisInputManager::Private::addKeyShortcut(KisAbstractInputAction* action, int index, + const QList &keys) +{ + if (keys.size() == 0) return; + + KisSingleActionShortcut *keyShortcut = + new KisSingleActionShortcut(action, index); + + //Note: Ordering is important here, Shift + V is different from V + Shift, + //which is the reason we use the last key here since most users will enter + //shortcuts as "Shift + V". Ideally this should not happen, but this is + //the way the shortcut matcher is currently implemented. + QList modifiers = keys; + Qt::Key key = modifiers.takeLast(); + keyShortcut->setKey(modifiers, key); + matcher.addShortcut(keyShortcut); +} + +void KisInputManager::Private::addWheelShortcut(KisAbstractInputAction* action, int index, + const QList &modifiers, + KisShortcutConfiguration::MouseWheelMovement wheelAction) +{ + KisSingleActionShortcut *keyShortcut = + new KisSingleActionShortcut(action, index); + + KisSingleActionShortcut::WheelAction a; + switch(wheelAction) { + case KisShortcutConfiguration::WheelUp: + a = KisSingleActionShortcut::WheelUp; + break; + case KisShortcutConfiguration::WheelDown: + a = KisSingleActionShortcut::WheelDown; + break; + case KisShortcutConfiguration::WheelLeft: + a = KisSingleActionShortcut::WheelLeft; + break; + case KisShortcutConfiguration::WheelRight: + a = KisSingleActionShortcut::WheelRight; + break; + default: + return; + } + + keyShortcut->setWheel(modifiers, a); + matcher.addShortcut(keyShortcut); +} + +void KisInputManager::Private::addTouchShortcut( KisAbstractInputAction* action, int index, KisShortcutConfiguration::GestureAction gesture) +{ + KisTouchShortcut *shortcut = new KisTouchShortcut(action, index); + switch(gesture) { + case KisShortcutConfiguration::PinchGesture: + shortcut->setMinimumTouchPoints(2); + shortcut->setMaximumTouchPoints(2); + break; + case KisShortcutConfiguration::PanGesture: + shortcut->setMinimumTouchPoints(3); + shortcut->setMaximumTouchPoints(10); + break; + default: + break; + } + matcher.addShortcut(shortcut); +} + +void KisInputManager::Private::setupActions() +{ + QList actions = KisInputProfileManager::instance()->actions(); + foreach(KisAbstractInputAction *action, actions) { + KisToolInvocationAction *toolAction = + dynamic_cast(action); + + if(toolAction) { + defaultInputAction = toolAction; + } + } + + connect(KisInputProfileManager::instance(), SIGNAL(currentProfileChanged()), q, SLOT(profileChanged())); + if(KisInputProfileManager::instance()->currentProfile()) { + q->profileChanged(); + } +} + +bool KisInputManager::Private::processUnhandledEvent(QEvent *event) +{ + bool retval = false; + + if (forwardAllEventsToTool || + event->type() == QEvent::KeyPress || + event->type() == QEvent::KeyRelease) { + + defaultInputAction->processUnhandledEvent(event); + retval = true; + } + + return retval && !forwardAllEventsToTool; +} + +Qt::Key KisInputManager::Private::workaroundShiftAltMetaHell(const QKeyEvent *keyEvent) +{ + Qt::Key key = (Qt::Key)keyEvent->key(); + + if (keyEvent->key() == Qt::Key_Meta && + keyEvent->modifiers().testFlag(Qt::ShiftModifier)) { + + key = Qt::Key_Alt; + } + + return key; +} + +bool KisInputManager::Private::tryHidePopupPalette() +{ + if (canvas->isPopupPaletteVisible()) { + canvas->slotShowPopupPalette(); + return true; + } + return false; +} + +#ifdef HAVE_X11 +inline QPointF dividePoints(const QPointF &pt1, const QPointF &pt2) { + return QPointF(pt1.x() / pt2.x(), pt1.y() / pt2.y()); +} + +inline QPointF multiplyPoints(const QPointF &pt1, const QPointF &pt2) { + return QPointF(pt1.x() * pt2.x(), pt1.y() * pt2.y()); +} +#endif + + +void KisInputManager::Private::saveTouchEvent( QTouchEvent* event ) +{ + delete lastTouchEvent; + lastTouchEvent = new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints()); +} + +void KisInputManager::Private::blockMouseEvents() +{ + globalEventEater->activate(); +} + +void KisInputManager::Private::allowMouseEvents() +{ + globalEventEater->deactivate(); +} diff --git a/krita/ui/input/kis_pan_action.h b/krita/ui/input/kis_pan_action.h --- a/krita/ui/input/kis_pan_action.h +++ b/krita/ui/input/kis_pan_action.h @@ -51,7 +51,7 @@ void begin(int shortcut, QEvent *event); virtual void inputEvent(QEvent* event); - void mouseMoved(const QPointF &lastPos, const QPointF &pos); + void cursorMoved(const QPointF &lastPos, const QPointF &pos); virtual bool isShortcutRequired(int shortcut) const; diff --git a/krita/ui/input/kis_pan_action.cpp b/krita/ui/input/kis_pan_action.cpp --- a/krita/ui/input/kis_pan_action.cpp +++ b/krita/ui/input/kis_pan_action.cpp @@ -88,8 +88,9 @@ switch (shortcut) { case PanModeShortcut: { QTouchEvent *tevent = dynamic_cast(event); - if(tevent) + if(tevent) { d->lastPosition = d->averagePoint(tevent); + } break; } case PanLeftShortcut: @@ -136,7 +137,7 @@ KisAbstractInputAction::inputEvent(event); } -void KisPanAction::mouseMoved(const QPointF &lastPos, const QPointF &pos) +void KisPanAction::cursorMoved(const QPointF &lastPos, const QPointF &pos) { QPointF relMovement = -(pos - lastPos); inputManager()->canvas()->canvasController()->pan(relMovement.toPoint()); diff --git a/krita/ui/input/kis_rotate_canvas_action.h b/krita/ui/input/kis_rotate_canvas_action.h --- a/krita/ui/input/kis_rotate_canvas_action.h +++ b/krita/ui/input/kis_rotate_canvas_action.h @@ -49,7 +49,7 @@ void activate(int shortcut); void deactivate(int shortcut); void begin(int shortcut, QEvent *event); - void mouseMoved(const QPointF &lastPos, const QPointF &pos); + void cursorMoved(const QPointF &lastPos, const QPointF &pos); private: class Private; diff --git a/krita/ui/input/kis_rotate_canvas_action.cpp b/krita/ui/input/kis_rotate_canvas_action.cpp --- a/krita/ui/input/kis_rotate_canvas_action.cpp +++ b/krita/ui/input/kis_rotate_canvas_action.cpp @@ -106,7 +106,7 @@ } } -void KisRotateCanvasAction::mouseMoved(const QPointF &lastPos, const QPointF &pos) +void KisRotateCanvasAction::cursorMoved(const QPointF &lastPos, const QPointF &pos) { const KisCoordinatesConverter *converter = inputManager()->canvas()->coordinatesConverter(); QPointF centerPoint = converter->flakeToWidget(converter->flakeCenterPoint()); diff --git a/krita/ui/input/kis_select_layer_action.cpp b/krita/ui/input/kis_select_layer_action.cpp --- a/krita/ui/input/kis_select_layer_action.cpp +++ b/krita/ui/input/kis_select_layer_action.cpp @@ -86,11 +86,10 @@ void KisSelectLayerAction::inputEvent(QEvent *event) { - QMouseEvent *mouseEvent; - if (event && (mouseEvent = dynamic_cast(event))) { + if (event) { QPoint pos = inputManager()->canvas()-> - coordinatesConverter()->widgetToImage(mouseEvent->posF()).toPoint(); + coordinatesConverter()->widgetToImage(eventPosF(event)).toPoint(); KisNodeSP node = KisToolUtils::findNode(inputManager()->canvas()->image()->root(), pos, false); diff --git a/krita/ui/input/kis_shortcut_matcher.h b/krita/ui/input/kis_shortcut_matcher.h --- a/krita/ui/input/kis_shortcut_matcher.h +++ b/krita/ui/input/kis_shortcut_matcher.h @@ -24,14 +24,18 @@ #include #include "kis_single_action_shortcut.h" +class QEvent; class QMouseEvent; class QKeyEvent; class QWheelEvent; class QTouchEvent; class KisStrokeShortcut; class KisTouchShortcut; +// Define this symbol to log shortcut matcher output. +#define DEBUG_MATCHER + /** * The class that manages connections between shortcuts and actions. * @@ -125,26 +129,26 @@ bool keyReleased(Qt::Key key); /** - * Handles the mouse button press event + * Handles button presses from a tablet or mouse. * - * \param button the button that has been pressed - * \param event the event that caused this call + * \param event the event that caused this call. + * Must be of type QTabletEvent or QMouseEvent. * * \return whether the event has been handled successfully and * should be eaten by the events filter */ - bool buttonPressed(Qt::MouseButton button, QMouseEvent *event); + bool buttonPressed(Qt::MouseButton button, QEvent *event); /** * Handles the mouse button release event * - * \param button the button that has been pressed - * \param event the event that caused this call + * \param event the event that caused this call. + * Must be of type QTabletEvent or QMouseEvent. * * \return whether the event has been handled successfully and * should be eaten by the events filter */ - bool buttonReleased(Qt::MouseButton button, QMouseEvent *event); + bool buttonReleased(Qt::MouseButton button, QEvent *event); /** * Handles the mouse wheel event @@ -155,14 +159,14 @@ bool wheelEvent(KisSingleActionShortcut::WheelAction wheelAction, QWheelEvent *event); /** - * Handles the mouse move event + * Handles tablet and mouse move events. * * \param event the event that caused this call * * \return whether the event has been handled successfully and * should be eaten by the events filter */ - bool mouseMoved(QMouseEvent *event); + bool pointerMoved(QEvent *event); /** * Handle cursor's Enter event. @@ -214,9 +218,9 @@ void prepareReadyShortcuts(); - bool tryRunReadyShortcut( Qt::MouseButton button, QMouseEvent* event ); + bool tryRunReadyShortcut( Qt::MouseButton button, QEvent* event ); void tryActivateReadyShortcut(); - bool tryEndRunningShortcut( Qt::MouseButton button, QMouseEvent* event ); + bool tryEndRunningShortcut( Qt::MouseButton button, QEvent* event ); bool tryRunTouchShortcut(QTouchEvent *event); bool tryEndTouchShortcut(QTouchEvent *event); diff --git a/krita/ui/input/kis_shortcut_matcher.cpp b/krita/ui/input/kis_shortcut_matcher.cpp --- a/krita/ui/input/kis_shortcut_matcher.cpp +++ b/krita/ui/input/kis_shortcut_matcher.cpp @@ -18,6 +18,7 @@ #include "kis_shortcut_matcher.h" +#include #include #include @@ -29,9 +30,11 @@ #include #define DEBUG_ACTION(action) qDebug() << __FUNCTION__ << ":" << action; #define DEBUG_BUTTON_ACTION(action, button) qDebug() << __FUNCTION__ << ":" << action << "button:" << button << "btns:" << m_d->buttons << "keys:" << m_d->keys; +#define DEBUG_EVENT_ACTION(action, event) if (event) {qDebug() << __FUNCTION__ << ":" << action << "type:" << event->type();} #else #define DEBUG_ACTION(action) #define DEBUG_BUTTON_ACTION(action, button) +#define DEBUG_EVENT_ACTION(action, event) #endif @@ -152,7 +155,7 @@ return false; } -bool KisShortcutMatcher::buttonPressed(Qt::MouseButton button, QMouseEvent *event) +bool KisShortcutMatcher::buttonPressed(Qt::MouseButton button, QEvent *event) { DEBUG_BUTTON_ACTION("entered", button); @@ -179,7 +182,7 @@ return retval; } -bool KisShortcutMatcher::buttonReleased(Qt::MouseButton button, QMouseEvent *event) +bool KisShortcutMatcher::buttonReleased(Qt::MouseButton button, QEvent *event) { DEBUG_BUTTON_ACTION("entered", button); @@ -207,12 +210,15 @@ bool KisShortcutMatcher::wheelEvent(KisSingleActionShortcut::WheelAction wheelAction, QWheelEvent *event) { - if (m_d->runningShortcut || m_d->usingTouch) return false; + if (m_d->runningShortcut || m_d->usingTouch) { + DEBUG_ACTION("Wheel event canceled."); + return false; + } return tryRunWheelShortcut(wheelAction, event); } -bool KisShortcutMatcher::mouseMoved(QMouseEvent *event) +bool KisShortcutMatcher::pointerMoved(QEvent *event) { if (m_d->usingTouch || !m_d->runningShortcut) { return false; @@ -336,24 +342,30 @@ return tryRunSingleActionShortcutImpl(key, event, filteredKeys); } +// Note: sometimes event can be zero!! template bool KisShortcutMatcher::tryRunSingleActionShortcutImpl(T param, U *event, const QList &keysState) { - if (m_d->actionsSuppressed()) return false; + if (m_d->actionsSuppressed()) { + DEBUG_EVENT_ACTION("Event suppressed", event) + return false; + } KisSingleActionShortcut *goodCandidate = 0; foreach(KisSingleActionShortcut *s, m_d->singleActionShortcuts) { if(s->match(keysState, param) && (!goodCandidate || s->priority() > goodCandidate->priority())) { - goodCandidate = s; } } if (goodCandidate) { + DEBUG_EVENT_ACTION("Beginning action for event", event) goodCandidate->action()->begin(goodCandidate->shortcutIndex(), event); goodCandidate->action()->end(0); + } else { + DEBUG_EVENT_ACTION("Could not match a candidate for event", event) } return goodCandidate; @@ -371,7 +383,7 @@ } } -bool KisShortcutMatcher::tryRunReadyShortcut( Qt::MouseButton button, QMouseEvent* event ) +bool KisShortcutMatcher::tryRunReadyShortcut( Qt::MouseButton button, QEvent* event ) { KisStrokeShortcut *goodCandidate = 0; @@ -394,6 +406,7 @@ goodCandidate->action()->activate(goodCandidate->shortcutIndex()); } + goodCandidate->action()->begin(goodCandidate->shortcutIndex(), event); m_d->runningShortcut = goodCandidate; } @@ -427,7 +440,7 @@ } } -bool KisShortcutMatcher::tryEndRunningShortcut( Qt::MouseButton button, QMouseEvent* event ) +bool KisShortcutMatcher::tryEndRunningShortcut( Qt::MouseButton button, QEvent* event ) { Q_ASSERT(m_d->runningShortcut); Q_ASSERT(!m_d->readyShortcut); @@ -454,7 +467,8 @@ return false; foreach(KisTouchShortcut* shortcut, m_d->touchShortcuts) { - if( shortcut->match( event ) && (!goodCandidate || shortcut->priority() > goodCandidate->priority()) ) { + if( shortcut->match( event ) && + (!goodCandidate || (shortcut->priority() > goodCandidate->priority()))) { goodCandidate = shortcut; } } diff --git a/krita/ui/input/kis_tablet_debugger.h b/krita/ui/input/kis_tablet_debugger.h --- a/krita/ui/input/kis_tablet_debugger.h +++ b/krita/ui/input/kis_tablet_debugger.h @@ -43,6 +43,7 @@ static QString tabletDeviceToString(QTabletEvent::TabletDevice device); static QString pointerTypeToString(QTabletEvent::PointerType pointer); + static QString exTypeToString(QEvent::Type type); private: KisTabletDebugger(); diff --git a/krita/ui/input/kis_tablet_debugger.cpp b/krita/ui/input/kis_tablet_debugger.cpp --- a/krita/ui/input/kis_tablet_debugger.cpp +++ b/krita/ui/input/kis_tablet_debugger.cpp @@ -20,20 +20,33 @@ #include #include +#include #include -inline QString button(const QWheelEvent &ev) { - Q_UNUSED(ev); - return "-"; -} -inline QString button(const QTabletEvent &ev) { - Q_UNUSED(ev); - return "-"; +QString KisTabletDebugger::exTypeToString(QEvent::Type type) { + return + type == QEvent::TabletEnterProximity ? "TabletEnterProximity" : + type == QEvent::TabletLeaveProximity ? "TabletLeaveProximity" : + type == QEvent::Enter ? "Enter" : + type == QEvent::Leave ? "Leave" : + type == QEvent::FocusIn ? "FocusIn" : + type == QEvent::Wheel ? "Wheel" : + type == QEvent::KeyPress ? "KeyPress" : + type == QEvent::KeyRelease ? "KeyRelease" : + type == QEvent::ShortcutOverride ? "ShortcutOverride" : + type == QMouseEvent::MouseButtonPress ? "MouseButtonPress" : + type == QMouseEvent::MouseButtonRelease ? "MouseButtonRelease" : + type == QMouseEvent::MouseButtonDblClick ? "MouseButtonDblClick" : + type == QMouseEvent::MouseMove ? "MouseMove" : + type == QTabletEvent::TabletMove ? "TabletMove" : + type == QTabletEvent::TabletPress ? "TabletPress" : + type == QTabletEvent::TabletRelease ? "TabletRelease" : + "unknown"; } -inline QString buttons(const QTabletEvent &ev) { +inline QString button(const QWheelEvent &ev) { Q_UNUSED(ev); return "-"; } @@ -51,42 +64,19 @@ template void dumpBaseParams(QTextStream &s, const Event &ev, const QString &prefix) { - s << qSetFieldWidth(10) << left << prefix << reset << " "; - s << qSetFieldWidth(17) << left << exTypeToString(ev.type()) << reset; + s << qSetFieldWidth(5) << left << prefix << reset << " "; + s << qSetFieldWidth(17) << left << KisTabletDebugger::exTypeToString(ev.type()) << reset; } template void dumpMouseRelatedParams(QTextStream &s, const Event &ev) { - s << "btn: " << button(ev) << " "; + s << "btn: " << button(ev) << " "; s << "btns: " << buttons(ev) << " "; - s << "pos: " << qSetFieldWidth(4) << ev.x() << qSetFieldWidth(0) << "," << qSetFieldWidth(4) << ev.y() << qSetFieldWidth(0) << " "; - s << "gpos: " << qSetFieldWidth(4) << ev.globalX() << qSetFieldWidth(0) << "," << qSetFieldWidth(4) << ev.globalY() << qSetFieldWidth(0) << " "; + s << "pos: " << qSetFieldWidth(4) << ev.x() << qSetFieldWidth(0) << "," << qSetFieldWidth(4) << ev.y() << qSetFieldWidth(0) << " "; + s << "gpos: " << qSetFieldWidth(4) << ev.globalX() << qSetFieldWidth(0) << "," << qSetFieldWidth(4) << ev.globalY() << qSetFieldWidth(0) << " "; } -QString exTypeToString(QEvent::Type type) { - return - type == QEvent::TabletEnterProximity ? "TabletEnterProximity" : - type == QEvent::TabletLeaveProximity ? "TabletLeaveProximity" : - type == QEvent::Enter ? "Enter" : - type == QEvent::Leave ? "Leave" : - type == QEvent::FocusIn ? "FocusIn" : - type == QEvent::Wheel ? "Wheel" : - type == QEvent::KeyPress ? "KeyPress" : - type == QEvent::KeyRelease ? "KeyRelease" : - type == QEvent::ShortcutOverride ? "ShortcutOverride" : - type == QMouseEvent::MouseButtonPress ? "MouseButtonPress" : - type == QMouseEvent::MouseButtonRelease ? "MouseButtonRelease" : - type == QMouseEvent::MouseButtonDblClick ? "MouseButtonDblClick" : - type == QMouseEvent::MouseMove ? "MouseMove" : - type == QTabletEvent::TabletMove ? "TabletMove" : - type == QTabletEvent::TabletPress ? "TabletPress" : - type == QTabletEvent::TabletRelease ? "TabletRelease" : - type == (QEvent::Type) KisTabletEvent::TabletMoveEx ? "TabletMoveEx" : - type == (QEvent::Type) KisTabletEvent::TabletPressEx ? "TabletPressEx" : - type == (QEvent::Type) KisTabletEvent::TabletReleaseEx ? "TabletReleaseEx" : - "unknown"; -} KisTabletDebugger::KisTabletDebugger() @@ -103,6 +93,16 @@ void KisTabletDebugger::toggleDebugging() { m_debugEnabled = !m_debugEnabled; + + QMessageBox::information(0, i18nc("@title:window", "Krita"), m_debugEnabled ? + i18n("Tablet Event Logging Enabled") : + i18n("Tablet Event Logging Disabled")); + if (m_debugEnabled) { + qDebug() << "vvvvvvvvvvvvvvvvvvvvvvv START TABLET EVENT LOG vvvvvvvvvvvvvvvvvvvvvvv"; + } + else { + qDebug() << "^^^^^^^^^^^^^^^^^^^^^^^ START TABLET EVENT LOG ^^^^^^^^^^^^^^^^^^^^^^^"; + } } bool KisTabletDebugger::debugEnabled() const @@ -129,6 +129,7 @@ dumpBaseParams(s, ev, prefix); dumpMouseRelatedParams(s, ev); + s << "Source:" << ev.source(); return string; } @@ -201,11 +202,6 @@ return tabletEventToString(ev, prefix); } -QString KisTabletDebugger::eventToString(const KisTabletEvent &ev, const QString &prefix) -{ - return tabletEventToString(ev, prefix); -} - QString KisTabletDebugger::tabletDeviceToString(QTabletEvent::TabletDevice device) { return diff --git a/krita/ui/input/kis_tool_invocation_action.cpp b/krita/ui/input/kis_tool_invocation_action.cpp --- a/krita/ui/input/kis_tool_invocation_action.cpp +++ b/krita/ui/input/kis_tool_invocation_action.cpp @@ -99,8 +99,7 @@ if (shortcut == ActivateShortcut || shortcut == LineToolShortcut) { d->active = inputManager()->toolProxy()->forwardEvent( - KisToolProxy::BEGIN, KisTool::Primary, event, event, - inputManager()->lastTabletEvent()); + KisToolProxy::BEGIN, KisTool::Primary, event, event); } else if (shortcut == ConfirmShortcut) { QKeyEvent pressEvent(QEvent::KeyPress, Qt::Key_Return, 0); inputManager()->toolProxy()->keyPressEvent(&pressEvent); @@ -128,8 +127,7 @@ { if (d->active) { inputManager()->toolProxy()-> - forwardEvent(KisToolProxy::END, KisTool::Primary, event, event, - inputManager()->lastTabletEvent()); + forwardEvent(KisToolProxy::END, KisTool::Primary, event, event); d->active = false; } @@ -143,8 +141,7 @@ inputManager()->toolProxy()-> - forwardEvent(KisToolProxy::CONTINUE, KisTool::Primary, event, event, - inputManager()->lastTabletEvent()); + forwardEvent(KisToolProxy::CONTINUE, KisTool::Primary, event, event); } void KisToolInvocationAction::processUnhandledEvent(QEvent* event) diff --git a/krita/ui/input/kis_zoom_action.h b/krita/ui/input/kis_zoom_action.h --- a/krita/ui/input/kis_zoom_action.h +++ b/krita/ui/input/kis_zoom_action.h @@ -51,7 +51,7 @@ void begin(int shortcut, QEvent *event = 0); void inputEvent(QEvent* event); - void mouseMoved(const QPointF &lastPos, const QPointF &pos); + void cursorMoved(const QPointF &lastPos, const QPointF &pos); virtual bool isShortcutRequired(int shortcut) const; diff --git a/krita/ui/input/kis_zoom_action.cpp b/krita/ui/input/kis_zoom_action.cpp --- a/krita/ui/input/kis_zoom_action.cpp +++ b/krita/ui/input/kis_zoom_action.cpp @@ -74,19 +74,7 @@ KoZoomAction *zoomAction = q->inputManager()->canvas()->viewManager()->zoomController()->zoomAction(); if (event) { - QPoint pos; - QMouseEvent *mouseEvent = dynamic_cast(event); - if (mouseEvent) { - pos = mouseEvent->pos(); - } else { - QWheelEvent *wheelEvent = dynamic_cast(event); - if (wheelEvent) { - pos = wheelEvent->pos(); - } else { - qWarning() << "Unhandled type of event"; - } - } - + QPoint pos = eventPos(event); float oldZoom = zoomAction->effectiveZoom(); float newZoom = zoomIn ? zoomAction->nextZoomLevel() : zoomAction->prevZoomLevel(); @@ -222,7 +210,7 @@ KisAbstractInputAction::inputEvent(event); } -void KisZoomAction::mouseMoved(const QPointF &lastPos, const QPointF &pos) +void KisZoomAction::cursorMoved(const QPointF &lastPos, const QPointF &pos) { QPointF diff = -(pos - lastPos); diff --git a/krita/ui/tests/kis_input_manager_test.cpp b/krita/ui/tests/kis_input_manager_test.cpp --- a/krita/ui/tests/kis_input_manager_test.cpp +++ b/krita/ui/tests/kis_input_manager_test.cpp @@ -343,7 +343,7 @@ QVERIFY(!m.keyPressed(Qt::Key_Control)); QCOMPARE(a->m_beginIndex, -1); - QVERIFY(!m.mouseMoved(&mouseEvent)); + QVERIFY(!m.cursorMoved(&mouseEvent)); QCOMPARE(a->m_gotInput, false); // Complete Ctrl+Shift+LB shortcut