1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtWidgets module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qmacgesturerecognizer_p.h"
41 #include "qgesture.h"
42 #include "qgesture_p.h"
43 #include "qevent.h"
44 #include "qwidget.h"
45 #include "qdebug.h"
46 #include <QtCore/qcoreapplication.h>
47 
48 #ifndef QT_NO_GESTURES
49 
50 QT_BEGIN_NAMESPACE
51 
QMacSwipeGestureRecognizer()52 QMacSwipeGestureRecognizer::QMacSwipeGestureRecognizer()
53 {
54 }
55 
create(QObject *)56 QGesture *QMacSwipeGestureRecognizer::create(QObject * /*target*/)
57 {
58     return new QSwipeGesture;
59 }
60 
61 QGestureRecognizer::Result
recognize(QGesture * gesture,QObject * obj,QEvent * event)62 QMacSwipeGestureRecognizer::recognize(QGesture *gesture, QObject *obj, QEvent *event)
63 {
64     if (event->type() == QEvent::NativeGesture && obj->isWidgetType()) {
65         QNativeGestureEvent *ev = static_cast<QNativeGestureEvent*>(event);
66         switch (ev->gestureType()) {
67             case Qt::SwipeNativeGesture: {
68                 QSwipeGesture *g = static_cast<QSwipeGesture *>(gesture);
69                 g->setSwipeAngle(ev->value());
70                 g->setHotSpot(ev->screenPos());
71                 return QGestureRecognizer::FinishGesture | QGestureRecognizer::ConsumeEventHint;
72                 break; }
73             default:
74                 break;
75         }
76     }
77 
78     return QGestureRecognizer::Ignore;
79 }
80 
reset(QGesture * gesture)81 void QMacSwipeGestureRecognizer::reset(QGesture *gesture)
82 {
83     QSwipeGesture *g = static_cast<QSwipeGesture *>(gesture);
84     g->setSwipeAngle(0);
85     QGestureRecognizer::reset(gesture);
86 }
87 
88 ////////////////////////////////////////////////////////////////////////
89 
QMacPinchGestureRecognizer()90 QMacPinchGestureRecognizer::QMacPinchGestureRecognizer()
91 {
92 }
93 
create(QObject *)94 QGesture *QMacPinchGestureRecognizer::create(QObject * /*target*/)
95 {
96     return new QPinchGesture;
97 }
98 
99 QGestureRecognizer::Result
recognize(QGesture * gesture,QObject * obj,QEvent * event)100 QMacPinchGestureRecognizer::recognize(QGesture *gesture, QObject *obj, QEvent *event)
101 {
102     if (event->type() == QEvent::NativeGesture && obj->isWidgetType()) {
103         QPinchGesture *g = static_cast<QPinchGesture *>(gesture);
104         QNativeGestureEvent *ev = static_cast<QNativeGestureEvent*>(event);
105         switch (ev->gestureType()) {
106         case Qt::BeginNativeGesture:
107             reset(gesture);
108             g->setStartCenterPoint(static_cast<QWidget*>(obj)->mapFromGlobal(ev->screenPos().toPoint()));
109             g->setCenterPoint(g->startCenterPoint());
110             g->setChangeFlags(QPinchGesture::CenterPointChanged);
111             g->setTotalChangeFlags(g->totalChangeFlags() | g->changeFlags());
112             g->setHotSpot(ev->screenPos());
113             return QGestureRecognizer::MayBeGesture | QGestureRecognizer::ConsumeEventHint;
114         case Qt::RotateNativeGesture:
115             g->setLastScaleFactor(g->scaleFactor());
116             g->setLastRotationAngle(g->rotationAngle());
117             g->setRotationAngle(g->rotationAngle() + ev->value());
118             g->setChangeFlags(QPinchGesture::RotationAngleChanged);
119             g->setTotalChangeFlags(g->totalChangeFlags() | g->changeFlags());
120             g->setHotSpot(ev->screenPos());
121             return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint;
122         case Qt::ZoomNativeGesture:
123             g->setLastScaleFactor(g->scaleFactor());
124             g->setLastRotationAngle(g->rotationAngle());
125             g->setScaleFactor(1 + ev->value());
126             g->setTotalScaleFactor(g->totalScaleFactor() * g->scaleFactor());
127             g->setChangeFlags(QPinchGesture::ScaleFactorChanged);
128             g->setTotalChangeFlags(g->totalChangeFlags() | g->changeFlags());
129             g->setHotSpot(ev->screenPos());
130             return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint;
131         case Qt::SmartZoomNativeGesture:
132             g->setLastScaleFactor(g->scaleFactor());
133             g->setLastRotationAngle(g->rotationAngle());
134             g->setScaleFactor(ev->value() ? 1.7f : 1.0f);
135             g->setChangeFlags(QPinchGesture::ScaleFactorChanged);
136             g->setTotalChangeFlags(g->totalChangeFlags() | g->changeFlags());
137             g->setHotSpot(ev->screenPos());
138             return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint;
139         case Qt::EndNativeGesture:
140             return QGestureRecognizer::FinishGesture | QGestureRecognizer::ConsumeEventHint;
141         default:
142             break;
143         }
144     }
145 
146     return QGestureRecognizer::Ignore;
147 }
148 
reset(QGesture * gesture)149 void QMacPinchGestureRecognizer::reset(QGesture *gesture)
150 {
151     QPinchGesture *g = static_cast<QPinchGesture *>(gesture);
152     g->setChangeFlags({});
153     g->setTotalChangeFlags({});
154     g->setScaleFactor(1.0f);
155     g->setTotalScaleFactor(1.0f);
156     g->setLastScaleFactor(1.0f);
157     g->setRotationAngle(0.0f);
158     g->setTotalRotationAngle(0.0f);
159     g->setLastRotationAngle(0.0f);
160     g->setCenterPoint(QPointF());
161     g->setStartCenterPoint(QPointF());
162     g->setLastCenterPoint(QPointF());
163     QGestureRecognizer::reset(gesture);
164 }
165 
166 ////////////////////////////////////////////////////////////////////////
167 
QMacPanGestureRecognizer()168 QMacPanGestureRecognizer::QMacPanGestureRecognizer() : _panCanceled(true)
169 {
170 }
171 
create(QObject * target)172 QGesture *QMacPanGestureRecognizer::create(QObject *target)
173 {
174     if (!target)
175         return new QPanGesture;
176 
177     if (QWidget *w = qobject_cast<QWidget *>(target)) {
178         w->setAttribute(Qt::WA_AcceptTouchEvents);
179         w->setAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents);
180         return new QPanGesture;
181     }
182     return 0;
183 }
184 
timerEvent(QTimerEvent * ev)185 void QMacPanGestureRecognizer::timerEvent(QTimerEvent *ev)
186 {
187     if (ev->timerId() == _panTimer.timerId()) {
188         if (_panTimer.isActive())
189            _panTimer.stop();
190         if (_target)
191             QCoreApplication::sendEvent(_target, ev);
192     }
193 }
194 
195 QGestureRecognizer::Result
recognize(QGesture * gesture,QObject * target,QEvent * event)196 QMacPanGestureRecognizer::recognize(QGesture *gesture, QObject *target, QEvent *event)
197 {
198     const int panBeginDelay = 300;
199     const int panBeginRadius = 3;
200 
201     QPanGesture *g = static_cast<QPanGesture *>(gesture);
202 
203     switch (event->type()) {
204     case QEvent::TouchBegin: {
205         const QTouchEvent *ev = static_cast<const QTouchEvent*>(event);
206         if (ev->touchPoints().size() == 1) {
207             reset(gesture);
208             _startPos = QCursor::pos();
209             _target = target;
210             _panTimer.start(panBeginDelay, this);
211             _panCanceled = false;
212             return QGestureRecognizer::MayBeGesture;
213         }
214         break;}
215     case QEvent::TouchEnd: {
216         if (_panCanceled)
217             break;
218 
219         const QTouchEvent *ev = static_cast<const QTouchEvent*>(event);
220         if (ev->touchPoints().size() == 1)
221             return QGestureRecognizer::FinishGesture;
222         break;}
223     case QEvent::TouchUpdate: {
224         if (_panCanceled)
225             break;
226 
227         const QTouchEvent *ev = static_cast<const QTouchEvent*>(event);
228         if (ev->touchPoints().size() == 1) {
229             if (_panTimer.isActive()) {
230                 // INVARIANT: Still in maybeGesture. Check if the user
231                 // moved his finger so much that it makes sense to cancel the pan:
232                 const QPointF p = QCursor::pos();
233                 if ((p - _startPos).manhattanLength() > panBeginRadius) {
234                     _panCanceled = true;
235                     _panTimer.stop();
236                     return QGestureRecognizer::CancelGesture;
237                 }
238             } else {
239                 const QPointF p = QCursor::pos();
240                 const QPointF posOffset = p - _startPos;
241                 g->setLastOffset(g->offset());
242                 g->setOffset(QPointF(posOffset.x(), posOffset.y()));
243                 g->setHotSpot(_startPos);
244                 return QGestureRecognizer::TriggerGesture;
245             }
246         } else if (_panTimer.isActive()) {
247             // I only want to cancel the pan if the user is pressing
248             // more than one finger, and the pan hasn't started yet:
249             _panCanceled = true;
250             _panTimer.stop();
251             return QGestureRecognizer::CancelGesture;
252         }
253         break;}
254     case QEvent::Timer: {
255         QTimerEvent *ev = static_cast<QTimerEvent *>(event);
256         if (ev->timerId() == _panTimer.timerId()) {
257             if (_panCanceled)
258                 break;
259             // Begin new pan session!
260             _startPos = QCursor::pos();
261             g->setHotSpot(_startPos);
262             return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint;
263         }
264         break; }
265     default:
266         break;
267     }
268 
269     return QGestureRecognizer::Ignore;
270 }
271 
reset(QGesture * gesture)272 void QMacPanGestureRecognizer::reset(QGesture *gesture)
273 {
274     QPanGesture *g = static_cast<QPanGesture *>(gesture);
275     _startPos = QPointF();
276     _panCanceled = true;
277     _panTimer.stop();
278     g->setOffset(QPointF(0, 0));
279     g->setLastOffset(QPointF(0, 0));
280     g->setAcceleration(qreal(1));
281     QGestureRecognizer::reset(gesture);
282 }
283 
284 QT_END_NAMESPACE
285 
286 #endif // QT_NO_GESTURES
287