From 77ef990fa7e301f2b1ad3380b4516925cecb750f Mon Sep 17 00:00:00 2001 From: DeepSOIC Date: Sat, 28 Mar 2015 00:34:28 +0300 Subject: [PATCH] Gestures: custom Windows gesture recognizer Translates native Windows pinch and rotate gestures into Qt's pinch gestures. --- src/Gui/CMakeLists.txt | 2 + src/Gui/WinNativeGestureRecognizers.cpp | 227 ++++++++++++++++++++++++ src/Gui/WinNativeGestureRecognizers.h | 70 ++++++++ 3 files changed, 299 insertions(+) create mode 100644 src/Gui/WinNativeGestureRecognizers.cpp create mode 100644 src/Gui/WinNativeGestureRecognizers.h diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index a9196aff9..8ed6a3272 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -897,6 +897,7 @@ SET(FreeCADGui_CPP_SRCS Utilities.cpp WaitCursor.cpp ManualAlignment.cpp + WinNativeGestureRecognizers.cpp ) SET(FreeCADGui_SRCS Application.h @@ -918,6 +919,7 @@ SET(FreeCADGui_SRCS Utilities.h WaitCursor.h ManualAlignment.h + WinNativeGestureRecognizers.h ) SET(FreeCADGui_SRCS diff --git a/src/Gui/WinNativeGestureRecognizers.cpp b/src/Gui/WinNativeGestureRecognizers.cpp new file mode 100644 index 000000000..c0e1c4677 --- /dev/null +++ b/src/Gui/WinNativeGestureRecognizers.cpp @@ -0,0 +1,227 @@ +/*************************************************************************** + * Copyright (c) Victor Titov (DeepSOIC) * + * (vv.titov@gmail.com) 2015 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "PreCompiled.h" +#ifndef _PreComp_ + #include + #include + #include + #include + #include +#endif + +#include "WinNativeGestureRecognizers.h" +#ifdef GESTURE_MESS +//this implementation is a bit incompatible with Qt5, since +//nativegesture members were transformed into properties, and +//the whole event was made public + + +#include +#include + +QT_BEGIN_NAMESPACE + +#if !defined(QT_NO_NATIVE_GESTURES) + +QGesture* WinNativeGestureRecognizerPinch::create(QObject* target) +{ + if (!target) + return new QPinchGestureN; // a special case + if (!target->isWidgetType()) + return 0; + if (qobject_cast(target)) + return 0; + + //QWidget* q = static_cast(target); + /*QWidgetPrivate *d = q->d_func(); + d->nativeGesturePanEnabled = true; + d->winSetupGestures();*/ //fails to compile =(, but we can rely on this being done by grabGesture(Pan... + + return new QPinchGestureN; +} + +QGestureRecognizer::Result WinNativeGestureRecognizerPinch::recognize(QGesture *gesture, QObject *watched, QEvent *event) +{ + QPinchGestureN* q = static_cast (gesture); + //QPinchGesturePrivate* d = q->d_func();//this fails to compile =( But we can get away without it. + + QGestureRecognizer::Result result = QGestureRecognizer::Ignore; + if (event->type() == QEvent::NativeGesture) { + bool bZoom = false; + bool bRotate = false; + QNativeGestureEvent *ev = static_cast(event); + switch(ev->gestureType) { + case QNativeGestureEvent::GestureBegin: + break; + case QNativeGestureEvent::Zoom: + case QNativeGestureEvent::Rotate: + bZoom = ev->gestureType == QNativeGestureEvent::Zoom; + bRotate = ev->gestureType == QNativeGestureEvent::Rotate; + result = QGestureRecognizer::TriggerGesture; + event->accept(); + break; + case QNativeGestureEvent::GestureEnd: + if (q->state() == Qt::NoGesture) + return QGestureRecognizer::Ignore; // some other gesture has ended + result = QGestureRecognizer::FinishGesture; + break; + default: + return QGestureRecognizer::Ignore; + } + double ang = 0.0; + if (bRotate) + ang=GID_ROTATE_ANGLE_FROM_ARGUMENT(LOWORD(ev->argument)); + if (q->state() == Qt::NoGesture) { + //start of a new gesture, prefill stuff + //d->isNewSequence = true; + q->setTotalChangeFlags(0); q->setChangeFlags(0); + + q->setLastCenterPoint(QPointF()); + q->setCenterPoint( + QPointF( + qreal(ev->position.x()), + qreal(ev->position.y()) + ) + ); + q->setStartCenterPoint(q->centerPoint()); + q->setTotalRotationAngle(0.0); q->setLastRotationAngle(0.0); q->setRotationAngle(0.0); + q->setTotalScaleFactor(1.0); q->setLastScaleFactor(1.0); q->setScaleFactor(1.0); + if(bZoom) { + q->lastFingerDistance = ev->argument; + q->fingerDistance = ev->argument; + } else if (bRotate) { + q->myLastRotationAngle = 0; + q->myRotationAngle = 0; + } + } else {//in the middle of gesture + + //store new last values + q->setLastCenterPoint(q->centerPoint()); + q->setLastRotationAngle(q->rotationAngle()); + q->setLastScaleFactor(q->scaleFactor()); + q->lastFingerDistance = q->fingerDistance; + q->myLastRotationAngle = q->myRotationAngle; + + //update the current values + if (bZoom) + q->fingerDistance = ev->argument; + if (bRotate) + q->myRotationAngle = ang; + if(ev->gestureType == QNativeGestureEvent::GestureEnd){ + q->fingerDistance = q->lastFingerDistance;//the end-of-gesture event holds no finger separation data, hence we are using the last value. + q->myRotationAngle = q->myLastRotationAngle; + } + if (bZoom) + q->setScaleFactor( + (qreal)(q->fingerDistance) / (qreal)(q->lastFingerDistance) + ); + if (bRotate) + q->setRotationAngle(qreal(unbranchAngle(q->myRotationAngle - q->myLastRotationAngle))); + q->setCenterPoint( + QPointF( + qreal(ev->position.x()), + qreal(ev->position.y()) + ) + ); + + //compute the changes + QPinchGesture::ChangeFlags cf = 0; + if ( q->scaleFactor() != 1.0 ) + cf |= QPinchGesture::ScaleFactorChanged; + if (q->lastCenterPoint() != q->centerPoint()) + cf |= QPinchGesture::CenterPointChanged; + if (q->rotationAngle() != 0.0) + cf |= QPinchGesture::RotationAngleChanged; + q->setChangeFlags(cf); + + //increment totals + q->setTotalChangeFlags (q->totalChangeFlags() | q->changeFlags()); + q->setTotalScaleFactor (q->totalScaleFactor() * q->scaleFactor()); + q->setTotalRotationAngle (q->totalRotationAngle() + q->rotationAngle()); + } + } + return result; +} + + +void WinNativeGestureRecognizerPinch::reset(QGesture* gesture) +{ + QGestureRecognizer::reset(gesture);//resets the state of the gesture, which is not write-accessible otherwise + QPinchGestureN *q = static_cast(gesture); + q->lastFingerDistance = 0; + q->setTotalChangeFlags(0); q->setChangeFlags(0); + + q->setLastCenterPoint(QPointF()); + q->setCenterPoint( + QPointF( + 0.0, + 0.0 + ) + ); + q->setStartCenterPoint(q->centerPoint()); + q->setTotalRotationAngle(0.0); q->setLastRotationAngle(0.0); q->setRotationAngle(0.0); + q->setTotalScaleFactor(1.0); q->setLastScaleFactor(1.0); q->setScaleFactor(1.0); + q->lastFingerDistance = 0; + q->fingerDistance = 0; +} + +void WinNativeGestureRecognizerPinch::TuneWindowsGestures(QWidget* target) +{ + //modify windows-specific gesture options +#if WINVER >= _WIN32_WINNT_WIN7 + HWND w = target->winId(); + + //fill in the options + const UINT nCfg = 2; + GESTURECONFIG cfgs[nCfg]; + ZeroMemory(&cfgs, sizeof(cfgs)); + cfgs[0].dwID = GID_PAN; + cfgs[0].dwWant = GC_PAN; + cfgs[0].dwBlock = GC_PAN_WITH_GUTTER;//disables stickiness to pure vertical/pure horizontal pans + cfgs[1].dwID = GID_ROTATE; + cfgs[1].dwWant = GC_ROTATE; + + //set the options + bool ret = SetGestureConfig(w, 0, nCfg, cfgs, sizeof(GESTURECONFIG)); + assert(ret); //if(!ret) throw + if(!ret){ + DWORD err = GetLastError();//for debugging + } +#endif +} + +/*! + * \brief WinNativeGestureRecognizerPinch::unbranchAngle utility function to bring an angle into -pi..pi region. + * \param ang + * \return + */ +double WinNativeGestureRecognizerPinch::unbranchAngle(double ang) +{ + const double Pi = 3.14159265358979323846; + return ang - 2.0*Pi*floor((ang+Pi)/(2.0*Pi)); +} + +#endif //!defined(QT_NO_NATIVE_GESTURES) + +#endif // GESTURE_MESS diff --git a/src/Gui/WinNativeGestureRecognizers.h b/src/Gui/WinNativeGestureRecognizers.h new file mode 100644 index 000000000..90900e389 --- /dev/null +++ b/src/Gui/WinNativeGestureRecognizers.h @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (c) Victor Titov (DeepSOIC) * + * (vv.titov@gmail.com) 2015 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +/*! This file adds support for pinch gestures in Windows 8 to Qt4.8. I think it + * may not be necessary for Qt5. I also think this was actually not absolutely + * necessary, and it may be possible to force Qt gesture recognition from plain + * touch input. --DeepSOIC + */ + +#ifndef WINNATIVEGESTURERECOGNIZERS_H +#define WINNATIVEGESTURERECOGNIZERS_H + +#include +#include + +#ifdef Q_WS_WIN +#if QT_VERSION < 0x050000 +#define GESTURE_MESS +#endif // QT_VERSION < 0x050000 +#endif // Q_WS_WIN + +#ifdef GESTURE_MESS + +/*! + * \brief The QPinchGestureN class is a special version of QPinchGesture, + * containing a few extra fields for state tracking. + */ +class QPinchGestureN: public QPinchGesture +{ +public: + int lastFingerDistance;//distance between fingers, in pixels + int fingerDistance; + double myRotationAngle; + double myLastRotationAngle; +}; + +class WinNativeGestureRecognizerPinch : public QGestureRecognizer +{ +public: + WinNativeGestureRecognizerPinch(){} + virtual QGesture* create ( QObject* target ); + virtual Result recognize ( QGesture* gesture, QObject* watched, QEvent* event ); + virtual void reset ( QGesture* gesture ); + static void TuneWindowsGestures(QWidget* target); + static double unbranchAngle(double ang); +}; + +#endif //GESTUREMESS + +#endif // WINNATIVEGESTURERECOGNIZERS_H