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 test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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 General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28 
29 #include "qdeclarativepinchgenerator_p.h"
30 
31 #include <QtTest/QtTest>
32 #include <QtGui/QGuiApplication>
33 #include <QtGui/qpa/qwindowsysteminterface.h>
34 #include <QtGui/QStyleHints>
35 
36 QT_BEGIN_NAMESPACE
37 
QDeclarativePinchGenerator()38 QDeclarativePinchGenerator::QDeclarativePinchGenerator():
39     target_(0),
40     state_(Invalid),
41     window_(0),
42     activeSwipe_(0),
43     replayTimer_(-1),
44     replayBookmark_(-1),
45     masterSwipe_(-1),
46     replaySpeedFactor_(1.0),
47     enabled_(true)
48 {
49     setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton);
50     swipeTimer_.invalidate();
51     device_ = new QTouchDevice;
52     device_->setType(QTouchDevice::TouchScreen);
53     QWindowSystemInterface::registerTouchDevice(device_);
54 }
55 
~QDeclarativePinchGenerator()56 QDeclarativePinchGenerator::~QDeclarativePinchGenerator()
57 {
58     clear();
59 }
60 
componentComplete()61 void QDeclarativePinchGenerator::componentComplete()
62 {
63     QQuickItem::componentComplete();
64 }
65 
mousePressEvent(QMouseEvent * event)66 void QDeclarativePinchGenerator::mousePressEvent(QMouseEvent *event)
67 {
68     if (state_ != Idle || !enabled_) {
69         event->ignore();
70         return;
71     }
72     Q_ASSERT(!activeSwipe_);
73     Q_ASSERT(!swipeTimer_.isValid());
74     // Start recording a pinch gesture.
75     activeSwipe_ = new Swipe;
76     activeSwipe_->touchPoints << event->pos();
77     activeSwipe_->durations << 0;
78     swipeTimer_.start();
79     setState(Recording);
80 }
81 
mouseMoveEvent(QMouseEvent * event)82 void QDeclarativePinchGenerator::mouseMoveEvent(QMouseEvent *event)
83 {
84     if (state_ != Recording || !enabled_) {
85         event->ignore();
86         return;
87     }
88     Q_ASSERT(activeSwipe_);
89     Q_ASSERT(swipeTimer_.isValid());
90 
91     activeSwipe_->touchPoints << event->pos();
92     activeSwipe_->durations << swipeTimer_.elapsed();
93     swipeTimer_.restart();
94 }
95 
mouseReleaseEvent(QMouseEvent * event)96 void QDeclarativePinchGenerator::mouseReleaseEvent(QMouseEvent *event)
97 {
98     if (state_ != Recording || !enabled_) {
99         event->ignore();
100         return;
101     }
102     Q_ASSERT(activeSwipe_);
103     Q_ASSERT(swipeTimer_.isValid());
104     activeSwipe_->touchPoints << event->pos();
105     activeSwipe_->durations << swipeTimer_.elapsed();
106 
107     if (swipes_.count() == SWIPES_REQUIRED)
108         delete swipes_.takeFirst();
109     swipes_ << activeSwipe_;
110     activeSwipe_ = 0;
111     swipeTimer_.invalidate();
112     if (window_ && target_) setState(Idle); else setState(Invalid);
113 }
114 
mouseDoubleClickEvent(QMouseEvent * event)115 void QDeclarativePinchGenerator::mouseDoubleClickEvent(QMouseEvent *event)
116 {
117     Q_UNUSED(event);
118     if (!enabled_) {
119         event->ignore();
120         return;
121     }
122     stop();
123     clear();
124     if (window_ && target_) setState(Idle); else setState(Invalid);
125 }
126 
keyPressEvent(QKeyEvent * e)127 void QDeclarativePinchGenerator::keyPressEvent(QKeyEvent *e)
128 {
129     if (!enabled_) {
130         e->ignore();
131     }
132 
133     if (e->key() == Qt::Key_C) {
134         clear();
135     } else if (e->key() == Qt::Key_R) {
136         replay();
137     } else if (e->key() == Qt::Key_S) {
138         stop();
139     } else if (e->key() == Qt::Key_Plus) {
140         setReplaySpeedFactor(replaySpeedFactor() + 0.1);
141     } else if (e->key() == Qt::Key_Minus) {
142         setReplaySpeedFactor(replaySpeedFactor() - 0.1);
143     } else {
144         qDebug() << metaObject()->className() << "Unsupported key event.";
145     }
146 }
147 
enabled() const148 bool QDeclarativePinchGenerator::enabled() const
149 {
150     return enabled_;
151 }
152 
153 
setEnabled(bool enabled)154 void QDeclarativePinchGenerator::setEnabled(bool enabled)
155 {
156     if (enabled == enabled_)
157         return;
158     enabled_ = enabled;
159     if (!enabled_) {
160         stop();
161         clear();
162     }
163     emit enabledChanged();
164 }
165 
166 
replaySpeedFactor() const167 qreal QDeclarativePinchGenerator::replaySpeedFactor() const
168 {
169     return replaySpeedFactor_;
170 }
171 
setReplaySpeedFactor(qreal factor)172 void QDeclarativePinchGenerator::setReplaySpeedFactor(qreal factor)
173 {
174     if (factor == replaySpeedFactor_ || factor < 0.001)
175         return;
176     replaySpeedFactor_ = factor;
177     emit replaySpeedFactorChanged();
178 }
179 
180 
state() const181 QString QDeclarativePinchGenerator::state() const
182 {
183     switch (state_) {
184     case Invalid:
185         return "Invalid";
186     case Idle:
187         return "Idle";
188         break;
189     case Recording:
190         return "Recording";
191         break;
192     case Replaying:
193         return "Replaying";
194         break;
195     default:
196         Q_ASSERT(false);
197     }
198     return "How emberassing";
199 }
200 
setState(GeneratorState state)201 void QDeclarativePinchGenerator::setState(GeneratorState state)
202 {
203     if (state == state_)
204         return;
205     state_ = state;
206     emit stateChanged();
207 }
208 
itemChange(ItemChange change,const ItemChangeData & data)209 void QDeclarativePinchGenerator::itemChange(ItemChange change, const ItemChangeData & data)
210 {
211     if (change == ItemSceneChange) {
212         window_ = data.window;
213         if (target_)
214             setState(Idle);
215     }
216 }
217 
timerEvent(QTimerEvent * event)218 void QDeclarativePinchGenerator::timerEvent(QTimerEvent *event)
219 {
220     Q_ASSERT(replayTimer_ == event->timerId());
221     Q_UNUSED(event);
222     Q_ASSERT(state_ == Replaying);
223 
224     int slaveSwipe = masterSwipe_ ^ 1;
225 
226     int masterCount = swipes_.at(masterSwipe_)->touchPoints.count();
227     int slaveCount = swipes_.at(slaveSwipe)->touchPoints.count();
228 
229     if (replayBookmark_ == 0) {
230         QTest::touchEvent(window_, device_)
231                 .press(0, swipes_.at(masterSwipe_)->touchPoints.at(replayBookmark_))
232                 .press(1, swipes_.at(slaveSwipe)->touchPoints.at(replayBookmark_));
233     } else if (replayBookmark_ == (slaveCount - 1)) {
234         if (masterCount != slaveCount) {
235             QTest::touchEvent(window_, device_)
236                     .move(0, swipes_.at(masterSwipe_)->touchPoints.at(replayBookmark_))
237                     .release(1, swipes_.at(slaveSwipe)->touchPoints.at(replayBookmark_));
238         } else {
239             QTest::touchEvent(window_, device_)
240                     .release(0, swipes_.at(masterSwipe_)->touchPoints.at(replayBookmark_))
241                     .release(1, swipes_.at(slaveSwipe)->touchPoints.at(replayBookmark_));
242         }
243     } else if (replayBookmark_ == (masterCount - 1)) {
244             QTest::touchEvent(window_, device_)
245                     .release(0, swipes_.at(masterSwipe_)->touchPoints.at(replayBookmark_));
246     }
247     else {
248         QTest::touchEvent(window_, device_)
249                 .move(0, swipes_.at(masterSwipe_)->touchPoints.at(replayBookmark_))
250                 .move(1, swipes_.at(slaveSwipe)->touchPoints.at(replayBookmark_));
251     }
252 
253     replayBookmark_++;
254     if (replayBookmark_ >= swipes_.at(masterSwipe_)->touchPoints.count())
255         stop();
256     else {
257         killTimer(replayTimer_);
258         replayTimer_ = startTimer((swipes_.at(masterSwipe_)->durations.at(replayBookmark_) + 5) / replaySpeedFactor_ );
259     }
260 }
261 
target() const262 QQuickItem* QDeclarativePinchGenerator::target() const
263 {
264     return target_;
265 }
266 
setTarget(QQuickItem * target)267 void QDeclarativePinchGenerator::setTarget(QQuickItem* target)
268 {
269     if (target == target_)
270         return;
271     target_ = target;
272     stop();
273     clear();
274     if (window_)
275         setState(Idle);
276     else
277         setState(Invalid);
278     emit targetChanged();
279 }
280 
pinch(QPoint point1From,QPoint point1To,QPoint point2From,QPoint point2To,int interval1,int interval2,int samples1,int samples2)281 void QDeclarativePinchGenerator::pinch(QPoint point1From,
282                                        QPoint point1To,
283                                        QPoint point2From,
284                                        QPoint point2To,
285                                        int interval1,
286                                        int interval2,
287                                        int samples1,
288                                        int samples2)
289 {
290     Q_ASSERT(interval1 > 10);
291     Q_ASSERT(interval2 > 10);
292     Q_ASSERT(samples1 >= 2); // we need press and release events at minimum
293     Q_ASSERT(samples2 >= 2);
294 
295     clear();
296 
297     Swipe* swipe1 = new Swipe;
298     Swipe* swipe2 = new Swipe;
299     for (int i = 0; i < samples1; ++i) {
300         swipe1->touchPoints << point1From + (point1To - point1From) / samples1 * i;
301         swipe1->durations << interval1;
302     }
303     for (int i = 0; i < samples2; ++i) {
304         swipe2->touchPoints << point2From + (point2To - point2From) / samples2 * i;
305         swipe2->durations << interval2;
306     }
307     swipes_ << swipe1 << swipe2;
308     Q_ASSERT(swipes_.at(0));
309     Q_ASSERT(swipes_.at(1));
310 
311     masterSwipe_ = (samples1 >= samples2) ? 0 : 1;
312 
313     replayTimer_ = startTimer(swipes_.at(masterSwipe_)->durations.at(0) / replaySpeedFactor_);
314     replayBookmark_ = 0;
315     setState(Replaying);
316 }
317 
pinchPress(QPoint point1From,QPoint point2From)318 void QDeclarativePinchGenerator::pinchPress(QPoint point1From, QPoint point2From)
319 {
320     QTest::touchEvent(window_, device_).press(0, point1From).press(1, point2From);
321 }
322 
pinchMoveTo(QPoint point1To,QPoint point2To)323 void QDeclarativePinchGenerator::pinchMoveTo(QPoint point1To, QPoint point2To)
324 {
325     QTest::touchEvent(window_, device_).move(0, point1To).move(1, point2To);
326 }
327 
pinchRelease(QPoint point1To,QPoint point2To)328 void QDeclarativePinchGenerator::pinchRelease(QPoint point1To, QPoint point2To)
329 {
330     QTest::touchEvent(window_, device_).release(0, point1To).release(1, point2To);
331 }
332 
replay()333 void QDeclarativePinchGenerator::replay()
334 {
335     if (state_ != Idle) {
336         qDebug() << "Wrong state, will not replay pinch, state: " << state_;
337         return;
338     }
339     if (swipes_.count() < SWIPES_REQUIRED) {
340         qDebug() << "Too few swipes, cannot replay, amount: " << swipes_.count();
341         return;
342     }
343     if ((swipes_.at(0)->touchPoints.count() < 2) || (swipes_.at(1)->touchPoints.count() < 2)) {
344         qDebug() << "Too few touchpoints, won't replay, amount: " <<
345                     swipes_.at(0)->touchPoints.count() << (swipes_.at(1)->touchPoints.count() < 2);
346         return;
347     }
348 
349     masterSwipe_ = (swipes_.at(0)->touchPoints.count() >= swipes_.at(1)->touchPoints.count()) ? 0 : 1;
350 
351     replayTimer_ = startTimer(swipes_.at(masterSwipe_)->touchPoints.count() / replaySpeedFactor_);
352     replayBookmark_ = 0;
353     setState(Replaying);
354 }
355 
clear()356 void QDeclarativePinchGenerator::clear()
357 {
358     stop();
359     delete activeSwipe_;
360     activeSwipe_ = 0;
361     if (!swipes_.isEmpty()) {
362         qDeleteAll(swipes_);
363         swipes_.clear();
364     }
365 }
366 
stop()367 void QDeclarativePinchGenerator::stop()
368 {
369     if (state_ != Replaying)
370         return;
371     // stop replay
372     Q_ASSERT(replayTimer_ != -1);
373     killTimer(replayTimer_);
374     replayTimer_ = -1;
375     setState(Idle);
376 }
377 
startDragDistance()378 int QDeclarativePinchGenerator::startDragDistance()
379 {
380     return qApp->styleHints()->startDragDistance();
381 }
382 
383 QT_END_NAMESPACE
384