1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2018 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtQml module 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 <QtTest/QtTest> |
30 | |
31 | #include <QtQml/qqmlengine.h> |
32 | #include <QtQml/qqmlproperty.h> |
33 | #include <QtQuick/private/qquickdraghandler_p.h> |
34 | #include <QtQuick/private/qquickrepeater_p.h> |
35 | #include <QtQuick/private/qquicktaphandler_p.h> |
36 | #include <QtQuick/qquickitem.h> |
37 | #include <QtQuick/qquickview.h> |
38 | |
39 | #include "../../../shared/util.h" |
40 | #include "../../shared/viewtestutil.h" |
41 | |
42 | Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests" ) |
43 | |
44 | class tst_DragHandler : public QQmlDataTest |
45 | { |
46 | Q_OBJECT |
47 | public: |
48 | tst_DragHandler() |
49 | :touchDevice(QTest::createTouchDevice()) |
50 | {} |
51 | |
52 | private slots: |
53 | void initTestCase(); |
54 | |
55 | void defaultPropertyValues(); |
56 | void touchDrag_data(); |
57 | void touchDrag(); |
58 | void mouseDrag_data(); |
59 | void mouseDrag(); |
60 | void mouseDragThreshold_data(); |
61 | void mouseDragThreshold(); |
62 | void dragFromMargin(); |
63 | void snapMode_data(); |
64 | void snapMode(); |
65 | void touchDragMulti(); |
66 | void touchDragMultiSliders_data(); |
67 | void touchDragMultiSliders(); |
68 | void touchPassiveGrabbers_data(); |
69 | void touchPassiveGrabbers(); |
70 | void touchPinchAndMouseMove(); |
71 | void underModalLayer(); |
72 | |
73 | private: |
74 | void createView(QScopedPointer<QQuickView> &window, const char *fileName); |
75 | QSet<QQuickPointerHandler *> passiveGrabbers(QQuickWindow *window, int pointId = 0); |
76 | QTouchDevice *touchDevice; |
77 | }; |
78 | |
79 | void tst_DragHandler::createView(QScopedPointer<QQuickView> &window, const char *fileName) |
80 | { |
81 | window.reset(other: new QQuickView); |
82 | window->setSource(testFileUrl(fileName)); |
83 | QTRY_COMPARE(window->status(), QQuickView::Ready); |
84 | QQuickViewTestUtil::centerOnScreen(window: window.data()); |
85 | QQuickViewTestUtil::moveMouseAway(window: window.data()); |
86 | |
87 | window->show(); |
88 | QVERIFY(QTest::qWaitForWindowActive(window.data())); |
89 | QVERIFY(window->rootObject() != nullptr); |
90 | } |
91 | |
92 | QSet<QQuickPointerHandler*> tst_DragHandler::passiveGrabbers(QQuickWindow *window, int pointId /*= 0*/) |
93 | { |
94 | QSet<QQuickPointerHandler*> result; |
95 | QQuickWindowPrivate *winp = QQuickWindowPrivate::get(c: window); |
96 | if (QQuickPointerDevice* device = QQuickPointerDevice::touchDevice(d: touchDevice)) { |
97 | QQuickPointerEvent *pointerEvent = winp->pointerEventInstance(device); |
98 | for (int i = 0; i < pointerEvent->pointCount(); ++i) { |
99 | QQuickEventPoint *eventPoint = pointerEvent->point(i); |
100 | QVector<QPointer <QQuickPointerHandler> > passives = eventPoint->passiveGrabbers(); |
101 | if (!pointId || eventPoint->pointId() == pointId) { |
102 | for (auto it = passives.constBegin(); it != passives.constEnd(); ++it) |
103 | result << it->data(); |
104 | } |
105 | } |
106 | } |
107 | return result; |
108 | } |
109 | |
110 | void tst_DragHandler::initTestCase() |
111 | { |
112 | // This test assumes that we don't get synthesized mouse events from QGuiApplication |
113 | qApp->setAttribute(attribute: Qt::AA_SynthesizeMouseForUnhandledTouchEvents, on: false); |
114 | |
115 | QQmlDataTest::initTestCase(); |
116 | } |
117 | |
118 | void tst_DragHandler::defaultPropertyValues() |
119 | { |
120 | QScopedPointer<QQuickView> windowPtr; |
121 | createView(window&: windowPtr, fileName: "draggables.qml" ); |
122 | QQuickView * window = windowPtr.data(); |
123 | |
124 | QQuickItem *ball = window->rootObject()->childItems().first(); |
125 | QVERIFY(ball); |
126 | QQuickDragHandler *dragHandler = ball->findChild<QQuickDragHandler*>(); |
127 | QVERIFY(dragHandler); |
128 | |
129 | QCOMPARE(dragHandler->acceptedButtons(), Qt::LeftButton); |
130 | QCOMPARE(dragHandler->translation(), QVector2D()); |
131 | QCOMPARE(dragHandler->centroid().position(), QPointF()); |
132 | QCOMPARE(dragHandler->centroid().scenePosition(), QPointF()); |
133 | QCOMPARE(dragHandler->centroid().pressPosition(), QPointF()); |
134 | QCOMPARE(dragHandler->centroid().scenePressPosition(), QPointF()); |
135 | QCOMPARE(dragHandler->centroid().sceneGrabPosition(), QPointF()); |
136 | } |
137 | |
138 | void tst_DragHandler::touchDrag_data() |
139 | { |
140 | QTest::addColumn<int>(name: "dragThreshold" ); |
141 | QTest::newRow(dataTag: "threshold zero" ) << 0; |
142 | QTest::newRow(dataTag: "threshold one" ) << 1; |
143 | QTest::newRow(dataTag: "threshold 20" ) << 20; |
144 | QTest::newRow(dataTag: "threshold default" ) << -1; |
145 | } |
146 | |
147 | void tst_DragHandler::touchDrag() |
148 | { |
149 | QFETCH(int, dragThreshold); |
150 | QScopedPointer<QQuickView> windowPtr; |
151 | createView(window&: windowPtr, fileName: "draggables.qml" ); |
152 | QQuickView * window = windowPtr.data(); |
153 | |
154 | QQuickItem *ball = window->rootObject()->childItems().first(); |
155 | QVERIFY(ball); |
156 | QQuickDragHandler *dragHandler = ball->findChild<QQuickDragHandler*>(); |
157 | QVERIFY(dragHandler); |
158 | if (dragThreshold < 0) { |
159 | dragThreshold = QGuiApplication::styleHints()->startDragDistance(); |
160 | QCOMPARE(dragHandler->dragThreshold(), dragThreshold); |
161 | } else { |
162 | dragHandler->setDragThreshold(dragThreshold); |
163 | } |
164 | |
165 | QSignalSpy translationChangedSpy(dragHandler, SIGNAL(translationChanged())); |
166 | QSignalSpy centroidChangedSpy(dragHandler, SIGNAL(centroidChanged())); |
167 | |
168 | QPointF ballCenter = ball->clipRect().center(); |
169 | QPointF scenePressPos = ball->mapToScene(point: ballCenter); |
170 | QPoint p1 = scenePressPos.toPoint(); |
171 | QTest::touchEvent(window, device: touchDevice).press(touchId: 1, pt: p1, window); |
172 | QQuickTouchUtils::flush(window); |
173 | QVERIFY(!dragHandler->active()); |
174 | QCOMPARE(dragHandler->centroid().position(), ballCenter); |
175 | QCOMPARE(dragHandler->centroid().pressPosition(), ballCenter); |
176 | QCOMPARE(dragHandler->centroid().scenePosition(), scenePressPos); |
177 | QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos); |
178 | QCOMPARE(dragHandler->centroid().velocity(), QVector2D()); |
179 | QCOMPARE(centroidChangedSpy.count(), 1); |
180 | p1 += QPoint(dragThreshold, 0); |
181 | QTest::touchEvent(window, device: touchDevice).move(touchId: 1, pt: p1, window); |
182 | QQuickTouchUtils::flush(window); |
183 | qCDebug(lcPointerTests) << "velocity after drag" << dragHandler->centroid().velocity(); |
184 | if (dragThreshold > 0) |
185 | QTRY_VERIFY(!qFuzzyIsNull(dragHandler->centroid().velocity().x())); |
186 | QCOMPARE(centroidChangedSpy.count(), 2); |
187 | QVERIFY(!dragHandler->active()); |
188 | p1 += QPoint(1, 0); |
189 | QTest::touchEvent(window, device: touchDevice).move(touchId: 1, pt: p1, window); |
190 | QQuickTouchUtils::flush(window); |
191 | QTRY_VERIFY(dragHandler->active()); |
192 | QCOMPARE(translationChangedSpy.count(), 0); |
193 | QCOMPARE(centroidChangedSpy.count(), 3); |
194 | QCOMPARE(dragHandler->translation().x(), 0.0); |
195 | QPointF sceneGrabPos = p1; |
196 | QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos); |
197 | p1 += QPoint(19, 0); |
198 | QTest::touchEvent(window, device: touchDevice).move(touchId: 1, pt: p1, window); |
199 | QQuickTouchUtils::flush(window); |
200 | QTRY_VERIFY(dragHandler->active()); |
201 | QCOMPARE(dragHandler->centroid().position(), ballCenter); |
202 | QCOMPARE(dragHandler->centroid().pressPosition(), ballCenter); |
203 | QCOMPARE(dragHandler->centroid().scenePosition(), ball->mapToScene(ballCenter)); |
204 | QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos); |
205 | QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos); |
206 | QCOMPARE(dragHandler->translation().x(), dragThreshold + 20.0); |
207 | QCOMPARE(dragHandler->translation().y(), 0.0); |
208 | QVERIFY(dragHandler->centroid().velocity().x() > 0); |
209 | QCOMPARE(centroidChangedSpy.count(), 4); |
210 | QTest::touchEvent(window, device: touchDevice).release(touchId: 1, pt: p1, window); |
211 | QQuickTouchUtils::flush(window); |
212 | QTRY_VERIFY(!dragHandler->active()); |
213 | QCOMPARE(dragHandler->centroid().pressedButtons(), Qt::NoButton); |
214 | QCOMPARE(dragHandler->centroid().velocity(), QVector2D()); |
215 | QCOMPARE(ball->mapToScene(ballCenter).toPoint(), p1); |
216 | QCOMPARE(translationChangedSpy.count(), 1); |
217 | QCOMPARE(centroidChangedSpy.count(), 5); |
218 | } |
219 | |
220 | void tst_DragHandler::mouseDrag_data() |
221 | { |
222 | QTest::addColumn<Qt::MouseButtons>(name: "acceptedButtons" ); |
223 | QTest::addColumn<Qt::MouseButtons>(name: "dragButton" ); |
224 | QTest::newRow(dataTag: "left: drag" ) << Qt::MouseButtons(Qt::LeftButton) << Qt::MouseButtons(Qt::LeftButton); |
225 | QTest::newRow(dataTag: "right: don't drag" ) << Qt::MouseButtons(Qt::LeftButton) << Qt::MouseButtons(Qt::RightButton); |
226 | QTest::newRow(dataTag: "left: don't drag" ) << Qt::MouseButtons(Qt::RightButton | Qt::MiddleButton) << Qt::MouseButtons(Qt::LeftButton); |
227 | QTest::newRow(dataTag: "right or middle: drag" ) << Qt::MouseButtons(Qt::RightButton | Qt::MiddleButton) << Qt::MouseButtons(Qt::MiddleButton); |
228 | } |
229 | |
230 | void tst_DragHandler::mouseDrag() |
231 | { |
232 | QFETCH(Qt::MouseButtons, acceptedButtons); |
233 | QFETCH(Qt::MouseButtons, dragButton); |
234 | bool shouldDrag = bool(acceptedButtons & dragButton); |
235 | |
236 | const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); |
237 | QScopedPointer<QQuickView> windowPtr; |
238 | createView(window&: windowPtr, fileName: "draggables.qml" ); |
239 | QQuickView * window = windowPtr.data(); |
240 | |
241 | QQuickItem *ball = window->rootObject()->childItems().first(); |
242 | QVERIFY(ball); |
243 | QQuickDragHandler *dragHandler = ball->findChild<QQuickDragHandler*>(); |
244 | QVERIFY(dragHandler); |
245 | dragHandler->setAcceptedButtons(acceptedButtons); // QTBUG-76875 |
246 | |
247 | QSignalSpy translationChangedSpy(dragHandler, SIGNAL(translationChanged())); |
248 | QSignalSpy centroidChangedSpy(dragHandler, SIGNAL(centroidChanged())); |
249 | |
250 | QPointF ballCenter = ball->clipRect().center(); |
251 | QPointF scenePressPos = ball->mapToScene(point: ballCenter); |
252 | QPoint p1 = scenePressPos.toPoint(); |
253 | QTest::mousePress(window, button: static_cast<Qt::MouseButton>(int(dragButton)), stateKey: Qt::NoModifier, pos: p1, delay: 500); |
254 | QVERIFY(!dragHandler->active()); |
255 | #if QT_CONFIG(cursor) |
256 | QCOMPARE(window->cursor().shape(), Qt::ArrowCursor); |
257 | #endif |
258 | if (shouldDrag) { |
259 | QCOMPARE(dragHandler->centroid().position(), ballCenter); |
260 | QCOMPARE(dragHandler->centroid().pressPosition(), ballCenter); |
261 | QCOMPARE(dragHandler->centroid().scenePosition(), scenePressPos); |
262 | QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos); |
263 | QCOMPARE(dragHandler->centroid().velocity(), QVector2D()); |
264 | QCOMPARE(centroidChangedSpy.count(), 1); |
265 | } |
266 | p1 += QPoint(dragThreshold, 0); |
267 | QTest::mouseMove(window, pos: p1); |
268 | if (shouldDrag) { |
269 | QTRY_VERIFY(dragHandler->centroid().velocity().x() > 0); |
270 | QCOMPARE(centroidChangedSpy.count(), 2); |
271 | QVERIFY(!dragHandler->active()); |
272 | #if QT_CONFIG(cursor) |
273 | QCOMPARE(window->cursor().shape(), Qt::ArrowCursor); |
274 | #endif |
275 | } |
276 | p1 += QPoint(1, 0); |
277 | QTest::mouseMove(window, pos: p1); |
278 | if (shouldDrag) |
279 | QTRY_VERIFY(dragHandler->active()); |
280 | else |
281 | QVERIFY(!dragHandler->active()); |
282 | QCOMPARE(translationChangedSpy.count(), 0); |
283 | if (shouldDrag) |
284 | QCOMPARE(centroidChangedSpy.count(), 3); |
285 | QCOMPARE(dragHandler->translation().x(), 0.0); |
286 | QPointF sceneGrabPos = p1; |
287 | if (shouldDrag) |
288 | QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos); |
289 | p1 += QPoint(19, 0); |
290 | QTest::mouseMove(window, pos: p1); |
291 | QVERIFY(shouldDrag ? dragHandler->active() : !dragHandler->active()); |
292 | if (shouldDrag) { |
293 | QCOMPARE(dragHandler->centroid().position(), ballCenter); |
294 | QCOMPARE(dragHandler->centroid().pressPosition(), ballCenter); |
295 | QCOMPARE(dragHandler->centroid().scenePosition(), ball->mapToScene(ballCenter)); |
296 | QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos); |
297 | QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos); |
298 | QCOMPARE(dragHandler->translation().x(), dragThreshold + 20.0); |
299 | QCOMPARE(dragHandler->translation().y(), 0.0); |
300 | QVERIFY(dragHandler->centroid().velocity().x() > 0); |
301 | QCOMPARE(centroidChangedSpy.count(), 4); |
302 | #if QT_CONFIG(cursor) |
303 | QCOMPARE(window->cursor().shape(), Qt::ClosedHandCursor); |
304 | #endif |
305 | } |
306 | QTest::mouseRelease(window, button: static_cast<Qt::MouseButton>(int(dragButton)), stateKey: Qt::NoModifier, pos: p1); |
307 | QTRY_VERIFY(!dragHandler->active()); |
308 | QCOMPARE(dragHandler->centroid().pressedButtons(), Qt::NoButton); |
309 | if (shouldDrag) |
310 | QCOMPARE(ball->mapToScene(ballCenter).toPoint(), p1); |
311 | QCOMPARE(translationChangedSpy.count(), shouldDrag ? 1 : 0); |
312 | QCOMPARE(centroidChangedSpy.count(), shouldDrag ? 5 : 0); |
313 | #if QT_CONFIG(cursor) |
314 | QTest::mouseMove(window, pos: p1 + QPoint(1, 0)); // TODO after fixing QTBUG-53987, don't send mouseMove |
315 | QCOMPARE(window->cursor().shape(), Qt::ArrowCursor); |
316 | #endif |
317 | } |
318 | |
319 | void tst_DragHandler::mouseDragThreshold_data() |
320 | { |
321 | QTest::addColumn<int>(name: "dragThreshold" ); |
322 | QTest::newRow(dataTag: "threshold zero" ) << 0; |
323 | QTest::newRow(dataTag: "threshold one" ) << 1; |
324 | QTest::newRow(dataTag: "threshold 20" ) << 20; |
325 | QTest::newRow(dataTag: "threshold default" ) << -1; |
326 | } |
327 | |
328 | void tst_DragHandler::mouseDragThreshold() |
329 | { |
330 | QFETCH(int, dragThreshold); |
331 | QScopedPointer<QQuickView> windowPtr; |
332 | createView(window&: windowPtr, fileName: "draggables.qml" ); |
333 | QQuickView * window = windowPtr.data(); |
334 | |
335 | QQuickItem *ball = window->rootObject()->childItems().first(); |
336 | QVERIFY(ball); |
337 | QQuickDragHandler *dragHandler = ball->findChild<QQuickDragHandler*>(); |
338 | QVERIFY(dragHandler); |
339 | if (dragThreshold < 0) { |
340 | dragThreshold = QGuiApplication::styleHints()->startDragDistance(); |
341 | QCOMPARE(dragHandler->dragThreshold(), dragThreshold); |
342 | } else { |
343 | dragHandler->setDragThreshold(dragThreshold); |
344 | } |
345 | |
346 | QSignalSpy translationChangedSpy(dragHandler, SIGNAL(translationChanged())); |
347 | QSignalSpy centroidChangedSpy(dragHandler, SIGNAL(centroidChanged())); |
348 | |
349 | QPointF ballCenter = ball->clipRect().center(); |
350 | QPointF scenePressPos = ball->mapToScene(point: ballCenter); |
351 | QPoint p1 = scenePressPos.toPoint(); |
352 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
353 | QVERIFY(!dragHandler->active()); |
354 | QCOMPARE(dragHandler->centroid().position(), ballCenter); |
355 | QCOMPARE(dragHandler->centroid().pressPosition(), ballCenter); |
356 | QCOMPARE(dragHandler->centroid().scenePosition(), scenePressPos); |
357 | QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos); |
358 | QCOMPARE(dragHandler->centroid().velocity(), QVector2D()); |
359 | QCOMPARE(centroidChangedSpy.count(), 1); |
360 | p1 += QPoint(qMax(a: 1, b: dragThreshold), 0); // QTBUG-85431: zero-distance mouse moves are not delivered |
361 | QTest::mouseMove(window, pos: p1); |
362 | if (dragThreshold > 0) |
363 | QTRY_VERIFY(dragHandler->centroid().velocity().x() > 0); |
364 | QCOMPARE(centroidChangedSpy.count(), 2); |
365 | // the handler is not yet active, unless the drag threshold was already exceeded |
366 | QCOMPARE(dragHandler->active(), dragThreshold == 0); |
367 | p1 += QPoint(1, 0); |
368 | QTest::mouseMove(window, pos: p1); |
369 | QTRY_VERIFY(dragHandler->active()); |
370 | QCOMPARE(translationChangedSpy.count(), dragThreshold ? 0 : 1); |
371 | QCOMPARE(centroidChangedSpy.count(), 3); |
372 | QCOMPARE(dragHandler->translation().x(), dragThreshold ? 0 : 2); |
373 | QPointF sceneGrabPos = dragThreshold ? p1 : p1 - QPoint(1, 0); |
374 | QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos); |
375 | p1 += QPoint(19, 0); |
376 | QTest::mouseMove(window, pos: p1); |
377 | QTRY_VERIFY(dragHandler->active()); |
378 | QCOMPARE(dragHandler->centroid().position(), ballCenter); |
379 | QCOMPARE(dragHandler->centroid().pressPosition(), ballCenter); |
380 | QCOMPARE(dragHandler->centroid().scenePosition(), ball->mapToScene(ballCenter)); |
381 | QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos); |
382 | QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos); |
383 | QCOMPARE(dragHandler->translation().x(), dragThreshold + (dragThreshold ? 20 : 21)); |
384 | QCOMPARE(dragHandler->translation().y(), 0.0); |
385 | QVERIFY(dragHandler->centroid().velocity().x() > 0); |
386 | QCOMPARE(centroidChangedSpy.count(), 4); |
387 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
388 | QTRY_VERIFY(!dragHandler->active()); |
389 | QCOMPARE(dragHandler->centroid().pressedButtons(), Qt::NoButton); |
390 | QCOMPARE(ball->mapToScene(ballCenter).toPoint(), p1); |
391 | QCOMPARE(translationChangedSpy.count(), dragThreshold ? 1 : 2); |
392 | QCOMPARE(centroidChangedSpy.count(), 5); |
393 | } |
394 | |
395 | void tst_DragHandler::dragFromMargin() // QTBUG-74966 |
396 | { |
397 | const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); |
398 | QScopedPointer<QQuickView> windowPtr; |
399 | createView(window&: windowPtr, fileName: "dragMargin.qml" ); |
400 | QQuickView * window = windowPtr.data(); |
401 | |
402 | QQuickItem *draggableItem = window->rootObject()->childItems().first(); |
403 | QVERIFY(draggableItem); |
404 | QQuickDragHandler *dragHandler = draggableItem->findChild<QQuickDragHandler*>(); |
405 | QVERIFY(dragHandler); |
406 | |
407 | QPointF originalPos = draggableItem->position(); |
408 | QPointF scenePressPos = originalPos - QPointF(10, 0); |
409 | QPoint p1 = scenePressPos.toPoint(); |
410 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
411 | QVERIFY(!dragHandler->active()); |
412 | QCOMPARE(dragHandler->centroid().scenePosition(), scenePressPos); |
413 | QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos); |
414 | #if QT_CONFIG(cursor) |
415 | QCOMPARE(window->cursor().shape(), Qt::ArrowCursor); |
416 | #endif |
417 | p1 += QPoint(dragThreshold * 2, 0); |
418 | QTest::mouseMove(window, pos: p1); |
419 | QTRY_VERIFY(dragHandler->active()); |
420 | QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos); |
421 | QCOMPARE(dragHandler->centroid().sceneGrabPosition(), p1); |
422 | QCOMPARE(dragHandler->translation().x(), 0.0); // hmm that's odd |
423 | QCOMPARE(dragHandler->translation().y(), 0.0); |
424 | QCOMPARE(draggableItem->position(), originalPos + QPointF(dragThreshold * 2, 0)); |
425 | #if QT_CONFIG(cursor) |
426 | // The cursor doesn't change until the next event after the handler becomes active. |
427 | p1 += QPoint(1, 0); |
428 | QTest::mouseMove(window, pos: p1); |
429 | QTRY_COMPARE(window->cursor().shape(), Qt::ClosedHandCursor); |
430 | #endif |
431 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
432 | QTRY_VERIFY(!dragHandler->active()); |
433 | QCOMPARE(dragHandler->centroid().pressedButtons(), Qt::NoButton); |
434 | #if QT_CONFIG(cursor) |
435 | QTRY_COMPARE(window->cursor().shape(), Qt::ArrowCursor); |
436 | #endif |
437 | } |
438 | |
439 | void tst_DragHandler::snapMode_data() |
440 | { |
441 | const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); |
442 | QTest::addColumn<QString>(name: "subTree" ); |
443 | QTest::addColumn<int>(name: "snapMode" ); |
444 | QTest::addColumn<QPoint>(name: "startDragPos" ); |
445 | QTest::addColumn<QPoint>(name: "dragMovement" ); |
446 | QTest::addColumn<QPoint>(name: "expectedMovement" ); |
447 | |
448 | struct TestEntry { |
449 | const char *desc; |
450 | const char *subTree; |
451 | QQuickDragHandler::SnapMode mode; |
452 | QPoint startDragPos; |
453 | QPoint dragMovement; |
454 | QPoint expectedMovement; |
455 | }; |
456 | |
457 | TestEntry testdata[] = { |
458 | {.desc: "outside the target" , .subTree: "rect1" , .mode: QQuickDragHandler::SnapAuto, .startDragPos: QPoint(45, -10), .dragMovement: QPoint(dragThreshold*2, 0), .expectedMovement: QPoint(dragThreshold*2, 0)}, |
459 | {.desc: "inside the target" , .subTree: "rect1" , .mode: QQuickDragHandler::SnapAuto, .startDragPos: QPoint(45, 10), .dragMovement: QPoint(dragThreshold*2, 0), .expectedMovement: QPoint(dragThreshold*2, 0)}, |
460 | {.desc: "outside the target" , .subTree: "rect1" , .mode: QQuickDragHandler::SnapAlways, .startDragPos: QPoint(45, -10), .dragMovement: QPoint(dragThreshold*2, 0), .expectedMovement: QPoint(dragThreshold*2, -50-10)}, |
461 | {.desc: "outside the target" , .subTree: "rect1" , .mode: QQuickDragHandler::NoSnap, .startDragPos: QPoint(45, -10), .dragMovement: QPoint(dragThreshold*2, 0), .expectedMovement: QPoint(dragThreshold*2, 0)}, |
462 | {.desc: "outside the target" , .subTree: "rect1" , .mode: QQuickDragHandler::SnapIfPressedOutsideTarget, .startDragPos: QPoint(45, -10), .dragMovement: QPoint(dragThreshold*2, 0), .expectedMovement: QPoint(dragThreshold*2, -50-10)}, |
463 | {.desc: "inside the target" , .subTree: "rect1" , .mode: QQuickDragHandler::SnapIfPressedOutsideTarget, .startDragPos: QPoint(45, 10), .dragMovement: QPoint(dragThreshold*2, 0), .expectedMovement: QPoint(dragThreshold*2, 0)}, |
464 | //targets y pos moves from -25 to (25 + dragThreshold*2) because of snapping to center: |
465 | {.desc: "outside target, should snap" , .subTree: "rect2" , .mode: QQuickDragHandler::SnapAuto, .startDragPos: QPoint(45, 50), .dragMovement: QPoint(0, dragThreshold*2), .expectedMovement: QPoint(0, 25 + 25 + dragThreshold*2)}, |
466 | {.desc: "inside target, shouldn't snap" , .subTree: "rect2" , .mode: QQuickDragHandler::SnapAuto, .startDragPos: QPoint(45, 10), .dragMovement: QPoint(0, dragThreshold*2), .expectedMovement: QPoint(0, dragThreshold*2)} |
467 | }; |
468 | |
469 | for (const TestEntry& e : testdata) { |
470 | const QMetaEnum = QMetaEnum::fromType<QQuickDragHandler::SnapMode>(); |
471 | const QString dataTag = QString::fromLatin1(str: "%1, %2, %3" ).arg(a: e.subTree).arg(a: menum.valueToKey(value: e.mode)).arg(a: e.desc); |
472 | QTest::newRow(dataTag: dataTag.toUtf8().constData()) << e.subTree << (int)e.mode |
473 | << e.startDragPos << e.dragMovement << e.expectedMovement; |
474 | } |
475 | } |
476 | |
477 | void tst_DragHandler::snapMode() |
478 | { |
479 | QFETCH(QString, subTree); |
480 | QFETCH(QPoint, startDragPos); |
481 | QFETCH(QPoint, dragMovement); |
482 | QFETCH(int, snapMode); |
483 | QFETCH(QPoint, expectedMovement); |
484 | |
485 | QScopedPointer<QQuickView> windowPtr; |
486 | createView(window&: windowPtr, fileName: "snapMode.qml" ); |
487 | QQuickView * window = windowPtr.data(); |
488 | |
489 | QQuickItem *rect1 = window->rootObject()->findChild<QQuickItem*>(aName: subTree); |
490 | QVERIFY(rect1); |
491 | QQuickItem *rect1b = rect1->childItems().first(); |
492 | QVERIFY(rect1b); |
493 | QQuickDragHandler *dragHandler1 = rect1->findChild<QQuickDragHandler*>(); |
494 | QVERIFY(dragHandler1); |
495 | dragHandler1->setSnapMode((QQuickDragHandler::SnapMode)snapMode); |
496 | QQuickItem *dragTarget = dragHandler1->target(); |
497 | QPointF oldTargetPos = dragTarget->position(); |
498 | |
499 | QPoint p1 = rect1->mapToScene(point: QPointF(startDragPos)).toPoint(); |
500 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
501 | QVERIFY(!dragHandler1->active()); |
502 | p1 += dragMovement; |
503 | QTest::mouseMove(window, pos: p1); |
504 | QTRY_VERIFY(dragHandler1->active()); |
505 | QCOMPARE(dragTarget->position(), oldTargetPos + expectedMovement); |
506 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
507 | QTRY_VERIFY(!dragHandler1->active()); |
508 | QCOMPARE(dragHandler1->centroid().pressedButtons(), Qt::NoButton); |
509 | } |
510 | |
511 | void tst_DragHandler::touchDragMulti() |
512 | { |
513 | const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); |
514 | QScopedPointer<QQuickView> windowPtr; |
515 | createView(window&: windowPtr, fileName: "draggables.qml" ); |
516 | QQuickView * window = windowPtr.data(); |
517 | |
518 | QQuickItem *ball1 = window->rootObject()->childItems().first(); |
519 | QVERIFY(ball1); |
520 | QQuickDragHandler *dragHandler1 = ball1->findChild<QQuickDragHandler*>(); |
521 | QVERIFY(dragHandler1); |
522 | QSignalSpy translationChangedSpy1(dragHandler1, SIGNAL(translationChanged())); |
523 | QSignalSpy centroidChangedSpy1(dragHandler1, SIGNAL(centroidChanged())); |
524 | |
525 | QQuickItem *ball2 = window->rootObject()->childItems().at(i: 1); |
526 | QVERIFY(ball2); |
527 | QQuickDragHandler *dragHandler2 = ball2->findChild<QQuickDragHandler*>(); |
528 | QVERIFY(dragHandler2); |
529 | QSignalSpy translationChangedSpy2(dragHandler2, SIGNAL(translationChanged())); |
530 | QSignalSpy centroidChangedSpy2(dragHandler1, SIGNAL(centroidChanged())); |
531 | |
532 | QPointF ball1Center = ball1->clipRect().center(); |
533 | QPointF scenePressPos1 = ball1->mapToScene(point: ball1Center); |
534 | QPoint p1 = scenePressPos1.toPoint(); |
535 | QPointF ball2Center = ball2->clipRect().center(); |
536 | QPointF scenePressPos2 = ball2->mapToScene(point: ball2Center); |
537 | QPoint p2 = scenePressPos2.toPoint(); |
538 | QTest::QTouchEventSequence touchSeq = QTest::touchEvent(window, device: touchDevice, autoCommit: false); |
539 | |
540 | touchSeq.press(touchId: 1, pt: p1, window).press(touchId: 2, pt: p2, window).commit(); |
541 | QQuickTouchUtils::flush(window); |
542 | QVERIFY(!dragHandler1->active()); |
543 | QCOMPARE(centroidChangedSpy1.count(), 1); |
544 | QCOMPARE(dragHandler1->centroid().position(), ball1Center); |
545 | QCOMPARE(dragHandler1->centroid().pressPosition(), ball1Center); |
546 | QCOMPARE(dragHandler1->centroid().scenePosition(), scenePressPos1); |
547 | QCOMPARE(dragHandler1->centroid().scenePressPosition(), scenePressPos1); |
548 | QVERIFY(!dragHandler2->active()); |
549 | QCOMPARE(centroidChangedSpy2.count(), 1); |
550 | QCOMPARE(dragHandler2->centroid().position(), ball2Center); |
551 | QCOMPARE(dragHandler2->centroid().pressPosition(), ball2Center); |
552 | QCOMPARE(dragHandler2->centroid().scenePosition(), scenePressPos2); |
553 | QCOMPARE(dragHandler2->centroid().scenePressPosition(), scenePressPos2); |
554 | p1 += QPoint(dragThreshold, 0); |
555 | p2 += QPoint(0, dragThreshold); |
556 | touchSeq.move(touchId: 1, pt: p1, window).move(touchId: 2, pt: p2, window).commit(); |
557 | QQuickTouchUtils::flush(window); |
558 | QVERIFY(!dragHandler1->active()); |
559 | QCOMPARE(centroidChangedSpy1.count(), 2); |
560 | QCOMPARE(dragHandler1->centroid().position(), ball1Center + QPointF(dragThreshold, 0)); |
561 | QCOMPARE(dragHandler1->centroid().pressPosition(), ball1Center); |
562 | QCOMPARE(dragHandler1->centroid().scenePosition().toPoint(), p1); |
563 | QCOMPARE(dragHandler1->centroid().scenePressPosition(), scenePressPos1); |
564 | QVERIFY(!dragHandler2->active()); |
565 | QCOMPARE(centroidChangedSpy2.count(), 2); |
566 | QCOMPARE(dragHandler2->centroid().position(), ball2Center + QPointF(0, dragThreshold)); |
567 | QCOMPARE(dragHandler2->centroid().pressPosition(), ball2Center); |
568 | QCOMPARE(dragHandler2->centroid().scenePosition().toPoint(), p2); |
569 | QCOMPARE(dragHandler2->centroid().scenePressPosition(), scenePressPos2); |
570 | p1 += QPoint(1, 0); |
571 | p2 += QPoint(0, 1); |
572 | touchSeq.move(touchId: 1, pt: p1, window).move(touchId: 2, pt: p2, window).commit(); |
573 | QQuickTouchUtils::flush(window); |
574 | QTRY_VERIFY(dragHandler1->active()); |
575 | QVERIFY(dragHandler2->active()); |
576 | QCOMPARE(translationChangedSpy1.count(), 0); |
577 | QCOMPARE(dragHandler1->translation().x(), 0.0); |
578 | QPointF sceneGrabPos1 = p1; |
579 | QPointF sceneGrabPos2 = p2; |
580 | QCOMPARE(dragHandler1->centroid().sceneGrabPosition(), sceneGrabPos1); |
581 | QCOMPARE(dragHandler2->centroid().sceneGrabPosition(), sceneGrabPos2); |
582 | p1 += QPoint(19, 0); |
583 | p2 += QPoint(0, 19); |
584 | QVERIFY(dragHandler2->active()); |
585 | QCOMPARE(translationChangedSpy2.count(), 0); |
586 | QCOMPARE(dragHandler2->translation().x(), 0.0); |
587 | QCOMPARE(dragHandler2->centroid().sceneGrabPosition(), sceneGrabPos2); |
588 | touchSeq.move(touchId: 1, pt: p1, window).move(touchId: 2, pt: p2, window).commit(); |
589 | QQuickTouchUtils::flush(window); |
590 | QVERIFY(dragHandler1->active()); |
591 | QVERIFY(dragHandler2->active()); |
592 | QCOMPARE(dragHandler1->centroid().position(), ball1Center); |
593 | QCOMPARE(dragHandler1->centroid().pressPosition(), ball1Center); |
594 | QCOMPARE(dragHandler1->centroid().scenePosition(), ball1->mapToScene(ball1Center)); |
595 | QCOMPARE(dragHandler1->centroid().scenePressPosition(), scenePressPos1); |
596 | QCOMPARE(dragHandler1->centroid().sceneGrabPosition(), sceneGrabPos1); |
597 | QCOMPARE(dragHandler1->translation().x(), dragThreshold + 20.0); |
598 | QCOMPARE(dragHandler1->translation().y(), 0.0); |
599 | QCOMPARE(dragHandler2->centroid().position(), ball2Center); |
600 | QCOMPARE(dragHandler2->centroid().pressPosition(), ball2Center); |
601 | QCOMPARE(dragHandler2->centroid().scenePosition(), ball2->mapToScene(ball2Center)); |
602 | QCOMPARE(dragHandler2->centroid().scenePressPosition(), scenePressPos2); |
603 | QCOMPARE(dragHandler2->centroid().sceneGrabPosition(), sceneGrabPos2); |
604 | QCOMPARE(dragHandler2->translation().x(), 0.0); |
605 | QCOMPARE(dragHandler2->translation().y(), dragThreshold + 20.0); |
606 | touchSeq.release(touchId: 1, pt: p1, window).stationary(touchId: 2).commit(); |
607 | QQuickTouchUtils::flush(window); |
608 | QTRY_VERIFY(!dragHandler1->active()); |
609 | QVERIFY(dragHandler2->active()); |
610 | QCOMPARE(dragHandler1->centroid().pressedButtons(), Qt::NoButton); |
611 | QCOMPARE(ball1->mapToScene(ball1Center).toPoint(), p1); |
612 | QCOMPARE(translationChangedSpy1.count(), 1); |
613 | touchSeq.release(touchId: 2, pt: p2, window).commit(); |
614 | QQuickTouchUtils::flush(window); |
615 | QTRY_VERIFY(!dragHandler2->active()); |
616 | QCOMPARE(ball2->mapToScene(ball2Center).toPoint(), p2); |
617 | QCOMPARE(translationChangedSpy2.count(), 1); |
618 | } |
619 | |
620 | void tst_DragHandler::touchDragMultiSliders_data() |
621 | { |
622 | QTest::addColumn<int>(name: "sliderRow" ); |
623 | QTest::addColumn<QVector<int> >(name: "whichSliders" ); |
624 | QTest::addColumn<QVector<int> >(name: "startingCenterOffsets" ); |
625 | QTest::addColumn<QVector<QVector2D> >(name: "movements" ); |
626 | |
627 | QTest::newRow(dataTag: "Drag Knob: start on the knobs, drag down" ) << |
628 | 0 << QVector<int> { 0, 1, 2 } << QVector<int> { 0, 0, 0 } << QVector<QVector2D> { {0, 60}, {0, 60}, {0, 60} }; |
629 | QTest::newRow(dataTag: "Drag Knob: start on the knobs, drag diagonally downward" ) << |
630 | 0 << QVector<int> { 0, 1, 2 } << QVector<int> { 0, 0, 0 } << QVector<QVector2D> { {20, 40}, {20, 60}, {20, 80} }; |
631 | QTest::newRow(dataTag: "Drag Anywhere: start on the knobs, drag down" ) << |
632 | 1 << QVector<int> { 0, 1, 2 } << QVector<int> { 0, 0, 0 } << QVector<QVector2D> { {0, 60}, {0, 60}, {0, 60} }; |
633 | QTest::newRow(dataTag: "Drag Anywhere: start on the knobs, drag diagonally downward" ) << |
634 | 1 << QVector<int> { 0, 1, 2 } << QVector<int> { 0, 0, 0 } << QVector<QVector2D> { {20, 40}, {20, 60}, {20, 80} }; |
635 | // TODO these next two fail because the DragHandler grabs when a finger |
636 | // drags across it from outside, but should rather start only if it is pressed inside |
637 | // QTest::newRow("Drag Knob: start above the knobs, drag down") << |
638 | // 0 << QVector<int> { 0, 1, 2 } << QVector<int> { -30, -30, -30 } << QVector<QVector2D> { {0, 40}, {0, 60}, {0, 80} }; |
639 | // QTest::newRow("Drag Knob: start above the knobs, drag diagonally downward") << |
640 | // 0 << QVector<int> { 0, 1, 2 } << QVector<int> { -30, -30, -30 } << QVector<QVector2D> { {20, 40}, {20, 60}, {20, 80} }; |
641 | QTest::newRow(dataTag: "Drag Anywhere: start above the knobs, drag down" ) << |
642 | 1 << QVector<int> { 0, 1, 2 } << QVector<int> { -20, -30, -40 } << QVector<QVector2D> { {0, 60}, {0, 60}, {0, 60} }; |
643 | QTest::newRow(dataTag: "Drag Anywhere: start above the knobs, drag diagonally downward" ) << |
644 | 1 << QVector<int> { 0, 1, 2 } << QVector<int> { -20, -30, -40 } << QVector<QVector2D> { {20, 40}, {20, 60}, {20, 80} }; |
645 | } |
646 | |
647 | void tst_DragHandler::touchDragMultiSliders() |
648 | { |
649 | QFETCH(int, sliderRow); |
650 | QFETCH(QVector<int>, whichSliders); |
651 | QFETCH(QVector<int>, startingCenterOffsets); |
652 | QFETCH(QVector<QVector2D>, movements); |
653 | const int moveCount = 8; |
654 | |
655 | QScopedPointer<QQuickView> windowPtr; |
656 | createView(window&: windowPtr, fileName: "multipleSliders.qml" ); |
657 | QQuickView * window = windowPtr.data(); |
658 | QTest::QTouchEventSequence touch = QTest::touchEvent(window, device: touchDevice); |
659 | |
660 | QQuickRepeater *rowRepeater = window->rootObject()->findChildren<QQuickRepeater *>()[sliderRow]; |
661 | QVector<QQuickItem *> knobs; |
662 | QVector<QQuickDragHandler *> dragHandlers; |
663 | QVector<QQuickTapHandler *> tapHandlers; |
664 | QVector<QPointF> startPoints; |
665 | for (int sli : whichSliders) { |
666 | QQuickItem *slider = rowRepeater->itemAt(index: sli); |
667 | QVERIFY(slider); |
668 | dragHandlers << slider->findChild<QQuickDragHandler*>(); |
669 | QVERIFY(dragHandlers[sli]); |
670 | tapHandlers << slider->findChild<QQuickTapHandler*>(); |
671 | QVERIFY(tapHandlers[sli]); |
672 | knobs << tapHandlers[sli]->parentItem(); |
673 | QPointF startPoint = knobs[sli]->mapToScene(point: knobs[sli]->clipRect().center()); |
674 | startPoint.setY(startPoint.y() + startingCenterOffsets[sli]); |
675 | startPoints << startPoint; |
676 | qCDebug(lcPointerTests) << "row" << sliderRow << "slider" << sli << slider->objectName() << |
677 | "start" << startingCenterOffsets[sli] << startPoints[sli]; |
678 | } |
679 | QVector<QPointF> touchPoints = startPoints; |
680 | |
681 | // Press |
682 | for (int sli : whichSliders) |
683 | touch.press(touchId: sli, pt: touchPoints[sli].toPoint()); |
684 | touch.commit(); |
685 | |
686 | // Moves |
687 | for (int m = 0; m < moveCount; ++m) { |
688 | for (int sli : whichSliders) { |
689 | QVector2D incr = movements[sli] / moveCount; |
690 | touchPoints[sli] += incr.toPointF(); |
691 | touch.move(touchId: sli, pt: touchPoints[sli].toPoint()); |
692 | } |
693 | touch.commit(); |
694 | QQuickTouchUtils::flush(window); |
695 | } |
696 | |
697 | // Check that they moved to where they should: since the slider is constrained, |
698 | // only the y component should have an effect; knobs should not come out of their "grooves" |
699 | for (int sli : whichSliders) { |
700 | QPoint endPosition = knobs[sli]->mapToScene(point: knobs[sli]->clipRect().center()).toPoint(); |
701 | QPoint expectedEndPosition(startPoints[sli].x(), startPoints[sli].y() + movements[sli].y()); |
702 | if (sliderRow == 0 && qAbs(t: startingCenterOffsets[sli]) > knobs[sli]->height() / 2) |
703 | expectedEndPosition = startPoints[sli].toPoint(); |
704 | qCDebug(lcPointerTests) << "slider " << knobs[sli]->objectName() << "started @" << startPoints[sli] |
705 | << "tried to move by" << movements[sli] << "ended up @" << endPosition << "expected" << expectedEndPosition; |
706 | QTRY_COMPARE(endPosition, expectedEndPosition); |
707 | } |
708 | |
709 | // Release |
710 | for (int sli : whichSliders) |
711 | touch.release(touchId: sli, pt: touchPoints[sli].toPoint()); |
712 | touch.commit(); |
713 | } |
714 | |
715 | void tst_DragHandler::touchPassiveGrabbers_data() |
716 | { |
717 | QTest::addColumn<QString>(name: "itemName" ); |
718 | QTest::addColumn<QStringList>(name: "expectedPassiveGrabberNames" ); |
719 | |
720 | QTest::newRow(dataTag: "Drag And Tap" ) << "dragAndTap" << QStringList({"drag" , "tap" }); |
721 | QTest::newRow(dataTag: "Tap And Drag" ) << "tapAndDrag" << QStringList({"tap" , "drag" }); |
722 | QTest::newRow(dataTag: "Drag And Tap (not siblings)" ) << "dragAndTapNotSiblings" << QStringList({"drag" , "tap" }); |
723 | QTest::newRow(dataTag: "Tap And Drag (not siblings)" ) << "tapAndDragNotSiblings" << QStringList({"tap" , "drag" }); |
724 | } |
725 | |
726 | void tst_DragHandler::touchPassiveGrabbers() |
727 | { |
728 | QFETCH(QString, itemName); |
729 | QFETCH(QStringList, expectedPassiveGrabberNames); |
730 | |
731 | QScopedPointer<QQuickView> windowPtr; |
732 | createView(window&: windowPtr, fileName: "simpleTapAndDragHandlers.qml" ); |
733 | QQuickView * window = windowPtr.data(); |
734 | |
735 | QQuickItem *row2 = window->rootObject()->findChild<QQuickItem*>(aName: itemName); |
736 | QSet<QQuickPointerHandler *> expectedPassiveGrabbers; |
737 | for (QString objectName : expectedPassiveGrabberNames) |
738 | expectedPassiveGrabbers << row2->findChild<QQuickPointerHandler*>(aName: objectName); |
739 | |
740 | QPointF p1 = row2->mapToScene(point: row2->clipRect().center()); |
741 | QTest::QTouchEventSequence touch = QTest::touchEvent(window, device: touchDevice); |
742 | touch.press(touchId: 1, pt: p1.toPoint()).commit(); |
743 | QQuickTouchUtils::flush(window); |
744 | |
745 | QCOMPARE(passiveGrabbers(window), expectedPassiveGrabbers); |
746 | |
747 | QQuickDragHandler *dragHandler = nullptr; |
748 | for (QQuickPointerHandler *handler: expectedPassiveGrabbers) { |
749 | QPointF scenePressPos; |
750 | if (QQuickMultiPointHandler *mph = qmlobject_cast<QQuickMultiPointHandler *>(object: handler)) |
751 | scenePressPos = mph->centroid().scenePressPosition(); |
752 | else |
753 | scenePressPos = static_cast<QQuickSinglePointHandler *>(handler)->point().scenePressPosition(); |
754 | QCOMPARE(scenePressPos, p1); |
755 | QQuickDragHandler *dh = qmlobject_cast<QQuickDragHandler *>(object: handler); |
756 | if (dh) |
757 | dragHandler = dh; |
758 | } |
759 | QVERIFY(dragHandler); |
760 | QPointF initialPos = dragHandler->target()->position(); |
761 | |
762 | p1 += QPointF(50, 50); |
763 | touch.move(touchId: 1, pt: p1.toPoint()).commit(); |
764 | QQuickTouchUtils::flush(window); |
765 | QTRY_VERIFY(dragHandler->active()); |
766 | |
767 | p1 += QPointF(50, 50); |
768 | touch.move(touchId: 1, pt: p1.toPoint()).commit(); |
769 | QQuickTouchUtils::flush(window); |
770 | QPointF movementDelta = dragHandler->target()->position() - initialPos; |
771 | qCDebug(lcPointerTests) << "DragHandler moved the target by" << movementDelta; |
772 | QVERIFY(movementDelta.x() >= 100); |
773 | QVERIFY(movementDelta.y() >= 100); |
774 | |
775 | QTest::qWait(ms: 500); |
776 | |
777 | touch.release(touchId: 1, pt: p1.toPoint()); |
778 | touch.commit(); |
779 | QQuickTouchUtils::flush(window); |
780 | } |
781 | |
782 | void tst_DragHandler::touchPinchAndMouseMove() |
783 | { |
784 | QScopedPointer<QQuickView> windowPtr; |
785 | createView(window&: windowPtr, fileName: "draghandler_and_pinchhandler.qml" ); |
786 | QQuickView *window = windowPtr.data(); |
787 | QQuickItem *rect = window->rootObject()->findChild<QQuickItem*>(aName: QLatin1String("Rect" )); |
788 | QQuickPointerHandler *pinchHandler = window->rootObject()->findChild<QQuickPointerHandler*>(aName: QLatin1String("PinchHandler" )); |
789 | |
790 | QPoint p1(150,200); |
791 | QPoint p2(250,200); |
792 | |
793 | // Trigger a scale pinch, PinchHandler should activate |
794 | QTest::QTouchEventSequence touch = QTest::touchEvent(window, device: touchDevice); |
795 | touch.press(touchId: 1, pt: p1).press(touchId: 2, pt: p2).commit(); |
796 | QQuickTouchUtils::flush(window); |
797 | QPoint delta(10,0); |
798 | for (int i = 0; i < 10 && !pinchHandler->active(); ++i) { |
799 | p1-=delta; |
800 | p2+=delta; |
801 | touch.move(touchId: 1, pt: p1).move(touchId: 2, pt: p2).commit(); |
802 | QQuickTouchUtils::flush(window); |
803 | } |
804 | QCOMPARE(pinchHandler->active(), true); |
805 | |
806 | // While having the touch points pressed, send wrong mouse event as MS Windows did: |
807 | // * A MoveMove with LeftButton down |
808 | // (in order to synthesize that, qtestMouseButtons needs to be modified) |
809 | // (This will make the DragHandler do a passive grab) |
810 | QTestPrivate::qtestMouseButtons = Qt::LeftButton; |
811 | QTest::mouseMove(window, pos: p1 + delta); |
812 | |
813 | touch.release(touchId: 1, pt: p1).release(touchId: 2, pt: p2).commit(); |
814 | QQuickTouchUtils::flush(window); |
815 | |
816 | // Now move the mouse with no buttons down and check if the rect did not move |
817 | // At this point, no touch points are pressed and no mouse buttons are pressed. |
818 | QTestPrivate::qtestMouseButtons = Qt::NoButton; |
819 | QSignalSpy rectMovedSpy(rect, SIGNAL(xChanged())); |
820 | for (int i = 0; i < 10; ++i) { |
821 | p1 += delta; |
822 | QTest::mouseMove(window, pos: p1); |
823 | QCOMPARE(rectMovedSpy.count(), 0); |
824 | } |
825 | } |
826 | |
827 | class ModalLayer : public QQuickItem { |
828 | public: |
829 | explicit ModalLayer(QQuickItem* parent = nullptr) : QQuickItem(parent) { |
830 | this->setAcceptedMouseButtons(Qt::AllButtons); |
831 | this->setAcceptTouchEvents(true); |
832 | this->setKeepMouseGrab(true); |
833 | this->setKeepTouchGrab(true); |
834 | } |
835 | |
836 | bool event(QEvent* event) override { |
837 | switch (event->type()) { |
838 | case QEvent::KeyPress: |
839 | case QEvent::MouseMove: |
840 | case QEvent::MouseButtonPress: |
841 | case QEvent::MouseButtonRelease: |
842 | case QEvent::MouseTrackingChange: |
843 | case QEvent::MouseButtonDblClick: |
844 | case QEvent::Wheel: |
845 | case QEvent::TouchBegin: |
846 | case QEvent::TouchUpdate: |
847 | case QEvent::TouchCancel: |
848 | case QEvent::TouchEnd: { |
849 | qCDebug(lcPointerTests) << "BLOCK!" << event->type(); |
850 | return true; |
851 | } |
852 | default: break; |
853 | } |
854 | return QQuickItem::event(event); |
855 | } |
856 | }; |
857 | |
858 | void tst_DragHandler::underModalLayer() // QTBUG-78258 |
859 | { |
860 | qmlRegisterType<ModalLayer>(uri: "Test" , versionMajor: 1, versionMinor: 0, qmlName: "ModalLayer" ); |
861 | |
862 | const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); |
863 | QScopedPointer<QQuickView> windowPtr; |
864 | createView(window&: windowPtr, fileName: "dragHandlerUnderModalLayer.qml" ); |
865 | QQuickView * window = windowPtr.data(); |
866 | QPointer<QQuickDragHandler> dragHandler = window->rootObject()->findChild<QQuickDragHandler*>(); |
867 | QVERIFY(dragHandler); |
868 | |
869 | QPoint p1(250, 250); |
870 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
871 | p1 += QPoint(dragThreshold, dragThreshold); |
872 | QTest::mouseMove(window, pos: p1); |
873 | QVERIFY(!dragHandler->active()); |
874 | p1 += QPoint(dragThreshold, dragThreshold); |
875 | QTest::mouseMove(window, pos: p1); |
876 | QVERIFY(!dragHandler->active()); |
877 | QTest::mouseRelease(window, button: Qt::LeftButton); |
878 | } |
879 | |
880 | QTEST_MAIN(tst_DragHandler) |
881 | |
882 | #include "tst_qquickdraghandler.moc" |
883 | |
884 | |