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
42Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests")
43
44class tst_DragHandler : public QQmlDataTest
45{
46 Q_OBJECT
47public:
48 tst_DragHandler()
49 :touchDevice(QTest::createTouchDevice())
50 {}
51
52private 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
73private:
74 void createView(QScopedPointer<QQuickView> &window, const char *fileName);
75 QSet<QQuickPointerHandler *> passiveGrabbers(QQuickWindow *window, int pointId = 0);
76 QTouchDevice *touchDevice;
77};
78
79void 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
92QSet<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
110void 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
118void 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
138void 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
147void 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
220void 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
230void 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
319void 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
328void 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
395void 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
439void 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 menum = 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
477void 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
511void 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
620void 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
647void 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
715void 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
726void 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
782void 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
827class ModalLayer : public QQuickItem {
828public:
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
858void 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
880QTEST_MAIN(tst_DragHandler)
881
882#include "tst_qquickdraghandler.moc"
883
884

source code of qtdeclarative/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp