1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 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 | |
30 | #include <QtTest/QtTest> |
31 | |
32 | #include <QtGui/qstylehints.h> |
33 | #include <QtQuick/qquickview.h> |
34 | #include <QtQuick/qquickitem.h> |
35 | #include <QtQuick/private/qquickpointerhandler_p.h> |
36 | #include <QtQuick/private/qquicktaphandler_p.h> |
37 | #include <qpa/qwindowsysteminterface.h> |
38 | |
39 | #include <private/qquickwindow_p.h> |
40 | |
41 | #include <QtQml/qqmlengine.h> |
42 | #include <QtQml/qqmlproperty.h> |
43 | |
44 | #include "../../../shared/util.h" |
45 | #include "../../shared/viewtestutil.h" |
46 | |
47 | Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests" ) |
48 | |
49 | class tst_TapHandler : public QQmlDataTest |
50 | { |
51 | Q_OBJECT |
52 | public: |
53 | tst_TapHandler() |
54 | :touchDevice(QTest::createTouchDevice()) |
55 | {} |
56 | |
57 | private slots: |
58 | void initTestCase(); |
59 | |
60 | void touchGesturePolicyDragThreshold(); |
61 | void mouseGesturePolicyDragThreshold(); |
62 | void touchMouseGesturePolicyDragThreshold(); |
63 | void touchGesturePolicyWithinBounds(); |
64 | void mouseGesturePolicyWithinBounds(); |
65 | void touchGesturePolicyReleaseWithinBounds(); |
66 | void mouseGesturePolicyReleaseWithinBounds(); |
67 | void touchMultiTap(); |
68 | void mouseMultiTap(); |
69 | void touchLongPress(); |
70 | void mouseLongPress(); |
71 | void buttonsMultiTouch(); |
72 | void componentUserBehavioralOverride(); |
73 | void rightLongPressIgnoreWheel(); |
74 | void nonTopLevelParentWindow(); |
75 | |
76 | private: |
77 | void createView(QScopedPointer<QQuickView> &window, const char *fileName, |
78 | QWindow *parent = nullptr); |
79 | QTouchDevice *touchDevice; |
80 | void mouseEvent(QEvent::Type type, Qt::MouseButton button, const QPoint &point, |
81 | QWindow *targetWindow, QWindow *mapToWindow); |
82 | }; |
83 | |
84 | void tst_TapHandler::createView(QScopedPointer<QQuickView> &window, const char *fileName, |
85 | QWindow *parent) |
86 | { |
87 | window.reset(other: new QQuickView(parent)); |
88 | if (parent) { |
89 | parent->show(); |
90 | QVERIFY(QTest::qWaitForWindowActive(parent)); |
91 | } |
92 | |
93 | window->setSource(testFileUrl(fileName)); |
94 | QTRY_COMPARE(window->status(), QQuickView::Ready); |
95 | QQuickViewTestUtil::centerOnScreen(window: window.data()); |
96 | QQuickViewTestUtil::moveMouseAway(window: window.data()); |
97 | |
98 | window->show(); |
99 | QVERIFY(QTest::qWaitForWindowActive(window.data())); |
100 | QVERIFY(window->rootObject() != nullptr); |
101 | } |
102 | |
103 | void tst_TapHandler::mouseEvent(QEvent::Type type, Qt::MouseButton button, const QPoint &point, |
104 | QWindow *targetWindow, QWindow *mapToWindow) |
105 | { |
106 | QVERIFY(targetWindow); |
107 | QVERIFY(mapToWindow); |
108 | auto buttons = button; |
109 | if (type == QEvent::MouseButtonRelease) { |
110 | buttons = Qt::NoButton; |
111 | } |
112 | QMouseEvent me(type, point, mapToWindow->mapToGlobal(pos: point), button, buttons, |
113 | Qt::KeyboardModifiers()); |
114 | QVERIFY(qApp->notify(targetWindow, &me)); |
115 | } |
116 | |
117 | void tst_TapHandler::initTestCase() |
118 | { |
119 | // This test assumes that we don't get synthesized mouse events from QGuiApplication |
120 | qApp->setAttribute(attribute: Qt::AA_SynthesizeMouseForUnhandledTouchEvents, on: false); |
121 | |
122 | QQmlDataTest::initTestCase(); |
123 | } |
124 | |
125 | void tst_TapHandler::touchGesturePolicyDragThreshold() |
126 | { |
127 | const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); |
128 | QScopedPointer<QQuickView> windowPtr; |
129 | createView(window&: windowPtr, fileName: "buttons.qml" ); |
130 | QQuickView * window = windowPtr.data(); |
131 | |
132 | QQuickItem *buttonDragThreshold = window->rootObject()->findChild<QQuickItem*>(aName: "DragThreshold" ); |
133 | QVERIFY(buttonDragThreshold); |
134 | QQuickTapHandler *tapHandler = buttonDragThreshold->findChild<QQuickTapHandler*>(); |
135 | QVERIFY(tapHandler); |
136 | QSignalSpy dragThresholdTappedSpy(buttonDragThreshold, SIGNAL(tapped())); |
137 | |
138 | // DragThreshold button stays pressed while touchpoint stays within dragThreshold, emits tapped on release |
139 | QPoint p1 = buttonDragThreshold->mapToScene(point: QPointF(20, 20)).toPoint(); |
140 | QTest::touchEvent(window, device: touchDevice).press(touchId: 1, pt: p1, window); |
141 | QQuickTouchUtils::flush(window); |
142 | QTRY_VERIFY(buttonDragThreshold->property("pressed" ).toBool()); |
143 | p1 += QPoint(dragThreshold, 0); |
144 | QTest::touchEvent(window, device: touchDevice).move(touchId: 1, pt: p1, window); |
145 | QQuickTouchUtils::flush(window); |
146 | QVERIFY(buttonDragThreshold->property("pressed" ).toBool()); |
147 | QTest::touchEvent(window, device: touchDevice).release(touchId: 1, pt: p1, window); |
148 | QQuickTouchUtils::flush(window); |
149 | QTRY_VERIFY(!buttonDragThreshold->property("pressed" ).toBool()); |
150 | QCOMPARE(dragThresholdTappedSpy.count(), 1); |
151 | QCOMPARE(buttonDragThreshold->property("tappedPosition" ).toPoint(), p1); |
152 | QCOMPARE(tapHandler->point().position(), QPointF()); |
153 | |
154 | // DragThreshold button is no longer pressed if touchpoint goes beyond dragThreshold |
155 | dragThresholdTappedSpy.clear(); |
156 | p1 = buttonDragThreshold->mapToScene(point: QPointF(20, 20)).toPoint(); |
157 | QTest::touchEvent(window, device: touchDevice).press(touchId: 1, pt: p1, window); |
158 | QQuickTouchUtils::flush(window); |
159 | QTRY_VERIFY(buttonDragThreshold->property("pressed" ).toBool()); |
160 | p1 += QPoint(dragThreshold, 0); |
161 | QTest::touchEvent(window, device: touchDevice).move(touchId: 1, pt: p1, window); |
162 | QQuickTouchUtils::flush(window); |
163 | QVERIFY(buttonDragThreshold->property("pressed" ).toBool()); |
164 | p1 += QPoint(1, 0); |
165 | QTest::touchEvent(window, device: touchDevice).move(touchId: 1, pt: p1, window); |
166 | QQuickTouchUtils::flush(window); |
167 | QTRY_VERIFY(!buttonDragThreshold->property("pressed" ).toBool()); |
168 | QTest::touchEvent(window, device: touchDevice).release(touchId: 1, pt: p1, window); |
169 | QQuickTouchUtils::flush(window); |
170 | QVERIFY(!buttonDragThreshold->property("pressed" ).toBool()); |
171 | QCOMPARE(dragThresholdTappedSpy.count(), 0); |
172 | } |
173 | |
174 | void tst_TapHandler::mouseGesturePolicyDragThreshold() |
175 | { |
176 | const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); |
177 | QScopedPointer<QQuickView> windowPtr; |
178 | createView(window&: windowPtr, fileName: "buttons.qml" ); |
179 | QQuickView * window = windowPtr.data(); |
180 | |
181 | QQuickItem *buttonDragThreshold = window->rootObject()->findChild<QQuickItem*>(aName: "DragThreshold" ); |
182 | QVERIFY(buttonDragThreshold); |
183 | QQuickTapHandler *tapHandler = buttonDragThreshold->findChild<QQuickTapHandler*>(); |
184 | QVERIFY(tapHandler); |
185 | QSignalSpy dragThresholdTappedSpy(buttonDragThreshold, SIGNAL(tapped())); |
186 | |
187 | // DragThreshold button stays pressed while mouse stays within dragThreshold, emits tapped on release |
188 | QPoint p1 = buttonDragThreshold->mapToScene(point: QPointF(20, 20)).toPoint(); |
189 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
190 | QTRY_VERIFY(buttonDragThreshold->property("pressed" ).toBool()); |
191 | p1 += QPoint(dragThreshold, 0); |
192 | QTest::mouseMove(window, pos: p1); |
193 | QVERIFY(buttonDragThreshold->property("pressed" ).toBool()); |
194 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
195 | QTRY_VERIFY(!buttonDragThreshold->property("pressed" ).toBool()); |
196 | QTRY_COMPARE(dragThresholdTappedSpy.count(), 1); |
197 | QCOMPARE(buttonDragThreshold->property("tappedPosition" ).toPoint(), p1); |
198 | QCOMPARE(tapHandler->point().position(), QPointF()); |
199 | |
200 | // DragThreshold button is no longer pressed if mouse goes beyond dragThreshold |
201 | dragThresholdTappedSpy.clear(); |
202 | p1 = buttonDragThreshold->mapToScene(point: QPointF(20, 20)).toPoint(); |
203 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
204 | QTRY_VERIFY(buttonDragThreshold->property("pressed" ).toBool()); |
205 | p1 += QPoint(dragThreshold, 0); |
206 | QTest::mouseMove(window, pos: p1); |
207 | QVERIFY(buttonDragThreshold->property("pressed" ).toBool()); |
208 | p1 += QPoint(1, 0); |
209 | QTest::mouseMove(window, pos: p1); |
210 | QTRY_VERIFY(!buttonDragThreshold->property("pressed" ).toBool()); |
211 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
212 | QVERIFY(!buttonDragThreshold->property("pressed" ).toBool()); |
213 | QCOMPARE(dragThresholdTappedSpy.count(), 0); |
214 | } |
215 | |
216 | void tst_TapHandler::touchMouseGesturePolicyDragThreshold() |
217 | { |
218 | QScopedPointer<QQuickView> windowPtr; |
219 | createView(window&: windowPtr, fileName: "buttons.qml" ); |
220 | QQuickView * window = windowPtr.data(); |
221 | |
222 | QQuickItem *buttonDragThreshold = window->rootObject()->findChild<QQuickItem*>(aName: "DragThreshold" ); |
223 | QVERIFY(buttonDragThreshold); |
224 | QSignalSpy tappedSpy(buttonDragThreshold, SIGNAL(tapped())); |
225 | QSignalSpy canceledSpy(buttonDragThreshold, SIGNAL(canceled())); |
226 | |
227 | // Press mouse, drag it outside the button, release |
228 | QPoint p1 = buttonDragThreshold->mapToScene(point: QPointF(20, 20)).toPoint(); |
229 | QPoint p2 = p1 + QPoint(int(buttonDragThreshold->height()), 0); |
230 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
231 | QTRY_VERIFY(buttonDragThreshold->property("pressed" ).toBool()); |
232 | QTest::mouseMove(window, pos: p2); |
233 | QTRY_COMPARE(canceledSpy.count(), 1); |
234 | QCOMPARE(tappedSpy.count(), 0); |
235 | QCOMPARE(buttonDragThreshold->property("pressed" ).toBool(), false); |
236 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p2); |
237 | |
238 | // Press and release touch, verify that it still works (QTBUG-71466) |
239 | QTest::touchEvent(window, device: touchDevice).press(touchId: 1, pt: p1, window); |
240 | QQuickTouchUtils::flush(window); |
241 | QTRY_VERIFY(buttonDragThreshold->property("pressed" ).toBool()); |
242 | QTest::touchEvent(window, device: touchDevice).release(touchId: 1, pt: p1, window); |
243 | QQuickTouchUtils::flush(window); |
244 | QTRY_VERIFY(!buttonDragThreshold->property("pressed" ).toBool()); |
245 | QCOMPARE(tappedSpy.count(), 1); |
246 | |
247 | // Press touch, drag it outside the button, release |
248 | QTest::touchEvent(window, device: touchDevice).press(touchId: 1, pt: p1, window); |
249 | QQuickTouchUtils::flush(window); |
250 | QTRY_VERIFY(buttonDragThreshold->property("pressed" ).toBool()); |
251 | QTest::touchEvent(window, device: touchDevice).move(touchId: 1, pt: p2, window); |
252 | QQuickTouchUtils::flush(window); |
253 | QTRY_COMPARE(buttonDragThreshold->property("pressed" ).toBool(), false); |
254 | QTest::touchEvent(window, device: touchDevice).release(touchId: 1, pt: p2, window); |
255 | QQuickTouchUtils::flush(window); |
256 | QTRY_COMPARE(canceledSpy.count(), 2); |
257 | QCOMPARE(tappedSpy.count(), 1); // didn't increase |
258 | QCOMPARE(buttonDragThreshold->property("pressed" ).toBool(), false); |
259 | |
260 | // Press and release mouse, verify that it still works |
261 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
262 | QTRY_VERIFY(buttonDragThreshold->property("pressed" ).toBool()); |
263 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
264 | QTRY_COMPARE(tappedSpy.count(), 2); |
265 | QCOMPARE(canceledSpy.count(), 2); // didn't increase |
266 | QCOMPARE(buttonDragThreshold->property("pressed" ).toBool(), false); |
267 | } |
268 | |
269 | void tst_TapHandler::touchGesturePolicyWithinBounds() |
270 | { |
271 | QScopedPointer<QQuickView> windowPtr; |
272 | createView(window&: windowPtr, fileName: "buttons.qml" ); |
273 | QQuickView * window = windowPtr.data(); |
274 | |
275 | QQuickItem *buttonWithinBounds = window->rootObject()->findChild<QQuickItem*>(aName: "WithinBounds" ); |
276 | QVERIFY(buttonWithinBounds); |
277 | QSignalSpy withinBoundsTappedSpy(buttonWithinBounds, SIGNAL(tapped())); |
278 | |
279 | // WithinBounds button stays pressed while touchpoint stays within bounds, emits tapped on release |
280 | QPoint p1 = buttonWithinBounds->mapToScene(point: QPointF(20, 20)).toPoint(); |
281 | QTest::touchEvent(window, device: touchDevice).press(touchId: 1, pt: p1, window); |
282 | QQuickTouchUtils::flush(window); |
283 | QTRY_VERIFY(buttonWithinBounds->property("pressed" ).toBool()); |
284 | p1 += QPoint(50, 0); |
285 | QTest::touchEvent(window, device: touchDevice).move(touchId: 1, pt: p1, window); |
286 | QQuickTouchUtils::flush(window); |
287 | QVERIFY(buttonWithinBounds->property("pressed" ).toBool()); |
288 | QTest::touchEvent(window, device: touchDevice).release(touchId: 1, pt: p1, window); |
289 | QQuickTouchUtils::flush(window); |
290 | QTRY_VERIFY(!buttonWithinBounds->property("pressed" ).toBool()); |
291 | QCOMPARE(withinBoundsTappedSpy.count(), 1); |
292 | |
293 | // WithinBounds button is no longer pressed if touchpoint leaves bounds |
294 | withinBoundsTappedSpy.clear(); |
295 | p1 = buttonWithinBounds->mapToScene(point: QPointF(20, 20)).toPoint(); |
296 | QTest::touchEvent(window, device: touchDevice).press(touchId: 1, pt: p1, window); |
297 | QQuickTouchUtils::flush(window); |
298 | QTRY_VERIFY(buttonWithinBounds->property("pressed" ).toBool()); |
299 | p1 += QPoint(0, 100); |
300 | QTest::touchEvent(window, device: touchDevice).move(touchId: 1, pt: p1, window); |
301 | QQuickTouchUtils::flush(window); |
302 | QTRY_VERIFY(!buttonWithinBounds->property("pressed" ).toBool()); |
303 | QTest::touchEvent(window, device: touchDevice).release(touchId: 1, pt: p1, window); |
304 | QQuickTouchUtils::flush(window); |
305 | QVERIFY(!buttonWithinBounds->property("pressed" ).toBool()); |
306 | QCOMPARE(withinBoundsTappedSpy.count(), 0); |
307 | } |
308 | |
309 | void tst_TapHandler::mouseGesturePolicyWithinBounds() |
310 | { |
311 | QScopedPointer<QQuickView> windowPtr; |
312 | createView(window&: windowPtr, fileName: "buttons.qml" ); |
313 | QQuickView * window = windowPtr.data(); |
314 | |
315 | QQuickItem *buttonWithinBounds = window->rootObject()->findChild<QQuickItem*>(aName: "WithinBounds" ); |
316 | QVERIFY(buttonWithinBounds); |
317 | QSignalSpy withinBoundsTappedSpy(buttonWithinBounds, SIGNAL(tapped())); |
318 | |
319 | // WithinBounds button stays pressed while touchpoint stays within bounds, emits tapped on release |
320 | QPoint p1 = buttonWithinBounds->mapToScene(point: QPointF(20, 20)).toPoint(); |
321 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
322 | QTRY_VERIFY(buttonWithinBounds->property("pressed" ).toBool()); |
323 | p1 += QPoint(50, 0); |
324 | QTest::mouseMove(window, pos: p1); |
325 | QVERIFY(buttonWithinBounds->property("pressed" ).toBool()); |
326 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
327 | QTRY_VERIFY(!buttonWithinBounds->property("pressed" ).toBool()); |
328 | QCOMPARE(withinBoundsTappedSpy.count(), 1); |
329 | |
330 | // WithinBounds button is no longer pressed if touchpoint leaves bounds |
331 | withinBoundsTappedSpy.clear(); |
332 | p1 = buttonWithinBounds->mapToScene(point: QPointF(20, 20)).toPoint(); |
333 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
334 | QTRY_VERIFY(buttonWithinBounds->property("pressed" ).toBool()); |
335 | p1 += QPoint(0, 100); |
336 | QTest::mouseMove(window, pos: p1); |
337 | QTRY_VERIFY(!buttonWithinBounds->property("pressed" ).toBool()); |
338 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
339 | QVERIFY(!buttonWithinBounds->property("pressed" ).toBool()); |
340 | QCOMPARE(withinBoundsTappedSpy.count(), 0); |
341 | } |
342 | |
343 | void tst_TapHandler::touchGesturePolicyReleaseWithinBounds() |
344 | { |
345 | QScopedPointer<QQuickView> windowPtr; |
346 | createView(window&: windowPtr, fileName: "buttons.qml" ); |
347 | QQuickView * window = windowPtr.data(); |
348 | |
349 | QQuickItem *buttonReleaseWithinBounds = window->rootObject()->findChild<QQuickItem*>(aName: "ReleaseWithinBounds" ); |
350 | QVERIFY(buttonReleaseWithinBounds); |
351 | QSignalSpy releaseWithinBoundsTappedSpy(buttonReleaseWithinBounds, SIGNAL(tapped())); |
352 | |
353 | // ReleaseWithinBounds button stays pressed while touchpoint wanders anywhere, |
354 | // then if it comes back within bounds, emits tapped on release |
355 | QPoint p1 = buttonReleaseWithinBounds->mapToScene(point: QPointF(20, 20)).toPoint(); |
356 | QTest::touchEvent(window, device: touchDevice).press(touchId: 1, pt: p1, window); |
357 | QQuickTouchUtils::flush(window); |
358 | QTRY_VERIFY(buttonReleaseWithinBounds->property("pressed" ).toBool()); |
359 | p1 += QPoint(50, 0); |
360 | QTest::touchEvent(window, device: touchDevice).move(touchId: 1, pt: p1, window); |
361 | QQuickTouchUtils::flush(window); |
362 | QVERIFY(buttonReleaseWithinBounds->property("pressed" ).toBool()); |
363 | p1 += QPoint(250, 100); |
364 | QTest::touchEvent(window, device: touchDevice).move(touchId: 1, pt: p1, window); |
365 | QQuickTouchUtils::flush(window); |
366 | QVERIFY(buttonReleaseWithinBounds->property("pressed" ).toBool()); |
367 | p1 = buttonReleaseWithinBounds->mapToScene(point: QPointF(25, 15)).toPoint(); |
368 | QTest::touchEvent(window, device: touchDevice).move(touchId: 1, pt: p1, window); |
369 | QQuickTouchUtils::flush(window); |
370 | QVERIFY(buttonReleaseWithinBounds->property("pressed" ).toBool()); |
371 | QTest::touchEvent(window, device: touchDevice).release(touchId: 1, pt: p1, window); |
372 | QQuickTouchUtils::flush(window); |
373 | QTRY_VERIFY(!buttonReleaseWithinBounds->property("pressed" ).toBool()); |
374 | QCOMPARE(releaseWithinBoundsTappedSpy.count(), 1); |
375 | |
376 | // ReleaseWithinBounds button does not emit tapped if released out of bounds |
377 | releaseWithinBoundsTappedSpy.clear(); |
378 | p1 = buttonReleaseWithinBounds->mapToScene(point: QPointF(20, 20)).toPoint(); |
379 | QTest::touchEvent(window, device: touchDevice).press(touchId: 1, pt: p1, window); |
380 | QQuickTouchUtils::flush(window); |
381 | QTRY_VERIFY(buttonReleaseWithinBounds->property("pressed" ).toBool()); |
382 | p1 += QPoint(0, 100); |
383 | QTest::touchEvent(window, device: touchDevice).move(touchId: 1, pt: p1, window); |
384 | QQuickTouchUtils::flush(window); |
385 | QVERIFY(buttonReleaseWithinBounds->property("pressed" ).toBool()); |
386 | QTest::touchEvent(window, device: touchDevice).release(touchId: 1, pt: p1, window); |
387 | QQuickTouchUtils::flush(window); |
388 | QTRY_VERIFY(!buttonReleaseWithinBounds->property("pressed" ).toBool()); |
389 | QCOMPARE(releaseWithinBoundsTappedSpy.count(), 0); |
390 | } |
391 | |
392 | void tst_TapHandler::mouseGesturePolicyReleaseWithinBounds() |
393 | { |
394 | QScopedPointer<QQuickView> windowPtr; |
395 | createView(window&: windowPtr, fileName: "buttons.qml" ); |
396 | QQuickView * window = windowPtr.data(); |
397 | |
398 | QQuickItem *buttonReleaseWithinBounds = window->rootObject()->findChild<QQuickItem*>(aName: "ReleaseWithinBounds" ); |
399 | QVERIFY(buttonReleaseWithinBounds); |
400 | QSignalSpy releaseWithinBoundsTappedSpy(buttonReleaseWithinBounds, SIGNAL(tapped())); |
401 | |
402 | // ReleaseWithinBounds button stays pressed while touchpoint wanders anywhere, |
403 | // then if it comes back within bounds, emits tapped on release |
404 | QPoint p1 = buttonReleaseWithinBounds->mapToScene(point: QPointF(20, 20)).toPoint(); |
405 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
406 | QTRY_VERIFY(buttonReleaseWithinBounds->property("pressed" ).toBool()); |
407 | p1 += QPoint(50, 0); |
408 | QTest::mouseMove(window, pos: p1); |
409 | QVERIFY(buttonReleaseWithinBounds->property("pressed" ).toBool()); |
410 | p1 += QPoint(250, 100); |
411 | QTest::mouseMove(window, pos: p1); |
412 | QVERIFY(buttonReleaseWithinBounds->property("pressed" ).toBool()); |
413 | p1 = buttonReleaseWithinBounds->mapToScene(point: QPointF(25, 15)).toPoint(); |
414 | QTest::mouseMove(window, pos: p1); |
415 | QVERIFY(buttonReleaseWithinBounds->property("pressed" ).toBool()); |
416 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
417 | QTRY_VERIFY(!buttonReleaseWithinBounds->property("pressed" ).toBool()); |
418 | QCOMPARE(releaseWithinBoundsTappedSpy.count(), 1); |
419 | |
420 | // ReleaseWithinBounds button does not emit tapped if released out of bounds |
421 | releaseWithinBoundsTappedSpy.clear(); |
422 | p1 = buttonReleaseWithinBounds->mapToScene(point: QPointF(20, 20)).toPoint(); |
423 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
424 | QTRY_VERIFY(buttonReleaseWithinBounds->property("pressed" ).toBool()); |
425 | p1 += QPoint(0, 100); |
426 | QTest::mouseMove(window, pos: p1); |
427 | QVERIFY(buttonReleaseWithinBounds->property("pressed" ).toBool()); |
428 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
429 | QTRY_VERIFY(!buttonReleaseWithinBounds->property("pressed" ).toBool()); |
430 | QCOMPARE(releaseWithinBoundsTappedSpy.count(), 0); |
431 | } |
432 | |
433 | void tst_TapHandler::touchMultiTap() |
434 | { |
435 | const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); |
436 | QScopedPointer<QQuickView> windowPtr; |
437 | createView(window&: windowPtr, fileName: "buttons.qml" ); |
438 | QQuickView * window = windowPtr.data(); |
439 | |
440 | QQuickItem *button = window->rootObject()->findChild<QQuickItem*>(aName: "DragThreshold" ); |
441 | QVERIFY(button); |
442 | QSignalSpy tappedSpy(button, SIGNAL(tapped())); |
443 | |
444 | // Tap once |
445 | QPoint p1 = button->mapToScene(point: QPointF(2, 2)).toPoint(); |
446 | QTest::touchEvent(window, device: touchDevice).press(touchId: 1, pt: p1, window); |
447 | QQuickTouchUtils::flush(window); |
448 | QTRY_VERIFY(button->property("pressed" ).toBool()); |
449 | QTest::touchEvent(window, device: touchDevice).release(touchId: 1, pt: p1, window); |
450 | QQuickTouchUtils::flush(window); |
451 | QTRY_VERIFY(!button->property("pressed" ).toBool()); |
452 | QCOMPARE(tappedSpy.count(), 1); |
453 | |
454 | // Tap again in exactly the same place (not likely with touch in the real world) |
455 | QTest::touchEvent(window, device: touchDevice).press(touchId: 1, pt: p1, window); |
456 | QQuickTouchUtils::flush(window); |
457 | QTRY_VERIFY(button->property("pressed" ).toBool()); |
458 | QTest::touchEvent(window, device: touchDevice).release(touchId: 1, pt: p1, window); |
459 | QQuickTouchUtils::flush(window); |
460 | QTRY_VERIFY(!button->property("pressed" ).toBool()); |
461 | QCOMPARE(tappedSpy.count(), 2); |
462 | |
463 | // Tap a third time, nearby |
464 | p1 += QPoint(dragThreshold, dragThreshold); |
465 | QTest::touchEvent(window, device: touchDevice).press(touchId: 1, pt: p1, window); |
466 | QQuickTouchUtils::flush(window); |
467 | QTRY_VERIFY(button->property("pressed" ).toBool()); |
468 | QTest::touchEvent(window, device: touchDevice).release(touchId: 1, pt: p1, window); |
469 | QQuickTouchUtils::flush(window); |
470 | QTRY_VERIFY(!button->property("pressed" ).toBool()); |
471 | QCOMPARE(tappedSpy.count(), 3); |
472 | |
473 | // Tap a fourth time, drifting farther away |
474 | p1 += QPoint(dragThreshold, dragThreshold); |
475 | QTest::touchEvent(window, device: touchDevice).press(touchId: 1, pt: p1, window); |
476 | QQuickTouchUtils::flush(window); |
477 | QTRY_VERIFY(button->property("pressed" ).toBool()); |
478 | QTest::touchEvent(window, device: touchDevice).release(touchId: 1, pt: p1, window); |
479 | QQuickTouchUtils::flush(window); |
480 | QTRY_VERIFY(!button->property("pressed" ).toBool()); |
481 | QCOMPARE(tappedSpy.count(), 4); |
482 | } |
483 | |
484 | void tst_TapHandler::mouseMultiTap() |
485 | { |
486 | const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); |
487 | QScopedPointer<QQuickView> windowPtr; |
488 | createView(window&: windowPtr, fileName: "buttons.qml" ); |
489 | QQuickView * window = windowPtr.data(); |
490 | |
491 | QQuickItem *button = window->rootObject()->findChild<QQuickItem*>(aName: "DragThreshold" ); |
492 | QVERIFY(button); |
493 | QSignalSpy tappedSpy(button, SIGNAL(tapped())); |
494 | |
495 | // Tap once |
496 | QPoint p1 = button->mapToScene(point: QPointF(2, 2)).toPoint(); |
497 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
498 | QTRY_VERIFY(button->property("pressed" ).toBool()); |
499 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
500 | QTRY_VERIFY(!button->property("pressed" ).toBool()); |
501 | QCOMPARE(tappedSpy.count(), 1); |
502 | |
503 | // Tap again in exactly the same place (not likely with touch in the real world) |
504 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
505 | QTRY_VERIFY(button->property("pressed" ).toBool()); |
506 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
507 | QTRY_VERIFY(!button->property("pressed" ).toBool()); |
508 | QCOMPARE(tappedSpy.count(), 2); |
509 | |
510 | // Tap a third time, nearby |
511 | p1 += QPoint(dragThreshold, dragThreshold); |
512 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
513 | QTRY_VERIFY(button->property("pressed" ).toBool()); |
514 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
515 | QTRY_VERIFY(!button->property("pressed" ).toBool()); |
516 | QCOMPARE(tappedSpy.count(), 3); |
517 | |
518 | // Tap a fourth time, drifting farther away |
519 | p1 += QPoint(dragThreshold, dragThreshold); |
520 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
521 | QTRY_VERIFY(button->property("pressed" ).toBool()); |
522 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
523 | QTRY_VERIFY(!button->property("pressed" ).toBool()); |
524 | QCOMPARE(tappedSpy.count(), 4); |
525 | } |
526 | |
527 | void tst_TapHandler::touchLongPress() |
528 | { |
529 | QScopedPointer<QQuickView> windowPtr; |
530 | createView(window&: windowPtr, fileName: "buttons.qml" ); |
531 | QQuickView * window = windowPtr.data(); |
532 | |
533 | QQuickItem *button = window->rootObject()->findChild<QQuickItem*>(aName: "DragThreshold" ); |
534 | QVERIFY(button); |
535 | QQuickTapHandler *tapHandler = button->findChild<QQuickTapHandler*>(aName: "DragThreshold" ); |
536 | QVERIFY(tapHandler); |
537 | QSignalSpy tappedSpy(button, SIGNAL(tapped())); |
538 | QSignalSpy longPressThresholdChangedSpy(tapHandler, SIGNAL(longPressThresholdChanged())); |
539 | QSignalSpy timeHeldSpy(tapHandler, SIGNAL(timeHeldChanged())); |
540 | QSignalSpy longPressedSpy(tapHandler, SIGNAL(longPressed())); |
541 | |
542 | // Reduce the threshold so that we can get a long press quickly |
543 | tapHandler->setLongPressThreshold(0.5); |
544 | QCOMPARE(longPressThresholdChangedSpy.count(), 1); |
545 | |
546 | // Press and hold |
547 | QPoint p1 = button->mapToScene(point: button->clipRect().center()).toPoint(); |
548 | QTest::touchEvent(window, device: touchDevice).press(touchId: 1, pt: p1, window); |
549 | QQuickTouchUtils::flush(window); |
550 | QTRY_VERIFY(button->property("pressed" ).toBool()); |
551 | QTRY_COMPARE(longPressedSpy.count(), 1); |
552 | timeHeldSpy.wait(); // the longer we hold it, the more this will occur |
553 | qDebug() << "held" << tapHandler->timeHeld() << "secs; timeHeld updated" << timeHeldSpy.count() << "times" ; |
554 | QVERIFY(timeHeldSpy.count() > 0); |
555 | QVERIFY(tapHandler->timeHeld() > 0.4); // Should be > 0.5 but slow CI and timer granularity can interfere |
556 | |
557 | // Release and verify that tapped was not emitted |
558 | QTest::touchEvent(window, device: touchDevice).release(touchId: 1, pt: p1, window); |
559 | QQuickTouchUtils::flush(window); |
560 | QTRY_VERIFY(!button->property("pressed" ).toBool()); |
561 | QCOMPARE(tappedSpy.count(), 0); |
562 | } |
563 | |
564 | void tst_TapHandler::mouseLongPress() |
565 | { |
566 | QScopedPointer<QQuickView> windowPtr; |
567 | createView(window&: windowPtr, fileName: "buttons.qml" ); |
568 | QQuickView * window = windowPtr.data(); |
569 | |
570 | QQuickItem *button = window->rootObject()->findChild<QQuickItem*>(aName: "DragThreshold" ); |
571 | QVERIFY(button); |
572 | QQuickTapHandler *tapHandler = button->findChild<QQuickTapHandler*>(aName: "DragThreshold" ); |
573 | QVERIFY(tapHandler); |
574 | QSignalSpy tappedSpy(button, SIGNAL(tapped())); |
575 | QSignalSpy longPressThresholdChangedSpy(tapHandler, SIGNAL(longPressThresholdChanged())); |
576 | QSignalSpy timeHeldSpy(tapHandler, SIGNAL(timeHeldChanged())); |
577 | QSignalSpy longPressedSpy(tapHandler, SIGNAL(longPressed())); |
578 | |
579 | // Reduce the threshold so that we can get a long press quickly |
580 | tapHandler->setLongPressThreshold(0.5); |
581 | QCOMPARE(longPressThresholdChangedSpy.count(), 1); |
582 | |
583 | // Press and hold |
584 | QPoint p1 = button->mapToScene(point: button->clipRect().center()).toPoint(); |
585 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
586 | QTRY_VERIFY(button->property("pressed" ).toBool()); |
587 | QTRY_COMPARE(longPressedSpy.count(), 1); |
588 | timeHeldSpy.wait(); // the longer we hold it, the more this will occur |
589 | qDebug() << "held" << tapHandler->timeHeld() << "secs; timeHeld updated" << timeHeldSpy.count() << "times" ; |
590 | QVERIFY(timeHeldSpy.count() > 0); |
591 | QVERIFY(tapHandler->timeHeld() > 0.4); // Should be > 0.5 but slow CI and timer granularity can interfere |
592 | |
593 | // Release and verify that tapped was not emitted |
594 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1, delay: 500); |
595 | QTRY_VERIFY(!button->property("pressed" ).toBool()); |
596 | QCOMPARE(tappedSpy.count(), 0); |
597 | } |
598 | |
599 | void tst_TapHandler::buttonsMultiTouch() |
600 | { |
601 | QScopedPointer<QQuickView> windowPtr; |
602 | createView(window&: windowPtr, fileName: "buttons.qml" ); |
603 | QQuickView * window = windowPtr.data(); |
604 | |
605 | QQuickItem *buttonDragThreshold = window->rootObject()->findChild<QQuickItem*>(aName: "DragThreshold" ); |
606 | QVERIFY(buttonDragThreshold); |
607 | QSignalSpy dragThresholdTappedSpy(buttonDragThreshold, SIGNAL(tapped())); |
608 | |
609 | QQuickItem *buttonWithinBounds = window->rootObject()->findChild<QQuickItem*>(aName: "WithinBounds" ); |
610 | QVERIFY(buttonWithinBounds); |
611 | QSignalSpy withinBoundsTappedSpy(buttonWithinBounds, SIGNAL(tapped())); |
612 | |
613 | QQuickItem *buttonReleaseWithinBounds = window->rootObject()->findChild<QQuickItem*>(aName: "ReleaseWithinBounds" ); |
614 | QVERIFY(buttonReleaseWithinBounds); |
615 | QSignalSpy releaseWithinBoundsTappedSpy(buttonReleaseWithinBounds, SIGNAL(tapped())); |
616 | QTest::QTouchEventSequence touchSeq = QTest::touchEvent(window, device: touchDevice, autoCommit: false); |
617 | |
618 | // can press multiple buttons at the same time |
619 | QPoint p1 = buttonDragThreshold->mapToScene(point: QPointF(20, 20)).toPoint(); |
620 | touchSeq.press(touchId: 1, pt: p1, window).commit(); |
621 | QQuickTouchUtils::flush(window); |
622 | QTRY_VERIFY(buttonDragThreshold->property("pressed" ).toBool()); |
623 | QPoint p2 = buttonWithinBounds->mapToScene(point: QPointF(20, 20)).toPoint(); |
624 | touchSeq.stationary(touchId: 1).press(touchId: 2, pt: p2, window).commit(); |
625 | QQuickTouchUtils::flush(window); |
626 | QTRY_VERIFY(buttonWithinBounds->property("pressed" ).toBool()); |
627 | QVERIFY(buttonWithinBounds->property("active" ).toBool()); |
628 | QPoint p3 = buttonReleaseWithinBounds->mapToScene(point: QPointF(20, 20)).toPoint(); |
629 | touchSeq.stationary(touchId: 1).stationary(touchId: 2).press(touchId: 3, pt: p3, window).commit(); |
630 | QQuickTouchUtils::flush(window); |
631 | QTRY_VERIFY(buttonReleaseWithinBounds->property("pressed" ).toBool()); |
632 | QVERIFY(buttonReleaseWithinBounds->property("active" ).toBool()); |
633 | QVERIFY(buttonWithinBounds->property("pressed" ).toBool()); |
634 | QVERIFY(buttonWithinBounds->property("active" ).toBool()); |
635 | QVERIFY(buttonDragThreshold->property("pressed" ).toBool()); |
636 | |
637 | // combinations of small touchpoint movements and stationary points should not cause state changes |
638 | p1 += QPoint(2, 0); |
639 | p2 += QPoint(3, 0); |
640 | touchSeq.move(touchId: 1, pt: p1).move(touchId: 2, pt: p2).stationary(touchId: 3).commit(); |
641 | QVERIFY(buttonDragThreshold->property("pressed" ).toBool()); |
642 | QVERIFY(buttonWithinBounds->property("pressed" ).toBool()); |
643 | QVERIFY(buttonWithinBounds->property("active" ).toBool()); |
644 | QVERIFY(buttonReleaseWithinBounds->property("pressed" ).toBool()); |
645 | QVERIFY(buttonReleaseWithinBounds->property("active" ).toBool()); |
646 | p3 += QPoint(4, 0); |
647 | touchSeq.stationary(touchId: 1).stationary(touchId: 2).move(touchId: 3, pt: p3).commit(); |
648 | QVERIFY(buttonDragThreshold->property("pressed" ).toBool()); |
649 | QVERIFY(buttonWithinBounds->property("pressed" ).toBool()); |
650 | QVERIFY(buttonWithinBounds->property("active" ).toBool()); |
651 | QVERIFY(buttonReleaseWithinBounds->property("pressed" ).toBool()); |
652 | QVERIFY(buttonReleaseWithinBounds->property("active" ).toBool()); |
653 | |
654 | // can release top button and press again: others stay pressed the whole time |
655 | touchSeq.stationary(touchId: 2).stationary(touchId: 3).release(touchId: 1, pt: p1, window).commit(); |
656 | QQuickTouchUtils::flush(window); |
657 | QTRY_VERIFY(!buttonDragThreshold->property("pressed" ).toBool()); |
658 | QCOMPARE(dragThresholdTappedSpy.count(), 1); |
659 | QVERIFY(buttonWithinBounds->property("pressed" ).toBool()); |
660 | QCOMPARE(withinBoundsTappedSpy.count(), 0); |
661 | QVERIFY(buttonReleaseWithinBounds->property("pressed" ).toBool()); |
662 | QCOMPARE(releaseWithinBoundsTappedSpy.count(), 0); |
663 | touchSeq.stationary(touchId: 2).stationary(touchId: 3).press(touchId: 1, pt: p1, window).commit(); |
664 | QQuickTouchUtils::flush(window); |
665 | QTRY_VERIFY(buttonDragThreshold->property("pressed" ).toBool()); |
666 | QVERIFY(buttonWithinBounds->property("pressed" ).toBool()); |
667 | QVERIFY(buttonReleaseWithinBounds->property("pressed" ).toBool()); |
668 | |
669 | // can release middle button and press again: others stay pressed the whole time |
670 | touchSeq.stationary(touchId: 1).stationary(touchId: 3).release(touchId: 2, pt: p2, window).commit(); |
671 | QQuickTouchUtils::flush(window); |
672 | QTRY_VERIFY(!buttonWithinBounds->property("pressed" ).toBool()); |
673 | QCOMPARE(withinBoundsTappedSpy.count(), 1); |
674 | QVERIFY(buttonDragThreshold->property("pressed" ).toBool()); |
675 | QCOMPARE(dragThresholdTappedSpy.count(), 1); |
676 | QVERIFY(buttonReleaseWithinBounds->property("pressed" ).toBool()); |
677 | QCOMPARE(releaseWithinBoundsTappedSpy.count(), 0); |
678 | touchSeq.stationary(touchId: 1).stationary(touchId: 3).press(touchId: 2, pt: p2, window).commit(); |
679 | QQuickTouchUtils::flush(window); |
680 | QVERIFY(buttonDragThreshold->property("pressed" ).toBool()); |
681 | QVERIFY(buttonWithinBounds->property("pressed" ).toBool()); |
682 | QVERIFY(buttonReleaseWithinBounds->property("pressed" ).toBool()); |
683 | |
684 | // can release bottom button and press again: others stay pressed the whole time |
685 | touchSeq.stationary(touchId: 1).stationary(touchId: 2).release(touchId: 3, pt: p3, window).commit(); |
686 | QQuickTouchUtils::flush(window); |
687 | QCOMPARE(releaseWithinBoundsTappedSpy.count(), 1); |
688 | QVERIFY(buttonWithinBounds->property("pressed" ).toBool()); |
689 | QCOMPARE(withinBoundsTappedSpy.count(), 1); |
690 | QVERIFY(!buttonReleaseWithinBounds->property("pressed" ).toBool()); |
691 | QCOMPARE(dragThresholdTappedSpy.count(), 1); |
692 | touchSeq.stationary(touchId: 1).stationary(touchId: 2).press(touchId: 3, pt: p3, window).commit(); |
693 | QQuickTouchUtils::flush(window); |
694 | QTRY_VERIFY(buttonDragThreshold->property("pressed" ).toBool()); |
695 | QVERIFY(buttonWithinBounds->property("pressed" ).toBool()); |
696 | QVERIFY(buttonReleaseWithinBounds->property("pressed" ).toBool()); |
697 | } |
698 | |
699 | void tst_TapHandler::componentUserBehavioralOverride() |
700 | { |
701 | QScopedPointer<QQuickView> windowPtr; |
702 | createView(window&: windowPtr, fileName: "buttonOverrideHandler.qml" ); |
703 | QQuickView * window = windowPtr.data(); |
704 | |
705 | QQuickItem *button = window->rootObject()->findChild<QQuickItem*>(aName: "Overridden" ); |
706 | QVERIFY(button); |
707 | QQuickTapHandler *innerTapHandler = button->findChild<QQuickTapHandler*>(aName: "Overridden" ); |
708 | QVERIFY(innerTapHandler); |
709 | QQuickTapHandler *userTapHandler = button->findChild<QQuickTapHandler*>(aName: "override" ); |
710 | QVERIFY(userTapHandler); |
711 | QSignalSpy tappedSpy(button, SIGNAL(tapped())); |
712 | QSignalSpy innerGrabChangedSpy(innerTapHandler, SIGNAL(grabChanged(QQuickEventPoint::GrabTransition, QQuickEventPoint *))); |
713 | QSignalSpy userGrabChangedSpy(userTapHandler, SIGNAL(grabChanged(QQuickEventPoint::GrabTransition, QQuickEventPoint *))); |
714 | QSignalSpy innerPressedChangedSpy(innerTapHandler, SIGNAL(pressedChanged())); |
715 | QSignalSpy userPressedChangedSpy(userTapHandler, SIGNAL(pressedChanged())); |
716 | |
717 | // Press |
718 | QPoint p1 = button->mapToScene(point: button->clipRect().center()).toPoint(); |
719 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
720 | QTRY_COMPARE(userPressedChangedSpy.count(), 1); |
721 | QCOMPARE(innerPressedChangedSpy.count(), 0); |
722 | QCOMPARE(innerGrabChangedSpy.count(), 0); |
723 | QCOMPARE(userGrabChangedSpy.count(), 1); |
724 | |
725 | // Release |
726 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
727 | QTRY_COMPARE(userPressedChangedSpy.count(), 2); |
728 | QCOMPARE(innerPressedChangedSpy.count(), 0); |
729 | QCOMPARE(tappedSpy.count(), 1); // only because the override handler makes that happen |
730 | QCOMPARE(innerGrabChangedSpy.count(), 0); |
731 | QCOMPARE(userGrabChangedSpy.count(), 2); |
732 | } |
733 | |
734 | void tst_TapHandler::rightLongPressIgnoreWheel() |
735 | { |
736 | QScopedPointer<QQuickView> windowPtr; |
737 | createView(window&: windowPtr, fileName: "rightTapHandler.qml" ); |
738 | QQuickView * window = windowPtr.data(); |
739 | |
740 | QQuickTapHandler *tap = window->rootObject()->findChild<QQuickTapHandler*>(); |
741 | QVERIFY(tap); |
742 | QSignalSpy tappedSpy(tap, SIGNAL(tapped(QQuickEventPoint *))); |
743 | QSignalSpy longPressedSpy(tap, SIGNAL(longPressed())); |
744 | QPoint p1(100, 100); |
745 | |
746 | // Mouse wheel with ScrollBegin phase (because as soon as two fingers are touching |
747 | // the trackpad, it will send such an event: QTBUG-71955) |
748 | { |
749 | QWheelEvent wheelEvent(p1, p1, QPoint(0, 0), QPoint(0, 0), |
750 | Qt::NoButton, Qt::NoModifier, Qt::ScrollBegin, false, Qt::MouseEventNotSynthesized); |
751 | QGuiApplication::sendEvent(receiver: window, event: &wheelEvent); |
752 | } |
753 | |
754 | // Press |
755 | QTest::mousePress(window, button: Qt::RightButton, stateKey: Qt::NoModifier, pos: p1); |
756 | QTRY_COMPARE(tap->isPressed(), true); |
757 | |
758 | // Mouse wheel ScrollEnd phase |
759 | QWheelEvent wheelEvent(p1, p1, QPoint(0, 0), QPoint(0, 0), |
760 | Qt::NoButton, Qt::NoModifier, Qt::ScrollEnd, false, Qt::MouseEventNotSynthesized); |
761 | QGuiApplication::sendEvent(receiver: window, event: &wheelEvent); |
762 | QTRY_COMPARE(longPressedSpy.count(), 1); |
763 | QCOMPARE(tap->isPressed(), true); |
764 | QCOMPARE(tappedSpy.count(), 0); |
765 | |
766 | // Release |
767 | QTest::mouseRelease(window, button: Qt::RightButton, stateKey: Qt::NoModifier, pos: p1, delay: 500); |
768 | QTRY_COMPARE(tap->isPressed(), false); |
769 | QCOMPARE(tappedSpy.count(), 0); |
770 | } |
771 | |
772 | void tst_TapHandler::nonTopLevelParentWindow() // QTBUG-91716 |
773 | { |
774 | QScopedPointer<QQuickWindow> parentWindowPtr(new QQuickWindow); |
775 | auto parentWindow = parentWindowPtr.get(); |
776 | parentWindow->setGeometry(posx: 400, posy: 400, w: 250, h: 250); |
777 | |
778 | QScopedPointer<QQuickView> windowPtr; |
779 | createView(window&: windowPtr, fileName: "simpleTapHandler.qml" , parent: parentWindow); |
780 | auto window = windowPtr.get(); |
781 | window->setGeometry(posx: 10, posy: 10, w: 100, h: 100); |
782 | |
783 | QQuickItem *root = window->rootObject(); |
784 | |
785 | auto p1 = QPoint(20, 20); |
786 | mouseEvent(type: QEvent::MouseButtonPress, button: Qt::LeftButton, point: p1, targetWindow: window, mapToWindow: parentWindow); |
787 | mouseEvent(type: QEvent::MouseButtonRelease, button: Qt::LeftButton, point: p1, targetWindow: window, mapToWindow: parentWindow); |
788 | |
789 | QCOMPARE(root->property("tapCount" ).toInt(), 1); |
790 | |
791 | QTest::touchEvent(window, device: touchDevice).press(touchId: 0, pt: p1, window: parentWindow).commit(); |
792 | QTest::touchEvent(window, device: touchDevice).release(touchId: 0, pt: p1, window: parentWindow).commit(); |
793 | |
794 | QCOMPARE(root->property("tapCount" ).toInt(), 2); |
795 | } |
796 | |
797 | QTEST_MAIN(tst_TapHandler) |
798 | |
799 | #include "tst_qquicktaphandler.moc" |
800 | |
801 | |