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 | |
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: device_); |
54 | } |
55 | |
56 | QDeclarativePinchGenerator::~QDeclarativePinchGenerator() |
57 | { |
58 | clear(); |
59 | } |
60 | |
61 | void QDeclarativePinchGenerator::componentComplete() |
62 | { |
63 | QQuickItem::componentComplete(); |
64 | } |
65 | |
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 | |
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 | |
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 | |
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 | |
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 | |
148 | bool QDeclarativePinchGenerator::enabled() const |
149 | { |
150 | return enabled_; |
151 | } |
152 | |
153 | |
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 | |
167 | qreal QDeclarativePinchGenerator::replaySpeedFactor() const |
168 | { |
169 | return replaySpeedFactor_; |
170 | } |
171 | |
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 | |
181 | 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 | |
201 | void QDeclarativePinchGenerator::setState(GeneratorState state) |
202 | { |
203 | if (state == state_) |
204 | return; |
205 | state_ = state; |
206 | emit stateChanged(); |
207 | } |
208 | |
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 | |
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(i: masterSwipe_)->touchPoints.count(); |
227 | int slaveCount = swipes_.at(i: slaveSwipe)->touchPoints.count(); |
228 | |
229 | if (replayBookmark_ == 0) { |
230 | QTest::touchEvent(window: window_, device: device_) |
231 | .press(touchId: 0, pt: swipes_.at(i: masterSwipe_)->touchPoints.at(i: replayBookmark_)) |
232 | .press(touchId: 1, pt: swipes_.at(i: slaveSwipe)->touchPoints.at(i: replayBookmark_)); |
233 | } else if (replayBookmark_ == (slaveCount - 1)) { |
234 | if (masterCount != slaveCount) { |
235 | QTest::touchEvent(window: window_, device: device_) |
236 | .move(touchId: 0, pt: swipes_.at(i: masterSwipe_)->touchPoints.at(i: replayBookmark_)) |
237 | .release(touchId: 1, pt: swipes_.at(i: slaveSwipe)->touchPoints.at(i: replayBookmark_)); |
238 | } else { |
239 | QTest::touchEvent(window: window_, device: device_) |
240 | .release(touchId: 0, pt: swipes_.at(i: masterSwipe_)->touchPoints.at(i: replayBookmark_)) |
241 | .release(touchId: 1, pt: swipes_.at(i: slaveSwipe)->touchPoints.at(i: replayBookmark_)); |
242 | } |
243 | } else if (replayBookmark_ == (masterCount - 1)) { |
244 | QTest::touchEvent(window: window_, device: device_) |
245 | .release(touchId: 0, pt: swipes_.at(i: masterSwipe_)->touchPoints.at(i: replayBookmark_)); |
246 | } |
247 | else { |
248 | QTest::touchEvent(window: window_, device: device_) |
249 | .move(touchId: 0, pt: swipes_.at(i: masterSwipe_)->touchPoints.at(i: replayBookmark_)) |
250 | .move(touchId: 1, pt: swipes_.at(i: slaveSwipe)->touchPoints.at(i: replayBookmark_)); |
251 | } |
252 | |
253 | replayBookmark_++; |
254 | if (replayBookmark_ >= swipes_.at(i: masterSwipe_)->touchPoints.count()) |
255 | stop(); |
256 | else { |
257 | killTimer(id: replayTimer_); |
258 | replayTimer_ = startTimer(interval: (swipes_.at(i: masterSwipe_)->durations.at(i: replayBookmark_) + 5) / replaySpeedFactor_ ); |
259 | } |
260 | } |
261 | |
262 | QQuickItem* QDeclarativePinchGenerator::target() const |
263 | { |
264 | return target_; |
265 | } |
266 | |
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 | |
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(interval: swipes_.at(i: masterSwipe_)->durations.at(i: 0) / replaySpeedFactor_); |
314 | replayBookmark_ = 0; |
315 | setState(Replaying); |
316 | } |
317 | |
318 | void QDeclarativePinchGenerator::pinchPress(QPoint point1From, QPoint point2From) |
319 | { |
320 | QTest::touchEvent(window: window_, device: device_).press(touchId: 0, pt: point1From).press(touchId: 1, pt: point2From); |
321 | } |
322 | |
323 | void QDeclarativePinchGenerator::pinchMoveTo(QPoint point1To, QPoint point2To) |
324 | { |
325 | QTest::touchEvent(window: window_, device: device_).move(touchId: 0, pt: point1To).move(touchId: 1, pt: point2To); |
326 | } |
327 | |
328 | void QDeclarativePinchGenerator::pinchRelease(QPoint point1To, QPoint point2To) |
329 | { |
330 | QTest::touchEvent(window: window_, device: device_).release(touchId: 0, pt: point1To).release(touchId: 1, pt: point2To); |
331 | } |
332 | |
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(i: 0)->touchPoints.count() < 2) || (swipes_.at(i: 1)->touchPoints.count() < 2)) { |
344 | qDebug() << "Too few touchpoints, won't replay, amount: " << |
345 | swipes_.at(i: 0)->touchPoints.count() << (swipes_.at(i: 1)->touchPoints.count() < 2); |
346 | return; |
347 | } |
348 | |
349 | masterSwipe_ = (swipes_.at(i: 0)->touchPoints.count() >= swipes_.at(i: 1)->touchPoints.count()) ? 0 : 1; |
350 | |
351 | replayTimer_ = startTimer(interval: swipes_.at(i: masterSwipe_)->touchPoints.count() / replaySpeedFactor_); |
352 | replayBookmark_ = 0; |
353 | setState(Replaying); |
354 | } |
355 | |
356 | void QDeclarativePinchGenerator::clear() |
357 | { |
358 | stop(); |
359 | delete activeSwipe_; |
360 | activeSwipe_ = 0; |
361 | if (!swipes_.isEmpty()) { |
362 | qDeleteAll(c: swipes_); |
363 | swipes_.clear(); |
364 | } |
365 | } |
366 | |
367 | void QDeclarativePinchGenerator::stop() |
368 | { |
369 | if (state_ != Replaying) |
370 | return; |
371 | // stop replay |
372 | Q_ASSERT(replayTimer_ != -1); |
373 | killTimer(id: replayTimer_); |
374 | replayTimer_ = -1; |
375 | setState(Idle); |
376 | } |
377 | |
378 | int QDeclarativePinchGenerator::startDragDistance() |
379 | { |
380 | return qApp->styleHints()->startDragDistance(); |
381 | } |
382 | |
383 | QT_END_NAMESPACE |
384 | |