Commit 9e9789d4 authored by Bernhard Liebl's avatar Bernhard Liebl

adds native touch gestures for macOS via QNativeGestureEvent; now includes new...

adds native touch gestures for macOS via QNativeGestureEvent; now includes new kis_native_gesture_shortcut files
parent a892dea9
......@@ -24,6 +24,7 @@ version=3
4={2;1;[];0;0;0}
5={3;1;[];0;0;0}
6={4;1;[];0;0;0}
7={0;3;[];0;5;0}
[Rotate Canvas]
0={0;2;[1000020,20];1;0;0}
......@@ -32,6 +33,7 @@ version=3
3={2;1;[34];0;0;0}
4={4;1;[35];0;0;0}
5={3;1;[36];0;0;0}
6={0;4;[];0;0;3}
[Select Layer]
0={1;2;[1000020,52];1;0;0}
......@@ -57,6 +59,7 @@ version=3
10={5;1;[32];0;0;0}
11={0;4;[];0;0;1}
12={8;2;[1000021,1000023];4;0;0}
13={0;4;[];0;0;4}
2={3;1;[2d];0;0;0}
3={2;1;[3d];0;0;0}
4={3;3;[];0;2;0}
......
......@@ -282,6 +282,7 @@ set(kritaui_LIB_SRCS
input/kis_show_palette_action.cpp
input/kis_change_primary_setting_action.cpp
input/kis_abstract_shortcut.cpp
input/kis_native_gesture_shortcut.cpp
input/kis_single_action_shortcut.cpp
input/kis_stroke_shortcut.cpp
input/kis_shortcut_matcher.cpp
......
......@@ -214,15 +214,20 @@ void KisCanvasController::Private::showRotationValueOnCanvas()
QIcon(), 500, KisFloatingMessage::Low, Qt::AlignCenter);
}
void KisCanvasController::rotateCanvas(qreal angle)
void KisCanvasController::rotateCanvas(qreal angle, const QPointF &center)
{
QPoint newOffset = m_d->coordinatesConverter->rotate(m_d->coordinatesConverter->widgetCenterPoint(), angle);
QPoint newOffset = m_d->coordinatesConverter->rotate(center, angle);
m_d->updateDocumentSizeAfterTransform();
setScrollBarValue(newOffset);
m_d->paintOpTransformationConnector->notifyTransformationChanged();
m_d->showRotationValueOnCanvas();
}
void KisCanvasController::rotateCanvas(qreal angle)
{
rotateCanvas(angle, m_d->coordinatesConverter->widgetCenterPoint());
}
void KisCanvasController::rotateCanvasRight15()
{
rotateCanvas(15.0);
......
......@@ -51,6 +51,7 @@ public:
public Q_SLOTS:
void mirrorCanvas(bool enable);
void rotateCanvas(qreal angle, const QPointF &center);
void rotateCanvas(qreal angle);
void rotateCanvasRight15();
void rotateCanvasLeft15();
......
......@@ -176,6 +176,9 @@ QPoint KisAbstractInputAction::eventPos(const QEvent *event) {
case QEvent::Wheel:
return static_cast<const QWheelEvent*>(event)->pos();
case QEvent::NativeGesture:
return static_cast<const QNativeGestureEvent*>(event)->pos();
default:
warnInput << "KisAbstractInputAction" << d->name << "tried to process event data from an unhandled event type" << event->type();
return QPoint();
......@@ -199,6 +202,9 @@ QPointF KisAbstractInputAction::eventPosF(const QEvent *event) {
case QEvent::Wheel:
return static_cast<const QWheelEvent*>(event)->posF();
case QEvent::NativeGesture:
return QPointF(static_cast<const QNativeGestureEvent*>(event)->pos());
default:
warnInput << "KisAbstractInputAction" << d->name << "tried to process event data from an unhandled event type" << event->type();
return QPointF();
......
......@@ -330,6 +330,21 @@ bool KisInputManager::eventFilterImpl(QEvent * event)
case QEvent::Wheel: {
d->debugEvent<QWheelEvent, false>(event);
QWheelEvent *wheelEvent = static_cast<QWheelEvent*>(event);
#ifdef Q_OS_OSX
// Some QT wheel events are actually touch pad pan events. From the QT docs:
// "Wheel events are generated for both mouse wheels and trackpad scroll gestures."
// We differentiate between touchpad events and real mouse wheels by inspecting the
// event source.
if (wheelEvent->source() == Qt::MouseEventSource::MouseEventSynthesizedBySystem) {
KisAbstractInputAction::setInputManager(this);
retval = d->matcher.wheelEvent(KisSingleActionShortcut::WheelTrackpad, wheelEvent);
break;
}
#endif
d->accumulatedScrollDelta += wheelEvent->delta();
KisSingleActionShortcut::WheelAction action;
......@@ -478,13 +493,8 @@ bool KisInputManager::eventFilterImpl(QEvent * event)
case QEvent::TouchBegin:
{
QTouchEvent *tevent = static_cast<QTouchEvent*>(event);
d->touchHasBlockedPressEvents = KisConfig().disableTouchOnCanvas();
// Touch rejection: if touch is disabled on canvas, no need to block mouse press events
if (KisConfig().disableTouchOnCanvas()) d->eatOneMousePress();
if (d->tryHidePopupPalette()) {
retval = true;
} else {
if (startTouch(retval)) {
QTouchEvent *tevent = static_cast<QTouchEvent*>(event);
KisAbstractInputAction::setInputManager(this);
retval = d->matcher.touchBeginEvent(tevent);
event->accept();
......@@ -518,12 +528,44 @@ bool KisInputManager::eventFilterImpl(QEvent * event)
}
case QEvent::TouchEnd:
{
endTouch();
QTouchEvent *tevent = static_cast<QTouchEvent*>(event);
d->touchHasBlockedPressEvents = false;
retval = d->matcher.touchEndEvent(tevent);
event->accept();
break;
}
case QEvent::NativeGesture:
{
QNativeGestureEvent *gevent = static_cast<QNativeGestureEvent*>(event);
switch (gevent->gestureType()) {
case Qt::BeginNativeGesture:
{
if (startTouch(retval)) {
KisAbstractInputAction::setInputManager(this);
retval = d->matcher.nativeGestureBeginEvent(gevent);
event->accept();
}
break;
}
case Qt::EndNativeGesture:
{
endTouch();
retval = d->matcher.nativeGestureEndEvent(gevent);
event->accept();
break;
}
default:
{
KisAbstractInputAction::setInputManager(this);
retval = d->matcher.nativeGestureEvent(gevent);
event->accept();
break;
}
}
break;
}
default:
break;
}
......@@ -531,6 +573,26 @@ bool KisInputManager::eventFilterImpl(QEvent * event)
return !retval ? d->processUnhandledEvent(event) : true;
}
bool KisInputManager::startTouch(bool &retval)
{
d->touchHasBlockedPressEvents = KisConfig().disableTouchOnCanvas();
// Touch rejection: if touch is disabled on canvas, no need to block mouse press events
if (KisConfig().disableTouchOnCanvas()) {
d->eatOneMousePress();
}
if (d->tryHidePopupPalette()) {
retval = true;
return false;
} else {
return true;
}
}
void KisInputManager::endTouch()
{
d->touchHasBlockedPressEvents = false;
}
void KisInputManager::slotCompressedMoveEvent()
{
if (d->compressedMoveEvent) {
......@@ -601,7 +663,9 @@ void KisInputManager::profileChanged()
d->addWheelShortcut(shortcut->action(), shortcut->mode(), shortcut->keys(), shortcut->wheel());
break;
case KisShortcutConfiguration::GestureType:
d->addTouchShortcut(shortcut->action(), shortcut->mode(), shortcut->gesture());
if (!d->addNativeGestureShortcut(shortcut->action(), shortcut->mode(), shortcut->gesture())) {
d->addTouchShortcut(shortcut->action(), shortcut->mode(), shortcut->gesture());
}
break;
default:
break;
......
......@@ -114,6 +114,9 @@ private Q_SLOTS:
void slotCompressedMoveEvent();
private:
bool startTouch(bool &retval);
void endTouch();
bool eventFilterImpl(QEvent * event);
template <class Event>
bool compressMoveEventCommon(Event *event);
......
......@@ -29,6 +29,7 @@
#include "kis_tool_invocation_action.h"
#include "kis_stroke_shortcut.h"
#include "kis_touch_shortcut.h"
#include "kis_native_gesture_shortcut.h"
#include "kis_input_profile_manager.h"
/**
......@@ -426,6 +427,9 @@ void KisInputManager::Private::addWheelShortcut(KisAbstractInputAction* action,
case KisShortcutConfiguration::WheelRight:
a = KisSingleActionShortcut::WheelRight;
break;
case KisShortcutConfiguration::WheelTrackpad:
a = KisSingleActionShortcut::WheelTrackpad;
break;
default:
return;
}
......@@ -452,6 +456,34 @@ void KisInputManager::Private::addTouchShortcut(KisAbstractInputAction* action,
matcher.addShortcut(shortcut);
}
bool KisInputManager::Private::addNativeGestureShortcut(KisAbstractInputAction* action, int index, KisShortcutConfiguration::GestureAction gesture)
{
// each platform should decide here which gestures are handled via QtNativeGestureEvent.
Qt::NativeGestureType type;
switch (gesture) {
#ifdef Q_OS_OSX
case KisShortcutConfiguration::PinchGesture:
type = Qt::ZoomNativeGesture;
break;
case KisShortcutConfiguration::PanGesture:
type = Qt::PanNativeGesture;
break;
case KisShortcutConfiguration::RotateGesture:
type = Qt::RotateNativeGesture;
break;
case KisShortcutConfiguration::SmartZoomGesture:
type = Qt::SmartZoomNativeGesture;
break;
#endif
default:
return false;
}
KisNativeGestureShortcut *shortcut = new KisNativeGestureShortcut(action, index, type);
matcher.addShortcut(shortcut);
return true;
}
void KisInputManager::Private::setupActions()
{
QList<KisAbstractInputAction*> actions = KisInputProfileManager::instance()->actions();
......
......@@ -47,6 +47,7 @@ public:
void addStrokeShortcut(KisAbstractInputAction* action, int index, const QList< Qt::Key >& modifiers, Qt::MouseButtons buttons);
void addKeyShortcut(KisAbstractInputAction* action, int index,const QList<Qt::Key> &keys);
void addTouchShortcut( KisAbstractInputAction* action, int index, KisShortcutConfiguration::GestureAction gesture );
bool addNativeGestureShortcut( 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);
void setupActions();
......
......@@ -233,6 +233,8 @@ void KisInputProfileManager::loadProfiles()
}
}
QStringList profilePaths;
Q_FOREACH(const QString & profileName, profileEntries.keys()) {
if (profileEntries[profileName].isEmpty()) {
......@@ -243,6 +245,11 @@ void KisInputProfileManager::loadProfiles()
// because that's the most local one.
ProfileEntry entry = profileEntries[profileName].first();
QString path(QFileInfo(entry.fullpath).dir().absolutePath());
if (!profilePaths.contains(path)) {
profilePaths.append(path);
}
KConfig config(entry.fullpath, KConfig::SimpleConfig);
KisInputProfile *newProfile = addProfile(entry.name);
......@@ -267,6 +274,9 @@ void KisInputProfileManager::loadProfiles()
}
}
QString profilePathsStr(profilePaths.join("' AND '"));
qDebug() << "input profiles were read from '" << qUtf8Printable(profilePathsStr) << "'.";
KisConfig cfg;
QString currentProfile = cfg.currentInputProfile();
if (d->profiles.size() > 0) {
......
/*
* Copyright (C) 2017 Bernhard Liebl <poke1024@gmx.de>
*
* 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_native_gesture_shortcut.h"
#include <QNativeGestureEvent>
class KisNativeGestureShortcut::Private
{
public:
Private() { }
Qt::NativeGestureType type;
};
KisNativeGestureShortcut::KisNativeGestureShortcut(KisAbstractInputAction* action, int index, Qt::NativeGestureType type)
: KisAbstractShortcut(action, index), d(new Private)
{
d->type = type;
}
KisNativeGestureShortcut::~KisNativeGestureShortcut()
{
delete d;
}
int KisNativeGestureShortcut::priority() const
{
return 0;
}
bool KisNativeGestureShortcut::match(QNativeGestureEvent* event)
{
//printf("checking NativeGesture against KisNativeGestureShortcut %d %d\n", (int)event->gestureType(), (int)d->type);
return event->gestureType() == d->type;
}
/*
* Copyright (C) 2017 Bernhard Liebl <poke1024@gmx.de>
*
* 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.
*
*/
#ifndef KISNATIVEGESTURESHORTCUT_H
#define KISNATIVEGESTURESHORTCUT_H
#include "kis_abstract_shortcut.h"
class QNativeGestureEvent;
class KisNativeGestureShortcut : public KisAbstractShortcut
{
public:
KisNativeGestureShortcut(KisAbstractInputAction* action, int index, Qt::NativeGestureType type);
~KisNativeGestureShortcut() override;
int priority() const override;
bool match(QNativeGestureEvent* event);
private:
class Private;
Private * const d;
};
#endif // KISNATIVEGESTURESHORTCUT_H
......@@ -85,11 +85,25 @@ void KisPanAction::begin(int shortcut, QEvent *event)
{
KisAbstractInputAction::begin(shortcut, event);
bool overrideCursor = true;
switch (shortcut) {
case PanModeShortcut: {
QTouchEvent *tevent = dynamic_cast<QTouchEvent*>(event);
if(tevent)
if (tevent) {
d->lastPosition = d->averagePoint(tevent);
break;
}
// Some QT wheel events are actually be touch pad pan events. From the QT docs:
// "Wheel events are generated for both mouse wheels and trackpad scroll gestures."
QWheelEvent *wheelEvent = dynamic_cast<QWheelEvent*>(event);
if (wheelEvent) {
inputManager()->canvas()->canvasController()->pan(-wheelEvent->pixelDelta());
overrideCursor = false;
break;
}
break;
}
case PanLeftShortcut:
......@@ -105,7 +119,10 @@ void KisPanAction::begin(int shortcut, QEvent *event)
inputManager()->canvas()->canvasController()->pan(QPoint(0, -d->panDistance));
break;
}
QApplication::setOverrideCursor(Qt::ClosedHandCursor);
if (overrideCursor) {
QApplication::setOverrideCursor(Qt::ClosedHandCursor);
}
}
void KisPanAction::end(QEvent *event)
......
......@@ -19,6 +19,7 @@
#include "kis_rotate_canvas_action.h"
#include <QApplication>
#include <QNativeGestureEvent>
#include <klocalizedstring.h>
#include "kis_cursor.h"
......@@ -130,3 +131,22 @@ void KisRotateCanvasAction::cursorMoved(const QPointF &lastPos, const QPointF &p
dynamic_cast<KisCanvasController*>(inputManager()->canvas()->canvasController());
canvasController->rotateCanvas(angle);
}
void KisRotateCanvasAction::inputEvent(QEvent* event)
{
switch (event->type()) {
case QEvent::NativeGesture: {
QNativeGestureEvent *gevent = static_cast<QNativeGestureEvent*>(event);
KisCanvas2 *canvas = inputManager()->canvas();
KisCanvasController *controller = static_cast<KisCanvasController*>(canvas->canvasController());
const float angle = gevent->value();
QPoint widgetPos = canvas->canvasWidget()->mapFromGlobal(gevent->globalPos());
controller->rotateCanvas(angle, widgetPos);
return;
}
default:
break;
}
KisAbstractInputAction::inputEvent(event);
}
......@@ -50,6 +50,7 @@ public:
void deactivate(int shortcut) override;
void begin(int shortcut, QEvent *event) override;
void cursorMoved(const QPointF &lastPos, const QPointF &pos) override;
void inputEvent(QEvent* event) override;
private:
class Private;
......
......@@ -345,6 +345,10 @@ QString KisShortcutConfiguration::wheelToText(KisShortcutConfiguration::MouseWhe
return i18n("Mouse Wheel Right");
break;
case KisShortcutConfiguration::WheelTrackpad:
return i18n("Trackpad Pan");
break;
default:
return i18nc("No mouse wheel buttons for shortcut", "None");
break;
......
......@@ -59,6 +59,7 @@ public:
WheelDown, ///< Downwards movement, toward the user.
WheelLeft, ///< Left movement.
WheelRight, ///< Right movement.
WheelTrackpad, ///< A pan movement on a trackpad.
};
/**
......@@ -68,6 +69,8 @@ public:
NoGesture, ///< No gesture.
PinchGesture, ///< Pinch gesture, fingers moving towards or away from each other.
PanGesture, ///< Pan gesture, fingers staying together but moving across the screen.
RotateGesture, ///<Rotate gesture, two fingers rotating around a pivot point.
SmartZoomGesture, ///< Smart zoom gesture, typically a double tap that is a boolean zoom/unzoom.
};
/**
......
......@@ -26,6 +26,7 @@
#include "kis_abstract_input_action.h"
#include "kis_stroke_shortcut.h"
#include "kis_touch_shortcut.h"
#include "kis_native_gesture_shortcut.h"
#ifdef DEBUG_MATCHER
#include <kis_debug.h>
......@@ -50,9 +51,11 @@ public:
: runningShortcut(0)
, readyShortcut(0)
, touchShortcut(0)
, nativeGestureShortcut(0)
, suppressAllActions(false)
, cursorEntered(false)
, usingTouch(false)
, usingNativeGesture(false)
{}
~Private()
......@@ -65,6 +68,7 @@ public:
QList<KisSingleActionShortcut*> singleActionShortcuts;
QList<KisStrokeShortcut*> strokeShortcuts;
QList<KisTouchShortcut*> touchShortcuts;
QList<KisNativeGestureShortcut*> nativeGestureShortcuts;
QSet<Qt::Key> keys; // Model of currently pressed keys
QSet<Qt::MouseButton> buttons; // Model of currently pressed buttons
......@@ -74,10 +78,12 @@ public:
QList<KisStrokeShortcut*> candidateShortcuts;
KisTouchShortcut *touchShortcut;
KisNativeGestureShortcut *nativeGestureShortcut;
bool suppressAllActions;
bool cursorEntered;
bool usingTouch;
bool usingNativeGesture;
inline bool actionsSuppressed() const {
return suppressAllActions || !cursorEntered;
......@@ -86,6 +92,10 @@ public:
inline bool actionsSuppressedIgnoreFocus() const {
return suppressAllActions;
}
inline bool isUsingTouch() const {
return usingTouch || usingNativeGesture;
}
};
KisShortcutMatcher::KisShortcutMatcher()
......@@ -117,6 +127,10 @@ void KisShortcutMatcher::addShortcut( KisTouchShortcut* shortcut )
m_d->touchShortcuts.append(shortcut);
}
void KisShortcutMatcher::addShortcut(KisNativeGestureShortcut *shortcut) {
m_d->nativeGestureShortcuts.append(shortcut);
}
bool KisShortcutMatcher::supportsHiResInputEvents()
{
return
......@@ -181,7 +195,7 @@ bool KisShortcutMatcher::buttonPressed(Qt::MouseButton button, QEvent *event)
bool retval = false;
if (m_d->usingTouch) {
if (m_d->isUsingTouch()) {
return retval;
}
......@@ -208,7 +222,7 @@ bool KisShortcutMatcher::buttonReleased(Qt::MouseButton button, QEvent *event)
bool retval = false;
if (m_d->usingTouch) {
if (m_d->isUsingTouch()) {
return retval;
}
......@@ -230,7 +244,7 @@ bool KisShortcutMatcher::buttonReleased(Qt::MouseButton button, QEvent *event)
bool KisShortcutMatcher::wheelEvent(KisSingleActionShortcut::WheelAction wheelAction, QWheelEvent *event)
{
if (m_d->runningShortcut || m_d->usingTouch) {
if (m_d->runningShortcut || m_d->isUsingTouch()) {
DEBUG_ACTION("Wheel event canceled.");
return false;
}
......@@ -240,7 +254,7 @@ bool KisShortcutMatcher::wheelEvent(KisSingleActionShortcut::WheelAction wheelAc
bool KisShortcutMatcher::pointerMoved(QEvent *event)
{
if (m_d->usingTouch || !m_d->runningShortcut) {
if (m_d->isUsingTouch() || !m_d->runningShortcut) {
return false;
}
......@@ -305,6 +319,38 @@ bool KisShortcutMatcher::touchEndEvent( QTouchEvent* event )
return false;
}
bool KisShortcutMatcher::nativeGestureBeginEvent(QNativeGestureEvent *event)
{
Q_UNUSED(event)
return true;
}
bool KisShortcutMatcher::nativeGestureEvent(QNativeGestureEvent *event)
{
bool retval = false;
if ( m_d->nativeGestureShortcut && !m_d->nativeGestureShortcut->match( event ) ) {
retval = tryEndNativeGestureShortcut( event );
}
if ( !m_d->nativeGestureShortcut ) {
retval = tryRunNativeGestureShortcut( event );
}
else {
m_d->nativeGestureShortcut->action()->inputEvent( event );
retval = true;
}
return retval;
}
bool KisShortcutMatcher::nativeGestureEndEvent(QNativeGestureEvent *event)
{