1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the 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 | #include <QDebug> |
32 | |
33 | #include <QtGui/qstylehints.h> |
34 | #include <private/qdebug_p.h> |
35 | |
36 | #include <QtQuick/qquickview.h> |
37 | #include <QtQuick/qquickitem.h> |
38 | #include <QtQuick/private/qquickevents_p_p.h> |
39 | #include <QtQuick/private/qquickmousearea_p.h> |
40 | #include <QtQuick/private/qquickmultipointtoucharea_p.h> |
41 | #include <QtQuick/private/qquickpincharea_p.h> |
42 | #include <QtQuick/private/qquickflickable_p.h> |
43 | #include <QtQuick/private/qquickwindow_p.h> |
44 | |
45 | #include <QtQml/qqmlengine.h> |
46 | #include <QtQml/qqmlproperty.h> |
47 | |
48 | #include "../../shared/util.h" |
49 | #include "../shared/viewtestutil.h" |
50 | |
51 | Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests" ) |
52 | |
53 | struct Event |
54 | { |
55 | Event(QEvent::Type t, QPoint mouse, QPoint global) |
56 | :type(t), mousePos(mouse), mousePosGlobal(global) |
57 | {} |
58 | |
59 | Event(QEvent::Type t, QList<QTouchEvent::TouchPoint> touch) |
60 | :type(t), points(touch) |
61 | {} |
62 | |
63 | QEvent::Type type; |
64 | QPoint mousePos; |
65 | QPoint mousePosGlobal; |
66 | QList<QTouchEvent::TouchPoint> points; |
67 | }; |
68 | |
69 | #ifndef QT_NO_DEBUG_STREAM |
70 | QDebug operator<<(QDebug dbg, const struct Event &event) { |
71 | QDebugStateSaver saver(dbg); |
72 | dbg.nospace(); |
73 | dbg << "Event(" ; |
74 | QtDebugUtils::formatQEnum(debug&: dbg, value: event.type); |
75 | if (event.points.isEmpty()) |
76 | dbg << " @ " << event.mousePos << " global " << event.mousePosGlobal; |
77 | else |
78 | dbg << ", " << event.points.count() << " touchpoints: " << event.points; |
79 | dbg << ')'; |
80 | return dbg; |
81 | } |
82 | #endif |
83 | |
84 | class EventItem : public QQuickItem |
85 | { |
86 | Q_OBJECT |
87 | |
88 | Q_SIGNALS: |
89 | void onTouchEvent(QQuickItem *receiver); |
90 | |
91 | public: |
92 | EventItem(QQuickItem *parent = nullptr) |
93 | : QQuickItem(parent) |
94 | { |
95 | setAcceptedMouseButtons(Qt::LeftButton); |
96 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) |
97 | setAcceptTouchEvents(true); |
98 | #endif |
99 | } |
100 | |
101 | void touchEvent(QTouchEvent *event) |
102 | { |
103 | eventList.append(t: Event(event->type(), event->touchPoints())); |
104 | QList<QTouchEvent::TouchPoint> tps = event->touchPoints(); |
105 | Q_ASSERT(!tps.isEmpty()); |
106 | point0 = tps.first().id(); |
107 | event->setAccepted(acceptTouch); |
108 | emit onTouchEvent(receiver: this); |
109 | } |
110 | void mousePressEvent(QMouseEvent *event) |
111 | { |
112 | eventList.append(t: Event(event->type(), event->pos(), event->globalPos())); |
113 | event->setAccepted(acceptMouse); |
114 | } |
115 | void mouseMoveEvent(QMouseEvent *event) |
116 | { |
117 | eventList.append(t: Event(event->type(), event->pos(), event->globalPos())); |
118 | event->setAccepted(acceptMouse); |
119 | } |
120 | void mouseReleaseEvent(QMouseEvent *event) |
121 | { |
122 | eventList.append(t: Event(event->type(), event->pos(), event->globalPos())); |
123 | event->setAccepted(acceptMouse); |
124 | } |
125 | void mouseDoubleClickEvent(QMouseEvent *event) |
126 | { |
127 | eventList.append(t: Event(event->type(), event->pos(), event->globalPos())); |
128 | event->setAccepted(acceptMouse); |
129 | } |
130 | |
131 | void mouseUngrabEvent() |
132 | { |
133 | eventList.append(t: Event(QEvent::UngrabMouse, QPoint(0,0), QPoint(0,0))); |
134 | } |
135 | |
136 | void touchUngrabEvent() |
137 | { |
138 | ++touchUngrabCount; |
139 | } |
140 | |
141 | bool event(QEvent *event) { |
142 | return QQuickItem::event(event); |
143 | } |
144 | |
145 | QList<Event> eventList; |
146 | int touchUngrabCount = 0; |
147 | bool acceptMouse = false; |
148 | bool acceptTouch = false; |
149 | bool filterTouch = false; // when used as event filter |
150 | |
151 | bool eventFilter(QObject *, QEvent *event) |
152 | { |
153 | if (event->type() == QEvent::TouchBegin || |
154 | event->type() == QEvent::TouchUpdate || |
155 | event->type() == QEvent::TouchCancel || |
156 | event->type() == QEvent::TouchEnd) { |
157 | QTouchEvent *touch = static_cast<QTouchEvent*>(event); |
158 | eventList.append(t: Event(event->type(), touch->touchPoints())); |
159 | QList<QTouchEvent::TouchPoint> tps = touch->touchPoints(); |
160 | Q_ASSERT(!tps.isEmpty()); |
161 | point0 = tps.first().id(); |
162 | if (filterTouch) |
163 | event->accept(); |
164 | return true; |
165 | } |
166 | return false; |
167 | } |
168 | int point0 = -1; |
169 | }; |
170 | |
171 | class tst_TouchMouse : public QQmlDataTest |
172 | { |
173 | Q_OBJECT |
174 | public: |
175 | tst_TouchMouse() |
176 | :device(QTest::createTouchDevice()) |
177 | {} |
178 | |
179 | private slots: |
180 | void initTestCase(); |
181 | |
182 | void simpleTouchEvent_data(); |
183 | void simpleTouchEvent(); |
184 | void testEventFilter(); |
185 | void mouse(); |
186 | void touchOverMouse(); |
187 | void mouseOverTouch(); |
188 | |
189 | void buttonOnFlickable(); |
190 | void touchButtonOnFlickable(); |
191 | void buttonOnDelayedPressFlickable_data(); |
192 | void buttonOnDelayedPressFlickable(); |
193 | void buttonOnTouch(); |
194 | |
195 | void pinchOnFlickable(); |
196 | void flickableOnPinch(); |
197 | void mouseOnFlickableOnPinch(); |
198 | |
199 | void tapOnDismissiveTopMouseAreaClicksBottomOne(); |
200 | |
201 | void touchGrabCausesMouseUngrab(); |
202 | void touchPointDeliveryOrder(); |
203 | |
204 | void hoverEnabled(); |
205 | void implicitUngrab(); |
206 | |
207 | protected: |
208 | bool eventFilter(QObject *, QEvent *event) |
209 | { |
210 | if (event->type() == QEvent::MouseButtonPress || |
211 | event->type() == QEvent::MouseMove || |
212 | event->type() == QEvent::MouseButtonRelease) { |
213 | QMouseEvent *me = static_cast<QMouseEvent*>(event); |
214 | filteredEventList.append(t: Event(me->type(), me->pos(), me->globalPos())); |
215 | } |
216 | return false; |
217 | } |
218 | |
219 | private: |
220 | QQuickView *createView(); |
221 | QTouchDevice *device; |
222 | QList<Event> filteredEventList; |
223 | }; |
224 | |
225 | QQuickView *tst_TouchMouse::createView() |
226 | { |
227 | QQuickView *window = new QQuickView(nullptr); |
228 | return window; |
229 | } |
230 | |
231 | void tst_TouchMouse::initTestCase() |
232 | { |
233 | QQmlDataTest::initTestCase(); |
234 | qmlRegisterType<EventItem>(uri: "Qt.test" , versionMajor: 1, versionMinor: 0, qmlName: "EventItem" ); |
235 | } |
236 | |
237 | void tst_TouchMouse::simpleTouchEvent_data() |
238 | { |
239 | QTest::addColumn<bool>(name: "synthMouse" ); // AA_SynthesizeMouseForUnhandledTouchEvents |
240 | QTest::newRow(dataTag: "no synth" ) << false; |
241 | QTest::newRow(dataTag: "synth" ) << true; |
242 | } |
243 | |
244 | void tst_TouchMouse::simpleTouchEvent() |
245 | { |
246 | QFETCH(bool, synthMouse); |
247 | qApp->setAttribute(attribute: Qt::AA_SynthesizeMouseForUnhandledTouchEvents, on: synthMouse); |
248 | |
249 | QScopedPointer<QQuickView> window(createView()); |
250 | window->setSource(testFileUrl(fileName: "singleitem.qml" )); |
251 | window->show(); |
252 | QQuickViewTestUtil::centerOnScreen(window: window.data()); |
253 | QVERIFY(QTest::qWaitForWindowActive(window.data())); |
254 | QVERIFY(window->rootObject() != nullptr); |
255 | |
256 | EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>(aName: "eventItem1" ); |
257 | QVERIFY(eventItem1); |
258 | |
259 | // Do not accept touch or mouse |
260 | QPoint p1; |
261 | p1 = QPoint(20, 20); |
262 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p1, window: window.data()); |
263 | QQuickTouchUtils::flush(window: window.data()); |
264 | // Get a touch and then mouse event offered |
265 | QCOMPARE(eventItem1->eventList.size(), synthMouse ? 2 : 1); |
266 | QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); |
267 | p1 += QPoint(10, 0); |
268 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p1, window: window.data()); |
269 | QQuickTouchUtils::flush(window: window.data()); |
270 | // Not accepted, no updates |
271 | QCOMPARE(eventItem1->eventList.size(), synthMouse ? 2 : 1); |
272 | QTest::touchEvent(window: window.data(), device).release(touchId: 0, pt: p1, window: window.data()); |
273 | QQuickTouchUtils::flush(window: window.data()); |
274 | QCOMPARE(eventItem1->eventList.size(), synthMouse ? 2 : 1); |
275 | eventItem1->eventList.clear(); |
276 | |
277 | // Accept touch |
278 | eventItem1->acceptTouch = true; |
279 | p1 = QPoint(20, 20); |
280 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p1, window: window.data()); |
281 | QQuickTouchUtils::flush(window: window.data()); |
282 | QCOMPARE(eventItem1->eventList.size(), 1); |
283 | p1 += QPoint(10, 0); |
284 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p1, window: window.data()); |
285 | QQuickTouchUtils::flush(window: window.data()); |
286 | QCOMPARE(eventItem1->eventList.size(), 2); |
287 | QTest::touchEvent(window: window.data(), device).release(touchId: 0, pt: p1, window: window.data()); |
288 | QQuickTouchUtils::flush(window: window.data()); |
289 | QCOMPARE(eventItem1->eventList.size(), 3); |
290 | eventItem1->eventList.clear(); |
291 | |
292 | // wait to avoid getting a double click event |
293 | QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); |
294 | |
295 | // Accept mouse |
296 | eventItem1->acceptTouch = false; |
297 | eventItem1->acceptMouse = true; |
298 | eventItem1->setAcceptedMouseButtons(Qt::LeftButton); |
299 | p1 = QPoint(20, 20); |
300 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p1, window: window.data()); |
301 | QQuickTouchUtils::flush(window: window.data()); |
302 | QCOMPARE(eventItem1->eventList.size(), synthMouse ? 2 : 1); |
303 | QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); |
304 | if (synthMouse) |
305 | QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress); |
306 | QCOMPARE(window->mouseGrabberItem(), synthMouse ? eventItem1 : nullptr); |
307 | |
308 | QPoint localPos = eventItem1->mapFromScene(point: p1).toPoint(); |
309 | QPoint globalPos = window->mapToGlobal(pos: p1); |
310 | QPoint scenePos = p1; // item is at 0,0 |
311 | QCOMPARE(eventItem1->eventList.at(0).points.at(0).pos().toPoint(), localPos); |
312 | QCOMPARE(eventItem1->eventList.at(0).points.at(0).scenePos().toPoint(), scenePos); |
313 | QCOMPARE(eventItem1->eventList.at(0).points.at(0).screenPos().toPoint(), globalPos); |
314 | if (synthMouse) { |
315 | QCOMPARE(eventItem1->eventList.at(1).mousePos, localPos); |
316 | QCOMPARE(eventItem1->eventList.at(1).mousePosGlobal, globalPos); |
317 | } |
318 | |
319 | p1 += QPoint(10, 0); |
320 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p1, window: window.data()); |
321 | QQuickTouchUtils::flush(window: window.data()); |
322 | QCOMPARE(eventItem1->eventList.size(), synthMouse ? 4 : 1); |
323 | if (synthMouse) { |
324 | QCOMPARE(eventItem1->eventList.at(2).type, QEvent::TouchUpdate); |
325 | QCOMPARE(eventItem1->eventList.at(3).type, QEvent::MouseMove); |
326 | } |
327 | // else, if there was no synth-mouse and we didn't accept the touch, |
328 | // TouchUpdate was not sent to eventItem1 either. |
329 | QTest::touchEvent(window: window.data(), device).release(touchId: 0, pt: p1, window: window.data()); |
330 | QQuickTouchUtils::flush(window: window.data()); |
331 | QCOMPARE(eventItem1->eventList.size(), synthMouse ? 7 : 1); |
332 | if (synthMouse) { |
333 | QCOMPARE(eventItem1->eventList.at(4).type, QEvent::TouchEnd); |
334 | QCOMPARE(eventItem1->eventList.at(5).type, QEvent::MouseButtonRelease); |
335 | QCOMPARE(eventItem1->eventList.at(6).type, QEvent::UngrabMouse); |
336 | } |
337 | // else, if there was no synth-mouse and we didn't accept the touch, |
338 | // TouchEnd was not sent to eventItem1 either. |
339 | eventItem1->eventList.clear(); |
340 | |
341 | // wait to avoid getting a double click event |
342 | QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); |
343 | |
344 | // Accept mouse buttons but not the event |
345 | eventItem1->acceptTouch = false; |
346 | eventItem1->acceptMouse = false; |
347 | eventItem1->setAcceptedMouseButtons(Qt::LeftButton); |
348 | p1 = QPoint(20, 20); |
349 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p1, window: window.data()); |
350 | QQuickTouchUtils::flush(window: window.data()); |
351 | QCOMPARE(eventItem1->eventList.size(), synthMouse ? 2 : 1); |
352 | QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); |
353 | if (synthMouse) |
354 | QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress); |
355 | p1 += QPoint(10, 0); |
356 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p1, window: window.data()); |
357 | QQuickTouchUtils::flush(window: window.data()); |
358 | QCOMPARE(eventItem1->eventList.size(), synthMouse ? 2 : 1); |
359 | QTest::touchEvent(window: window.data(), device).release(touchId: 0, pt: p1, window: window.data()); |
360 | QQuickTouchUtils::flush(window: window.data()); |
361 | QCOMPARE(eventItem1->eventList.size(), synthMouse ? 2 : 1); |
362 | eventItem1->eventList.clear(); |
363 | |
364 | // wait to avoid getting a double click event |
365 | QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); |
366 | |
367 | // Accept touch and mouse |
368 | eventItem1->acceptTouch = true; |
369 | eventItem1->setAcceptedMouseButtons(Qt::LeftButton); |
370 | p1 = QPoint(20, 20); |
371 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p1, window: window.data()); |
372 | QQuickTouchUtils::flush(window: window.data()); |
373 | QCOMPARE(eventItem1->eventList.size(), 1); |
374 | QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); |
375 | p1 += QPoint(10, 0); |
376 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p1, window: window.data()); |
377 | QQuickTouchUtils::flush(window: window.data()); |
378 | QCOMPARE(eventItem1->eventList.size(), 2); |
379 | QCOMPARE(eventItem1->eventList.at(1).type, QEvent::TouchUpdate); |
380 | QTest::touchEvent(window: window.data(), device).release(touchId: 0, pt: p1, window: window.data()); |
381 | QQuickTouchUtils::flush(window: window.data()); |
382 | QCOMPARE(eventItem1->eventList.size(), 3); |
383 | QCOMPARE(eventItem1->eventList.at(2).type, QEvent::TouchEnd); |
384 | eventItem1->eventList.clear(); |
385 | } |
386 | |
387 | void tst_TouchMouse::testEventFilter() |
388 | { |
389 | // // install event filter on item and see that it can grab events |
390 | // QScopedPointer<QQuickView> window(createView()); |
391 | // window->setSource(testFileUrl("singleitem.qml")); |
392 | // window->show(); |
393 | // QQuickViewTestUtil::centerOnScreen(window.data()); |
394 | // QVERIFY(QTest::qWaitForWindowActive(window.data())); |
395 | // QVERIFY(window->rootObject() != 0); |
396 | |
397 | // EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>("eventItem1"); |
398 | // QVERIFY(eventItem1); |
399 | // eventItem1->acceptTouch = true; |
400 | |
401 | // EventItem *filter = new EventItem; |
402 | // filter->filterTouch = true; |
403 | // eventItem1->installEventFilter(filter); |
404 | |
405 | // QPoint p1 = QPoint(20, 20); |
406 | // QTest::touchEvent(window.data(), device).press(0, p1, window.data()); |
407 | // // QEXPECT_FAIL("", "We do not implement event filters correctly", Abort); |
408 | // QCOMPARE(eventItem1->eventList.size(), 0); |
409 | // QCOMPARE(filter->eventList.size(), 1); |
410 | // QTest::touchEvent(window.data(), device).release(0, p1, window.data()); |
411 | // QCOMPARE(eventItem1->eventList.size(), 0); |
412 | // QCOMPARE(filter->eventList.size(), 2); |
413 | |
414 | // delete filter; |
415 | } |
416 | |
417 | void tst_TouchMouse::mouse() |
418 | { |
419 | // eventItem1 |
420 | // - eventItem2 |
421 | |
422 | QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); |
423 | QScopedPointer<QQuickView> window(createView()); |
424 | window->setSource(testFileUrl(fileName: "twoitems.qml" )); |
425 | window->show(); |
426 | QQuickViewTestUtil::centerOnScreen(window: window.data()); |
427 | QVERIFY(QTest::qWaitForWindowActive(window.data())); |
428 | QVERIFY(window->rootObject() != nullptr); |
429 | |
430 | EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>(aName: "eventItem1" ); |
431 | QVERIFY(eventItem1); |
432 | EventItem *eventItem2 = window->rootObject()->findChild<EventItem*>(aName: "eventItem2" ); |
433 | QVERIFY(eventItem2); |
434 | |
435 | // bottom item likes mouse, top likes touch |
436 | eventItem1->setAcceptedMouseButtons(Qt::LeftButton); |
437 | eventItem1->acceptMouse = true; |
438 | // item 2 doesn't accept anything, thus it sees a touch pass by |
439 | QPoint p1 = QPoint(30, 30); |
440 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p1, window: window.data()); |
441 | QQuickTouchUtils::flush(window: window.data()); |
442 | |
443 | QCOMPARE(eventItem1->eventList.size(), 2); |
444 | QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); |
445 | QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress); |
446 | } |
447 | |
448 | void tst_TouchMouse::touchOverMouse() |
449 | { |
450 | // eventItem1 |
451 | // - eventItem2 |
452 | |
453 | QScopedPointer<QQuickView> window(createView()); |
454 | window->setSource(testFileUrl(fileName: "twoitems.qml" )); |
455 | window->show(); |
456 | QQuickViewTestUtil::centerOnScreen(window: window.data()); |
457 | QVERIFY(QTest::qWaitForWindowActive(window.data())); |
458 | QVERIFY(window->rootObject() != nullptr); |
459 | |
460 | EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>(aName: "eventItem1" ); |
461 | QVERIFY(eventItem1); |
462 | EventItem *eventItem2 = window->rootObject()->findChild<EventItem*>(aName: "eventItem2" ); |
463 | QVERIFY(eventItem2); |
464 | |
465 | // bottom item likes mouse, top likes touch |
466 | eventItem1->setAcceptedMouseButtons(Qt::LeftButton); |
467 | eventItem2->acceptTouch = true; |
468 | |
469 | QCOMPARE(eventItem1->eventList.size(), 0); |
470 | QPoint p1 = QPoint(20, 20); |
471 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p1, window: window.data()); |
472 | QQuickTouchUtils::flush(window: window.data()); |
473 | QCOMPARE(eventItem1->eventList.size(), 0); |
474 | QCOMPARE(eventItem2->eventList.size(), 1); |
475 | QCOMPARE(eventItem2->eventList.at(0).type, QEvent::TouchBegin); |
476 | p1 += QPoint(10, 0); |
477 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p1, window: window.data()); |
478 | QQuickTouchUtils::flush(window: window.data()); |
479 | QCOMPARE(eventItem2->eventList.size(), 2); |
480 | QCOMPARE(eventItem2->eventList.at(1).type, QEvent::TouchUpdate); |
481 | QTest::touchEvent(window: window.data(), device).release(touchId: 0, pt: p1, window: window.data()); |
482 | QQuickTouchUtils::flush(window: window.data()); |
483 | QCOMPARE(eventItem2->eventList.size(), 3); |
484 | QCOMPARE(eventItem2->eventList.at(2).type, QEvent::TouchEnd); |
485 | eventItem2->eventList.clear(); |
486 | } |
487 | |
488 | void tst_TouchMouse::mouseOverTouch() |
489 | { |
490 | // eventItem1 |
491 | // - eventItem2 |
492 | |
493 | QScopedPointer<QQuickView> window(createView()); |
494 | window->setSource(testFileUrl(fileName: "twoitems.qml" )); |
495 | window->show(); |
496 | QQuickViewTestUtil::centerOnScreen(window: window.data()); |
497 | QVERIFY(QTest::qWaitForWindowActive(window.data())); |
498 | QVERIFY(window->rootObject() != nullptr); |
499 | |
500 | EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>(aName: "eventItem1" ); |
501 | QVERIFY(eventItem1); |
502 | EventItem *eventItem2 = window->rootObject()->findChild<EventItem*>(aName: "eventItem2" ); |
503 | QVERIFY(eventItem2); |
504 | |
505 | // bottom item likes mouse, top likes touch |
506 | eventItem1->acceptTouch = true; |
507 | eventItem2->setAcceptedMouseButtons(Qt::LeftButton); |
508 | eventItem2->acceptMouse = true; |
509 | |
510 | QPoint p1 = QPoint(20, 20); |
511 | QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); |
512 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p1, window: window.data()); |
513 | QQuickTouchUtils::flush(window: window.data()); |
514 | QCOMPARE(eventItem1->eventList.size(), 0); |
515 | QCOMPARE(eventItem2->eventList.size(), 2); |
516 | QCOMPARE(eventItem2->eventList.at(0).type, QEvent::TouchBegin); |
517 | QCOMPARE(eventItem2->eventList.at(1).type, QEvent::MouseButtonPress); |
518 | |
519 | |
520 | // p1 += QPoint(10, 0); |
521 | // QTest::touchEvent(window.data(), device).move(0, p1, window.data()); |
522 | // QCOMPARE(eventItem2->eventList.size(), 1); |
523 | // QTest::touchEvent(window.data(), device).release(0, p1, window.data()); |
524 | // QCOMPARE(eventItem2->eventList.size(), 1); |
525 | // eventItem2->eventList.clear(); |
526 | } |
527 | |
528 | void tst_TouchMouse::buttonOnFlickable() |
529 | { |
530 | // flickable - height 500 / 1000 |
531 | // - eventItem1 y: 100, height 100 |
532 | // - eventItem2 y: 300, height 100 |
533 | |
534 | QScopedPointer<QQuickView> window(createView()); |
535 | window->setSource(testFileUrl(fileName: "buttononflickable.qml" )); |
536 | window->show(); |
537 | QQuickViewTestUtil::centerOnScreen(window: window.data()); |
538 | QVERIFY(QTest::qWaitForWindowActive(window.data())); |
539 | QVERIFY(window->rootObject() != nullptr); |
540 | |
541 | QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>(aName: "flickable" ); |
542 | QVERIFY(flickable); |
543 | |
544 | // should a mouse area button be clickable on top of flickable? yes :) |
545 | EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>(aName: "eventItem1" ); |
546 | QVERIFY(eventItem1); |
547 | eventItem1->setAcceptedMouseButtons(Qt::LeftButton); |
548 | eventItem1->acceptMouse = true; |
549 | |
550 | // should a touch button be touchable on top of flickable? yes :) |
551 | EventItem *eventItem2 = window->rootObject()->findChild<EventItem*>(aName: "eventItem2" ); |
552 | QVERIFY(eventItem2); |
553 | QCOMPARE(eventItem2->eventList.size(), 0); |
554 | eventItem2->acceptTouch = true; |
555 | |
556 | // wait to avoid getting a double click event |
557 | QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); |
558 | |
559 | // check that buttons are clickable |
560 | // mouse button |
561 | QCOMPARE(eventItem1->eventList.size(), 0); |
562 | QPoint p1 = QPoint(20, 130); |
563 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p1, window: window.data()); |
564 | QQuickTouchUtils::flush(window: window.data()); |
565 | QTRY_COMPARE(eventItem1->eventList.size(), 2); |
566 | QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); |
567 | QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress); |
568 | QTest::touchEvent(window: window.data(), device).release(touchId: 0, pt: p1, window: window.data()); |
569 | QQuickTouchUtils::flush(window: window.data()); |
570 | QCOMPARE(eventItem1->eventList.size(), 5); |
571 | QCOMPARE(eventItem1->eventList.at(2).type, QEvent::TouchEnd); |
572 | QCOMPARE(eventItem1->eventList.at(3).type, QEvent::MouseButtonRelease); |
573 | QCOMPARE(eventItem1->eventList.at(4).type, QEvent::UngrabMouse); |
574 | eventItem1->eventList.clear(); |
575 | |
576 | // touch button |
577 | p1 = QPoint(10, 310); |
578 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p1, window: window.data()); |
579 | QQuickTouchUtils::flush(window: window.data()); |
580 | QCOMPARE(eventItem2->eventList.size(), 1); |
581 | QCOMPARE(eventItem2->eventList.at(0).type, QEvent::TouchBegin); |
582 | QTest::touchEvent(window: window.data(), device).release(touchId: 0, pt: p1, window: window.data()); |
583 | QQuickTouchUtils::flush(window: window.data()); |
584 | QCOMPARE(eventItem2->eventList.size(), 2); |
585 | QCOMPARE(eventItem2->eventList.at(1).type, QEvent::TouchEnd); |
586 | QCOMPARE(eventItem1->eventList.size(), 0); |
587 | eventItem2->eventList.clear(); |
588 | |
589 | // wait to avoid getting a double click event |
590 | QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); |
591 | |
592 | // click above button, no events please |
593 | p1 = QPoint(10, 90); |
594 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p1, window: window.data()); |
595 | QQuickTouchUtils::flush(window: window.data()); |
596 | QCOMPARE(eventItem1->eventList.size(), 0); |
597 | QTest::touchEvent(window: window.data(), device).release(touchId: 0, pt: p1, window: window.data()); |
598 | QQuickTouchUtils::flush(window: window.data()); |
599 | QCOMPARE(eventItem1->eventList.size(), 0); |
600 | eventItem1->eventList.clear(); |
601 | |
602 | // wait to avoid getting a double click event |
603 | QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); |
604 | |
605 | // check that flickable moves - mouse button |
606 | QCOMPARE(eventItem1->eventList.size(), 0); |
607 | p1 = QPoint(10, 110); |
608 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p1, window: window.data()); |
609 | QQuickTouchUtils::flush(window: window.data()); |
610 | QCOMPARE(eventItem1->eventList.size(), 2); |
611 | QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); |
612 | QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress); |
613 | |
614 | QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(c: window.data()); |
615 | QVERIFY(windowPriv->touchMouseId != -1); |
616 | auto pointerEvent = windowPriv->pointerEventInstance(device: QQuickPointerDevice::touchDevices().at(i: 0)); |
617 | QCOMPARE(pointerEvent->point(0)->exclusiveGrabber(), eventItem1); |
618 | QCOMPARE(window->mouseGrabberItem(), eventItem1); |
619 | |
620 | int dragDelta = -qApp->styleHints()->startDragDistance(); |
621 | p1 += QPoint(0, dragDelta); |
622 | QPoint p2 = p1 + QPoint(0, dragDelta); |
623 | QPoint p3 = p2 + QPoint(0, dragDelta); |
624 | QQuickTouchUtils::flush(window: window.data()); |
625 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p1, window: window.data()); |
626 | QQuickTouchUtils::flush(window: window.data()); |
627 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p2, window: window.data()); |
628 | QQuickTouchUtils::flush(window: window.data()); |
629 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p3, window: window.data()); |
630 | QQuickTouchUtils::flush(window: window.data()); |
631 | |
632 | // we cannot really know when the events get grabbed away |
633 | QVERIFY(eventItem1->eventList.size() >= 4); |
634 | QCOMPARE(eventItem1->eventList.at(2).type, QEvent::TouchUpdate); |
635 | QCOMPARE(eventItem1->eventList.at(3).type, QEvent::MouseMove); |
636 | |
637 | QCOMPARE(window->mouseGrabberItem(), flickable); |
638 | QVERIFY(windowPriv->touchMouseId != -1); |
639 | QCOMPARE(pointerEvent->point(0)->exclusiveGrabber(), flickable); |
640 | QVERIFY(flickable->isMovingVertically()); |
641 | |
642 | QTest::touchEvent(window: window.data(), device).release(touchId: 0, pt: p3, window: window.data()); |
643 | QQuickTouchUtils::flush(window: window.data()); |
644 | } |
645 | |
646 | void tst_TouchMouse::touchButtonOnFlickable() |
647 | { |
648 | // flickable - height 500 / 1000 |
649 | // - eventItem1 y: 100, height 100 |
650 | // - eventItem2 y: 300, height 100 |
651 | |
652 | QScopedPointer<QQuickView> window(createView()); |
653 | window->setSource(testFileUrl(fileName: "buttononflickable.qml" )); |
654 | window->show(); |
655 | QQuickViewTestUtil::centerOnScreen(window: window.data()); |
656 | QVERIFY(QTest::qWaitForWindowActive(window.data())); |
657 | QVERIFY(window->rootObject() != nullptr); |
658 | |
659 | QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>(aName: "flickable" ); |
660 | QVERIFY(flickable); |
661 | |
662 | EventItem *eventItem2 = window->rootObject()->findChild<EventItem*>(aName: "eventItem2" ); |
663 | QVERIFY(eventItem2); |
664 | QCOMPARE(eventItem2->eventList.size(), 0); |
665 | eventItem2->acceptTouch = true; |
666 | |
667 | // press via touch, then drag: check that flickable moves and that the button gets ungrabbed |
668 | QCOMPARE(eventItem2->eventList.size(), 0); |
669 | QPoint p1 = QPoint(10, 310); |
670 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p1, window: window.data()); |
671 | QQuickTouchUtils::flush(window: window.data()); |
672 | QCOMPARE(eventItem2->eventList.size(), 1); |
673 | QCOMPARE(eventItem2->eventList.at(0).type, QEvent::TouchBegin); |
674 | |
675 | QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(c: window.data()); |
676 | QVERIFY(windowPriv->touchMouseId == -1); |
677 | auto pointerEvent = windowPriv->pointerEventInstance(device: QQuickPointerDevice::touchDevices().at(i: 0)); |
678 | QCOMPARE(pointerEvent->point(0)->grabberItem(), eventItem2); |
679 | QCOMPARE(window->mouseGrabberItem(), nullptr); |
680 | |
681 | int dragDelta = qApp->styleHints()->startDragDistance() * -0.7; |
682 | p1 += QPoint(0, dragDelta); |
683 | QPoint p2 = p1 + QPoint(0, dragDelta); |
684 | QPoint p3 = p2 + QPoint(0, dragDelta); |
685 | |
686 | QQuickTouchUtils::flush(window: window.data()); |
687 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p1, window: window.data()); |
688 | QQuickTouchUtils::flush(window: window.data()); |
689 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p2, window: window.data()); |
690 | QQuickTouchUtils::flush(window: window.data()); |
691 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p3, window: window.data()); |
692 | QQuickTouchUtils::flush(window: window.data()); |
693 | |
694 | QTRY_COMPARE(eventItem2->touchUngrabCount, 1); |
695 | QVERIFY(eventItem2->eventList.size() > 2); |
696 | QCOMPARE(eventItem2->eventList.at(1).type, QEvent::TouchUpdate); |
697 | QCOMPARE(window->mouseGrabberItem(), flickable); |
698 | QVERIFY(windowPriv->touchMouseId != -1); |
699 | QCOMPARE(pointerEvent->point(0)->grabberItem(), flickable); |
700 | QVERIFY(flickable->isMovingVertically()); |
701 | |
702 | QTest::touchEvent(window: window.data(), device).release(touchId: 0, pt: p3, window: window.data()); |
703 | QQuickTouchUtils::flush(window: window.data()); |
704 | } |
705 | |
706 | void tst_TouchMouse::buttonOnDelayedPressFlickable_data() |
707 | { |
708 | QTest::addColumn<bool>(name: "scrollBeforeDelayIsOver" ); |
709 | QTest::addColumn<bool>(name: "releaseBeforeDelayIsOver" ); |
710 | |
711 | // the item should never see the event, |
712 | // due to the pressDelay which never delivers if we start moving |
713 | QTest::newRow(dataTag: "scroll before press delay is over" ) << true << false; |
714 | |
715 | // after release, the item should see the press and release via event replay (QTBUG-61144) |
716 | QTest::newRow(dataTag: "release before press delay is over" ) << false << true; |
717 | |
718 | // wait until the "button" sees the press but then |
719 | // start moving: the button gets a press and cancel event |
720 | QTest::newRow(dataTag: "scroll after press delay is over" ) << false << false; |
721 | } |
722 | |
723 | void tst_TouchMouse::buttonOnDelayedPressFlickable() |
724 | { |
725 | // flickable - height 500 / 1000 |
726 | // - eventItem1 y: 100, height 100 |
727 | // - eventItem2 y: 300, height 100 |
728 | QFETCH(bool, scrollBeforeDelayIsOver); |
729 | QFETCH(bool, releaseBeforeDelayIsOver); |
730 | |
731 | qApp->setAttribute(attribute: Qt::AA_SynthesizeMouseForUnhandledTouchEvents, on: true); |
732 | filteredEventList.clear(); |
733 | |
734 | QScopedPointer<QQuickView> window(createView()); |
735 | window->setSource(testFileUrl(fileName: "buttononflickable.qml" )); |
736 | window->show(); |
737 | QQuickViewTestUtil::centerOnScreen(window: window.data()); |
738 | QVERIFY(QTest::qWaitForWindowActive(window.data())); |
739 | QVERIFY(window->rootObject() != nullptr); |
740 | |
741 | QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>(aName: "flickable" ); |
742 | QVERIFY(flickable); |
743 | |
744 | window->installEventFilter(filterObj: this); |
745 | |
746 | // wait 600 ms before letting the child see the press event |
747 | flickable->setPressDelay(600); |
748 | |
749 | // should a mouse area button be clickable on top of flickable? yes :) |
750 | EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>(aName: "eventItem1" ); |
751 | QVERIFY(eventItem1); |
752 | eventItem1->setAcceptedMouseButtons(Qt::LeftButton); |
753 | eventItem1->acceptMouse = true; |
754 | |
755 | // should a touch button be touchable on top of flickable? yes :) |
756 | EventItem *eventItem2 = window->rootObject()->findChild<EventItem*>(aName: "eventItem2" ); |
757 | QVERIFY(eventItem2); |
758 | QCOMPARE(eventItem2->eventList.size(), 0); |
759 | eventItem2->acceptTouch = true; |
760 | |
761 | // wait to avoid getting a double click event |
762 | QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); |
763 | QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(c: window.data()); |
764 | QCOMPARE(windowPriv->touchMouseId, -1); // no grabber |
765 | |
766 | // touch press |
767 | QPoint p1 = QPoint(10, 110); |
768 | QPoint pEnd = p1; |
769 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p1, window: window.data()); |
770 | QQuickTouchUtils::flush(window: window.data()); |
771 | |
772 | if (scrollBeforeDelayIsOver || releaseBeforeDelayIsOver) { |
773 | // no events yet: press is delayed |
774 | QCOMPARE(eventItem1->eventList.size(), 0); |
775 | } else { |
776 | // wait until the button sees the press |
777 | QTRY_COMPARE(eventItem1->eventList.size(), 1); |
778 | QCOMPARE(eventItem1->eventList.at(0).type, QEvent::MouseButtonPress); |
779 | QCOMPARE(filteredEventList.count(), 1); |
780 | } |
781 | |
782 | if (!releaseBeforeDelayIsOver) { |
783 | // move the touchpoint: try to flick |
784 | p1 += QPoint(0, -10); |
785 | QPoint p2 = p1 + QPoint(0, -10); |
786 | pEnd = p2 + QPoint(0, -10); |
787 | QQuickTouchUtils::flush(window: window.data()); |
788 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p1, window: window.data()); |
789 | QQuickTouchUtils::flush(window: window.data()); |
790 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p2, window: window.data()); |
791 | QQuickTouchUtils::flush(window: window.data()); |
792 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: pEnd, window: window.data()); |
793 | QQuickTouchUtils::flush(window: window.data()); |
794 | QTRY_VERIFY(flickable->isMovingVertically()); |
795 | |
796 | if (scrollBeforeDelayIsOver) { |
797 | QCOMPARE(eventItem1->eventList.size(), 0); |
798 | QCOMPARE(filteredEventList.count(), 0); |
799 | } else { |
800 | // see at least press, move and ungrab |
801 | QTRY_VERIFY(eventItem1->eventList.size() > 2); |
802 | QCOMPARE(eventItem1->eventList.at(0).type, QEvent::MouseButtonPress); |
803 | QCOMPARE(eventItem1->eventList.last().type, QEvent::UngrabMouse); |
804 | QCOMPARE(filteredEventList.count(), 1); |
805 | } |
806 | |
807 | // flickable should have the mouse grab, and have moved the itemForTouchPointId |
808 | // for the touchMouseId to the new grabber. |
809 | QCOMPARE(window->mouseGrabberItem(), flickable); |
810 | QVERIFY(windowPriv->touchMouseId != -1); |
811 | auto pointerEvent = windowPriv->pointerEventInstance(device: QQuickPointerDevice::touchDevices().at(i: 0)); |
812 | QCOMPARE(pointerEvent->point(0)->grabberItem(), flickable); |
813 | } |
814 | |
815 | QTest::touchEvent(window: window.data(), device).release(touchId: 0, pt: pEnd, window: window.data()); |
816 | QQuickTouchUtils::flush(window: window.data()); |
817 | |
818 | if (releaseBeforeDelayIsOver) { |
819 | // when the touchpoint was released, the child saw the delayed press and the release in sequence |
820 | qCDebug(lcTests) << "expected delivered events: press, release, ungrab" << eventItem1->eventList; |
821 | qCDebug(lcTests) << "expected filtered events: delayed press, release" << filteredEventList; |
822 | QTRY_COMPARE(eventItem1->eventList.size(), 3); |
823 | QCOMPARE(eventItem1->eventList.at(0).type, QEvent::MouseButtonPress); |
824 | QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonRelease); |
825 | QCOMPARE(eventItem1->eventList.last().type, QEvent::UngrabMouse); |
826 | // QQuickWindow filters the delayed press and release |
827 | QCOMPARE(filteredEventList.count(), 2); |
828 | QCOMPARE(filteredEventList.at(0).type, QEvent::MouseButtonPress); |
829 | QCOMPARE(filteredEventList.at(1).type, QEvent::MouseButtonRelease); |
830 | } else { |
831 | // QQuickWindow filters the delayed press if there was one; otherwise nothing |
832 | if (scrollBeforeDelayIsOver) { |
833 | QCOMPARE(filteredEventList.count(), 0); |
834 | } else { |
835 | qCDebug(lcTests) << "expected filtered event: delayed press" << filteredEventList; |
836 | QCOMPARE(filteredEventList.count(), 1); |
837 | QCOMPARE(filteredEventList.at(0).type, QEvent::MouseButtonPress); |
838 | } |
839 | } |
840 | } |
841 | |
842 | void tst_TouchMouse::buttonOnTouch() |
843 | { |
844 | // 400x800 |
845 | // PinchArea - height 400 |
846 | // - eventItem1 y: 100, height 100 |
847 | // - eventItem2 y: 300, height 100 |
848 | // MultiPointTouchArea - height 400 |
849 | // - eventItem1 y: 100, height 100 |
850 | // - eventItem2 y: 300, height 100 |
851 | |
852 | QScopedPointer<QQuickView> window(createView()); |
853 | window->setSource(testFileUrl(fileName: "buttonontouch.qml" )); |
854 | window->show(); |
855 | QQuickViewTestUtil::centerOnScreen(window: window.data()); |
856 | QVERIFY(QTest::qWaitForWindowActive(window.data())); |
857 | QVERIFY(window->rootObject() != nullptr); |
858 | |
859 | QQuickPinchArea *pinchArea = window->rootObject()->findChild<QQuickPinchArea*>(aName: "pincharea" ); |
860 | QVERIFY(pinchArea); |
861 | QQuickItem *button1 = window->rootObject()->findChild<QQuickItem*>(aName: "button1" ); |
862 | QVERIFY(button1); |
863 | EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>(aName: "eventItem1" ); |
864 | QVERIFY(eventItem1); |
865 | EventItem *eventItem2 = window->rootObject()->findChild<EventItem*>(aName: "eventItem2" ); |
866 | QVERIFY(eventItem2); |
867 | |
868 | QQuickMultiPointTouchArea *touchArea = window->rootObject()->findChild<QQuickMultiPointTouchArea*>(aName: "toucharea" ); |
869 | QVERIFY(touchArea); |
870 | EventItem *eventItem3 = window->rootObject()->findChild<EventItem*>(aName: "eventItem3" ); |
871 | QVERIFY(eventItem3); |
872 | EventItem *eventItem4 = window->rootObject()->findChild<EventItem*>(aName: "eventItem4" ); |
873 | QVERIFY(eventItem4); |
874 | |
875 | QTest::QTouchEventSequence touchSeq = QTest::touchEvent(window: window.data(), device, autoCommit: false); |
876 | |
877 | // Test the common case of a mouse area on top of pinch |
878 | eventItem1->setAcceptedMouseButtons(Qt::LeftButton); |
879 | eventItem1->acceptMouse = true; |
880 | |
881 | |
882 | // wait to avoid getting a double click event |
883 | QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); |
884 | |
885 | // Normal touch click |
886 | QPoint p1 = QPoint(10, 110); |
887 | touchSeq.press(touchId: 0, pt: p1, window: window.data()).commit(); |
888 | QQuickTouchUtils::flush(window: window.data()); |
889 | touchSeq.release(touchId: 0, pt: p1, window: window.data()).commit(); |
890 | QQuickTouchUtils::flush(window: window.data()); |
891 | QCOMPARE(eventItem1->eventList.size(), 5); |
892 | QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); |
893 | QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress); |
894 | QCOMPARE(eventItem1->eventList.at(2).type, QEvent::TouchEnd); |
895 | QCOMPARE(eventItem1->eventList.at(3).type, QEvent::MouseButtonRelease); |
896 | QCOMPARE(eventItem1->eventList.at(4).type, QEvent::UngrabMouse); |
897 | eventItem1->eventList.clear(); |
898 | |
899 | // Normal mouse click |
900 | QTest::mouseClick(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
901 | QCOMPARE(eventItem1->eventList.size(), 3); |
902 | QCOMPARE(eventItem1->eventList.at(0).type, QEvent::MouseButtonPress); |
903 | QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonRelease); |
904 | QCOMPARE(eventItem1->eventList.at(2).type, QEvent::UngrabMouse); |
905 | eventItem1->eventList.clear(); |
906 | |
907 | // Pinch starting on the PinchArea should work |
908 | p1 = QPoint(40, 10); |
909 | QPoint p2 = QPoint(60, 10); |
910 | |
911 | // Start the events after each other |
912 | touchSeq.press(touchId: 0, pt: p1, window: window.data()).commit(); |
913 | QQuickTouchUtils::flush(window: window.data()); |
914 | touchSeq.stationary(touchId: 0).press(touchId: 1, pt: p2, window: window.data()).commit(); |
915 | QQuickTouchUtils::flush(window: window.data()); |
916 | |
917 | QCOMPARE(button1->scale(), 1.0); |
918 | |
919 | // This event seems to be discarded, let's ignore it for now until someone digs into pincharea |
920 | p1 -= QPoint(10, 0); |
921 | p2 += QPoint(10, 0); |
922 | touchSeq.move(touchId: 0, pt: p1, window: window.data()).move(touchId: 1, pt: p2, window: window.data()).commit(); |
923 | QQuickTouchUtils::flush(window: window.data()); |
924 | |
925 | p1 -= QPoint(10, 0); |
926 | p2 += QPoint(10, 0); |
927 | touchSeq.move(touchId: 0, pt: p1, window: window.data()).move(touchId: 1, pt: p2, window: window.data()).commit(); |
928 | QQuickTouchUtils::flush(window: window.data()); |
929 | // QCOMPARE(button1->scale(), 1.5); |
930 | qDebug() << "Button scale: " << button1->scale(); |
931 | |
932 | p1 -= QPoint(10, 0); |
933 | p2 += QPoint(10, 0); |
934 | touchSeq.move(touchId: 0, pt: p1, window: window.data()).move(touchId: 1, pt: p2, window: window.data()).commit(); |
935 | QQuickTouchUtils::flush(window: window.data()); |
936 | // QCOMPARE(button1->scale(), 2.0); |
937 | qDebug() << "Button scale: " << button1->scale(); |
938 | |
939 | touchSeq.release(touchId: 0, pt: p1, window: window.data()).release(touchId: 1, pt: p2, window: window.data()).commit(); |
940 | QQuickTouchUtils::flush(window: window.data()); |
941 | // QVERIFY(eventItem1->eventList.isEmpty()); |
942 | // QCOMPARE(button1->scale(), 2.0); |
943 | qDebug() << "Button scale: " << button1->scale(); |
944 | |
945 | |
946 | // wait to avoid getting a double click event |
947 | QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); |
948 | |
949 | // Start pinching while on the button |
950 | button1->setScale(1.0); |
951 | p1 = QPoint(40, 110); |
952 | p2 = QPoint(60, 110); |
953 | touchSeq.press(touchId: 0, pt: p1, window: window.data()).press(touchId: 1, pt: p2, window: window.data()).commit(); |
954 | QQuickTouchUtils::flush(window: window.data()); |
955 | QCOMPARE(button1->scale(), 1.0); |
956 | QCOMPARE(eventItem1->eventList.count(), 2); |
957 | QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); |
958 | QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress); |
959 | |
960 | // This event seems to be discarded, let's ignore it for now until someone digs into pincharea |
961 | p1 -= QPoint(10, 0); |
962 | p2 += QPoint(10, 0); |
963 | touchSeq.move(touchId: 0, pt: p1, window: window.data()).move(touchId: 1, pt: p2, window: window.data()).commit(); |
964 | QQuickTouchUtils::flush(window: window.data()); |
965 | |
966 | p1 -= QPoint(10, 0); |
967 | p2 += QPoint(10, 0); |
968 | touchSeq.move(touchId: 0, pt: p1, window: window.data()).move(touchId: 1, pt: p2, window: window.data()).commit(); |
969 | QQuickTouchUtils::flush(window: window.data()); |
970 | //QCOMPARE(button1->scale(), 1.5); |
971 | qDebug() << button1->scale(); |
972 | |
973 | p1 -= QPoint(10, 0); |
974 | p2 += QPoint(10, 0); |
975 | touchSeq.move(touchId: 0, pt: p1, window: window.data()).move(touchId: 1, pt: p2, window: window.data()).commit(); |
976 | QQuickTouchUtils::flush(window: window.data()); |
977 | qDebug() << button1->scale(); |
978 | //QCOMPARE(button1->scale(), 2.0); |
979 | |
980 | touchSeq.release(touchId: 0, pt: p1, window: window.data()).release(touchId: 1, pt: p2, window: window.data()).commit(); |
981 | QQuickTouchUtils::flush(window: window.data()); |
982 | // QCOMPARE(eventItem1->eventList.size(), 99); |
983 | qDebug() << button1->scale(); |
984 | //QCOMPARE(button1->scale(), 2.0); |
985 | } |
986 | |
987 | void tst_TouchMouse::pinchOnFlickable() |
988 | { |
989 | QScopedPointer<QQuickView> window(createView()); |
990 | window->setSource(testFileUrl(fileName: "pinchonflickable.qml" )); |
991 | window->show(); |
992 | QQuickViewTestUtil::centerOnScreen(window: window.data()); |
993 | QVERIFY(QTest::qWaitForWindowActive(window.data())); |
994 | QVERIFY(window->rootObject() != nullptr); |
995 | |
996 | QQuickPinchArea *pinchArea = window->rootObject()->findChild<QQuickPinchArea*>(aName: "pincharea" ); |
997 | QVERIFY(pinchArea); |
998 | QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>(aName: "flickable" ); |
999 | QVERIFY(flickable); |
1000 | QQuickItem *rect = window->rootObject()->findChild<QQuickItem*>(aName: "rect" ); |
1001 | QVERIFY(rect); |
1002 | |
1003 | // flickable - single touch point |
1004 | QCOMPARE(flickable->contentX(), 0.0); |
1005 | QPoint p = QPoint(100, 100); |
1006 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p, window: window.data()); |
1007 | QQuickTouchUtils::flush(window: window.data()); |
1008 | QCOMPARE(rect->position(), QPointF(200.0, 200.0)); |
1009 | p -= QPoint(10, 0); |
1010 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p, window: window.data()); |
1011 | QQuickTouchUtils::flush(window: window.data()); |
1012 | p -= QPoint(10, 0); |
1013 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p, window: window.data()); |
1014 | QQuickTouchUtils::flush(window: window.data()); |
1015 | p -= QPoint(10, 0); |
1016 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p, window: window.data()); |
1017 | QQuickTouchUtils::flush(window: window.data()); |
1018 | p -= QPoint(10, 0); |
1019 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p, window: window.data()); |
1020 | QQuickTouchUtils::flush(window: window.data()); |
1021 | QTest::touchEvent(window: window.data(), device).release(touchId: 0, pt: p, window: window.data()); |
1022 | QQuickTouchUtils::flush(window: window.data()); |
1023 | |
1024 | QGuiApplication::processEvents(); |
1025 | QTest::qWait(ms: 10); |
1026 | QVERIFY(!flickable->isAtXBeginning()); |
1027 | // wait until flicking is done |
1028 | QTRY_VERIFY(!flickable->isFlicking()); |
1029 | |
1030 | // pinch |
1031 | QPoint p1 = QPoint(40, 20); |
1032 | QPoint p2 = QPoint(60, 20); |
1033 | |
1034 | QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window: window.data(), device); |
1035 | QQuickTouchUtils::flush(window: window.data()); |
1036 | pinchSequence.press(touchId: 0, pt: p1, window: window.data()).commit(); |
1037 | QQuickTouchUtils::flush(window: window.data()); |
1038 | // In order for the stationary point to remember its previous position, |
1039 | // we have to reuse the same pinchSequence object. Otherwise if we let it |
1040 | // be destroyed and then start a new sequence, point 0 will default to being |
1041 | // stationary at 0, 0, and PinchArea will filter out that touchpoint because |
1042 | // it is outside its bounds. |
1043 | pinchSequence.stationary(touchId: 0).press(touchId: 1, pt: p2, window: window.data()).commit(); |
1044 | QQuickTouchUtils::flush(window: window.data()); |
1045 | p1 -= QPoint(10,10); |
1046 | p2 += QPoint(10,10); |
1047 | pinchSequence.move(touchId: 0, pt: p1, window: window.data()).move(touchId: 1, pt: p2, window: window.data()).commit(); |
1048 | QQuickTouchUtils::flush(window: window.data()); |
1049 | QCOMPARE(rect->scale(), 1.0); |
1050 | p1 -= QPoint(10, 0); |
1051 | p2 += QPoint(10, 0); |
1052 | pinchSequence.move(touchId: 0, pt: p1, window: window.data()).move(touchId: 1, pt: p2, window: window.data()).commit(); |
1053 | QQuickTouchUtils::flush(window: window.data()); |
1054 | p1 -= QPoint(10, 0); |
1055 | p2 += QPoint(10, 0); |
1056 | pinchSequence.move(touchId: 0, pt: p1, window: window.data()).move(touchId: 1, pt: p2, window: window.data()).commit(); |
1057 | QQuickTouchUtils::flush(window: window.data()); |
1058 | p1 -= QPoint(10, 0); |
1059 | p2 += QPoint(10, 0); |
1060 | pinchSequence.move(touchId: 0, pt: p1, window: window.data()).move(touchId: 1, pt: p2, window: window.data()).commit(); |
1061 | QVERIFY(!flickable->isDragging()); |
1062 | QQuickTouchUtils::flush(window: window.data()); |
1063 | pinchSequence.release(touchId: 0, pt: p1, window: window.data()).release(touchId: 1, pt: p2, window: window.data()).commit(); |
1064 | QQuickTouchUtils::flush(window: window.data()); |
1065 | QVERIFY(rect->scale() > 1.0); |
1066 | } |
1067 | |
1068 | void tst_TouchMouse::flickableOnPinch() |
1069 | { |
1070 | QScopedPointer<QQuickView> window(createView()); |
1071 | window->setSource(testFileUrl(fileName: "flickableonpinch.qml" )); |
1072 | window->show(); |
1073 | QQuickViewTestUtil::centerOnScreen(window: window.data()); |
1074 | QVERIFY(QTest::qWaitForWindowActive(window.data())); |
1075 | QVERIFY(window->rootObject() != nullptr); |
1076 | |
1077 | QQuickPinchArea *pinchArea = window->rootObject()->findChild<QQuickPinchArea*>(aName: "pincharea" ); |
1078 | QVERIFY(pinchArea); |
1079 | QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>(aName: "flickable" ); |
1080 | QVERIFY(flickable); |
1081 | QQuickItem *rect = window->rootObject()->findChild<QQuickItem*>(aName: "rect" ); |
1082 | QVERIFY(rect); |
1083 | |
1084 | // flickable - single touch point |
1085 | QCOMPARE(flickable->contentX(), 0.0); |
1086 | QPoint p = QPoint(100, 100); |
1087 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p, window: window.data()); |
1088 | QQuickTouchUtils::flush(window: window.data()); |
1089 | QCOMPARE(rect->position(), QPointF(200.0, 200.0)); |
1090 | p -= QPoint(10, 0); |
1091 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p, window: window.data()); |
1092 | QQuickTouchUtils::flush(window: window.data()); |
1093 | p -= QPoint(10, 0); |
1094 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p, window: window.data()); |
1095 | QQuickTouchUtils::flush(window: window.data()); |
1096 | |
1097 | QTest::qWait(ms: 1000); |
1098 | |
1099 | p -= QPoint(10, 0); |
1100 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p, window: window.data()); |
1101 | QQuickTouchUtils::flush(window: window.data()); |
1102 | QTest::touchEvent(window: window.data(), device).release(touchId: 0, pt: p, window: window.data()); |
1103 | QQuickTouchUtils::flush(window: window.data()); |
1104 | |
1105 | QTest::qWait(ms: 1000); |
1106 | |
1107 | //QVERIFY(flickable->isMovingHorizontally()); |
1108 | qDebug() << "Pos: " << rect->position(); |
1109 | // wait until flicking is done |
1110 | QTRY_VERIFY(!flickable->isFlicking()); |
1111 | |
1112 | // pinch |
1113 | QPoint p1 = QPoint(40, 20); |
1114 | QPoint p2 = QPoint(60, 20); |
1115 | QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window: window.data(), device); |
1116 | pinchSequence.press(touchId: 0, pt: p1, window: window.data()).commit(); |
1117 | QQuickTouchUtils::flush(window: window.data()); |
1118 | // In order for the stationary point to remember its previous position, |
1119 | // we have to reuse the same pinchSequence object. Otherwise if we let it |
1120 | // be destroyed and then start a new sequence, point 0 will default to being |
1121 | // stationary at 0, 0, and PinchArea will filter out that touchpoint because |
1122 | // it is outside its bounds. |
1123 | pinchSequence.stationary(touchId: 0).press(touchId: 1, pt: p2, window: window.data()).commit(); |
1124 | QQuickTouchUtils::flush(window: window.data()); |
1125 | p1 -= QPoint(10,10); |
1126 | p2 += QPoint(10,10); |
1127 | pinchSequence.move(touchId: 0, pt: p1, window: window.data()).move(touchId: 1, pt: p2, window: window.data()).commit(); |
1128 | QQuickTouchUtils::flush(window: window.data()); |
1129 | QCOMPARE(rect->scale(), 1.0); |
1130 | p1 -= QPoint(10, 0); |
1131 | p2 += QPoint(10, 0); |
1132 | pinchSequence.move(touchId: 0, pt: p1, window: window.data()).move(touchId: 1, pt: p2, window: window.data()).commit(); |
1133 | QQuickTouchUtils::flush(window: window.data()); |
1134 | p1 -= QPoint(10, 0); |
1135 | p2 += QPoint(10, 0); |
1136 | pinchSequence.move(touchId: 0, pt: p1, window: window.data()).move(touchId: 1, pt: p2, window: window.data()).commit(); |
1137 | QQuickTouchUtils::flush(window: window.data()); |
1138 | p1 -= QPoint(10, 0); |
1139 | p2 += QPoint(10, 0); |
1140 | pinchSequence.move(touchId: 0, pt: p1, window: window.data()).move(touchId: 1, pt: p2, window: window.data()).commit(); |
1141 | QQuickTouchUtils::flush(window: window.data()); |
1142 | pinchSequence.release(touchId: 0, pt: p1, window: window.data()).release(touchId: 1, pt: p2, window: window.data()).commit(); |
1143 | QQuickTouchUtils::flush(window: window.data()); |
1144 | QVERIFY(rect->scale() > 1.0); |
1145 | } |
1146 | |
1147 | void tst_TouchMouse::mouseOnFlickableOnPinch() |
1148 | { |
1149 | QScopedPointer<QQuickView> window(createView()); |
1150 | window->setSource(testFileUrl(fileName: "mouseonflickableonpinch.qml" )); |
1151 | window->show(); |
1152 | QQuickViewTestUtil::centerOnScreen(window: window.data()); |
1153 | QVERIFY(QTest::qWaitForWindowActive(window.data())); |
1154 | QVERIFY(window->rootObject() != nullptr); |
1155 | |
1156 | QRect windowRect = QRect(window->position(), window->size()); |
1157 | QCursor::setPos(windowRect.center()); |
1158 | |
1159 | QQuickPinchArea *pinchArea = window->rootObject()->findChild<QQuickPinchArea*>(aName: "pincharea" ); |
1160 | QVERIFY(pinchArea); |
1161 | QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>(aName: "flickable" ); |
1162 | QVERIFY(flickable); |
1163 | QQuickItem *rect = window->rootObject()->findChild<QQuickItem*>(aName: "rect" ); |
1164 | QVERIFY(rect); |
1165 | |
1166 | // flickable - single touch point |
1167 | QCOMPARE(flickable->contentX(), 0.0); |
1168 | QPoint p = QPoint(100, 100); |
1169 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p, window: window.data()); |
1170 | QQuickTouchUtils::flush(window: window.data()); |
1171 | QCOMPARE(rect->position(), QPointF(200.0, 200.0)); |
1172 | p -= QPoint(10, 0); |
1173 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p, window: window.data()); |
1174 | QQuickTouchUtils::flush(window: window.data()); |
1175 | p -= QPoint(10, 0); |
1176 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p, window: window.data()); |
1177 | QQuickTouchUtils::flush(window: window.data()); |
1178 | p -= QPoint(10, 0); |
1179 | QTest::touchEvent(window: window.data(), device).move(touchId: 0, pt: p, window: window.data()); |
1180 | QQuickTouchUtils::flush(window: window.data()); |
1181 | QTest::touchEvent(window: window.data(), device).release(touchId: 0, pt: p, window: window.data()); |
1182 | QQuickTouchUtils::flush(window: window.data()); |
1183 | |
1184 | //QVERIFY(flickable->isMovingHorizontally()); |
1185 | |
1186 | // Wait for flick to end |
1187 | QTRY_VERIFY(!flickable->isMoving()); |
1188 | qDebug() << "Pos: " << rect->position(); |
1189 | |
1190 | // pinch |
1191 | QPoint p1 = QPoint(40, 20); |
1192 | QPoint p2 = QPoint(60, 20); |
1193 | QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window: window.data(), device); |
1194 | pinchSequence.press(touchId: 0, pt: p1, window: window.data()).commit(); |
1195 | QQuickTouchUtils::flush(window: window.data()); |
1196 | // In order for the stationary point to remember its previous position, |
1197 | // we have to reuse the same pinchSequence object. Otherwise if we let it |
1198 | // be destroyed and then start a new sequence, point 0 will default to being |
1199 | // stationary at 0, 0, and PinchArea will filter out that touchpoint because |
1200 | // it is outside its bounds. |
1201 | pinchSequence.stationary(touchId: 0).press(touchId: 1, pt: p2, window: window.data()).commit(); |
1202 | QQuickTouchUtils::flush(window: window.data()); |
1203 | p1 -= QPoint(10,10); |
1204 | p2 += QPoint(10,10); |
1205 | pinchSequence.move(touchId: 0, pt: p1, window: window.data()).move(touchId: 1, pt: p2, window: window.data()).commit(); |
1206 | QQuickTouchUtils::flush(window: window.data()); |
1207 | QCOMPARE(rect->scale(), 1.0); |
1208 | p1 -= QPoint(10, 0); |
1209 | p2 += QPoint(10, 0); |
1210 | pinchSequence.move(touchId: 0, pt: p1, window: window.data()).move(touchId: 1, pt: p2, window: window.data()).commit(); |
1211 | QQuickTouchUtils::flush(window: window.data()); |
1212 | p1 -= QPoint(10, 0); |
1213 | p2 += QPoint(10, 0); |
1214 | pinchSequence.move(touchId: 0, pt: p1, window: window.data()).move(touchId: 1, pt: p2, window: window.data()).commit(); |
1215 | QQuickTouchUtils::flush(window: window.data()); |
1216 | p1 -= QPoint(10, 0); |
1217 | p2 += QPoint(10, 0); |
1218 | pinchSequence.move(touchId: 0, pt: p1, window: window.data()).move(touchId: 1, pt: p2, window: window.data()).commit(); |
1219 | QQuickTouchUtils::flush(window: window.data()); |
1220 | pinchSequence.release(touchId: 0, pt: p1, window: window.data()).release(touchId: 1, pt: p2, window: window.data()).commit(); |
1221 | QQuickTouchUtils::flush(window: window.data()); |
1222 | QVERIFY(rect->scale() > 1.0); |
1223 | |
1224 | // PinchArea should steal the event after flicking started |
1225 | rect->setScale(1.0); |
1226 | flickable->setContentX(0.0); |
1227 | p = QPoint(100, 100); |
1228 | pinchSequence.press(touchId: 0, pt: p, window: window.data()).commit(); |
1229 | QQuickTouchUtils::flush(window: window.data()); |
1230 | QCOMPARE(rect->position(), QPointF(200.0, 200.0)); |
1231 | p -= QPoint(10, 0); |
1232 | pinchSequence.move(touchId: 0, pt: p, window: window.data()).commit(); |
1233 | QQuickTouchUtils::flush(window: window.data()); |
1234 | p -= QPoint(10, 0); |
1235 | pinchSequence.move(touchId: 0, pt: p, window: window.data()).commit(); |
1236 | QQuickTouchUtils::flush(window: window.data()); |
1237 | QGuiApplication::processEvents(); |
1238 | p -= QPoint(10, 0); |
1239 | pinchSequence.move(touchId: 0, pt: p, window: window.data()).commit(); |
1240 | QQuickTouchUtils::flush(window: window.data()); |
1241 | |
1242 | QCOMPARE(window->mouseGrabberItem(), flickable); |
1243 | |
1244 | // Add a second finger, this should lead to stealing |
1245 | p1 = QPoint(40, 100); |
1246 | p2 = QPoint(60, 100); |
1247 | pinchSequence.stationary(touchId: 0).press(touchId: 1, pt: p2, window: window.data()).commit(); |
1248 | QQuickTouchUtils::flush(window: window.data()); |
1249 | QCOMPARE(rect->scale(), 1.0); |
1250 | |
1251 | p1 -= QPoint(5, 0); |
1252 | p2 += QPoint(5, 0); |
1253 | pinchSequence.move(touchId: 0, pt: p1, window: window.data()).move(touchId: 1, pt: p2, window: window.data()).commit(); |
1254 | QQuickTouchUtils::flush(window: window.data()); |
1255 | p1 -= QPoint(5, 0); |
1256 | p2 += QPoint(5, 0); |
1257 | pinchSequence.move(touchId: 0, pt: p1, window: window.data()).move(touchId: 1, pt: p2, window: window.data()).commit(); |
1258 | QQuickTouchUtils::flush(window: window.data()); |
1259 | p1 -= QPoint(5, 0); |
1260 | p2 += QPoint(5, 0); |
1261 | pinchSequence.move(touchId: 0, pt: p1, window: window.data()).move(touchId: 1, pt: p2, window: window.data()).commit(); |
1262 | QQuickTouchUtils::flush(window: window.data()); |
1263 | pinchSequence.release(touchId: 0, pt: p1, window: window.data()).release(touchId: 1, pt: p2, window: window.data()).commit(); |
1264 | QQuickTouchUtils::flush(window: window.data()); |
1265 | QVERIFY(rect->scale() > 1.0); |
1266 | pinchSequence.release(touchId: 0, pt: p, window: window.data()).commit(); |
1267 | QQuickTouchUtils::flush(window: window.data()); |
1268 | } |
1269 | |
1270 | /* |
1271 | Regression test for the following use case: |
1272 | You have two mouse areas, on on top of the other. |
1273 | 1 - You tap the top one. |
1274 | 2 - That top mouse area receives a mouse press event but doesn't accept it |
1275 | Expected outcome: |
1276 | 3 - the bottom mouse area gets clicked (besides press and release mouse events) |
1277 | Bogus outcome: |
1278 | 3 - the bottom mouse area gets double clicked. |
1279 | */ |
1280 | void tst_TouchMouse::tapOnDismissiveTopMouseAreaClicksBottomOne() |
1281 | { |
1282 | QScopedPointer<QQuickView> window(createView()); |
1283 | window->setSource(testFileUrl(fileName: "twoMouseAreas.qml" )); |
1284 | window->show(); |
1285 | QQuickViewTestUtil::centerOnScreen(window: window.data()); |
1286 | QVERIFY(QTest::qWaitForWindowActive(window.data())); |
1287 | QVERIFY(window->rootObject() != nullptr); |
1288 | |
1289 | QQuickMouseArea *bottomMouseArea = |
1290 | window->rootObject()->findChild<QQuickMouseArea*>(aName: "rear mouseArea" ); |
1291 | |
1292 | QSignalSpy bottomClickedSpy(bottomMouseArea, SIGNAL(clicked(QQuickMouseEvent*))); |
1293 | QSignalSpy bottomDoubleClickedSpy(bottomMouseArea, |
1294 | SIGNAL(doubleClicked(QQuickMouseEvent*))); |
1295 | |
1296 | // tap the front mouse area (see qml file) |
1297 | QPoint p1(20, 20); |
1298 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p1, window: window.data()); |
1299 | QQuickTouchUtils::flush(window: window.data()); |
1300 | QTest::touchEvent(window: window.data(), device).release(touchId: 0, pt: p1, window: window.data()); |
1301 | QQuickTouchUtils::flush(window: window.data()); |
1302 | |
1303 | QCOMPARE(bottomClickedSpy.count(), 1); |
1304 | QCOMPARE(bottomDoubleClickedSpy.count(), 0); |
1305 | |
1306 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p1, window: window.data()); |
1307 | QQuickTouchUtils::flush(window: window.data()); |
1308 | QTest::touchEvent(window: window.data(), device).release(touchId: 0, pt: p1, window: window.data()); |
1309 | QQuickTouchUtils::flush(window: window.data()); |
1310 | |
1311 | QCOMPARE(bottomClickedSpy.count(), 1); |
1312 | QCOMPARE(bottomDoubleClickedSpy.count(), 1); |
1313 | } |
1314 | |
1315 | /* |
1316 | If an item grabs a touch that is currently being used for mouse pointer emulation, |
1317 | the current mouse grabber should lose the mouse as mouse events will no longer |
1318 | be generated from that touch point. |
1319 | */ |
1320 | void tst_TouchMouse::touchGrabCausesMouseUngrab() |
1321 | { |
1322 | QScopedPointer<QQuickView> window(createView()); |
1323 | window->setSource(testFileUrl(fileName: "twosiblingitems.qml" )); |
1324 | window->show(); |
1325 | QQuickViewTestUtil::centerOnScreen(window: window.data()); |
1326 | QVERIFY(QTest::qWaitForWindowActive(window.data())); |
1327 | QVERIFY(window->rootObject() != nullptr); |
1328 | |
1329 | EventItem *leftItem = window->rootObject()->findChild<EventItem*>(aName: "leftItem" ); |
1330 | QVERIFY(leftItem); |
1331 | |
1332 | EventItem *rightItem = window->rootObject()->findChild<EventItem*>(aName: "rightItem" ); |
1333 | QVERIFY(leftItem); |
1334 | |
1335 | // Send a touch to the leftItem. But leftItem accepts only mouse events, thus |
1336 | // a mouse event will be synthesized out of this touch and will get accepted by |
1337 | // leftItem. |
1338 | leftItem->acceptMouse = true; |
1339 | leftItem->setAcceptedMouseButtons(Qt::LeftButton); |
1340 | QPoint p1; |
1341 | p1 = QPoint(leftItem->width() / 2, leftItem->height() / 2); |
1342 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p1, window: window.data()); |
1343 | QQuickTouchUtils::flush(window: window.data()); |
1344 | QCOMPARE(leftItem->eventList.size(), 2); |
1345 | QCOMPARE(leftItem->eventList.at(0).type, QEvent::TouchBegin); |
1346 | QCOMPARE(leftItem->eventList.at(1).type, QEvent::MouseButtonPress); |
1347 | QCOMPARE(window->mouseGrabberItem(), leftItem); |
1348 | leftItem->eventList.clear(); |
1349 | |
1350 | rightItem->acceptTouch = true; |
1351 | { |
1352 | QVector<int> ids; |
1353 | ids.append(t: leftItem->point0); |
1354 | rightItem->grabTouchPoints(ids); |
1355 | } |
1356 | |
1357 | // leftItem should have lost the mouse as the touch point that was being used to emulate it |
1358 | // has been grabbed by another item. |
1359 | QCOMPARE(leftItem->eventList.size(), 1); |
1360 | QCOMPARE(leftItem->eventList.at(0).type, QEvent::UngrabMouse); |
1361 | QCOMPARE(window->mouseGrabberItem(), (QQuickItem*)nullptr); |
1362 | } |
1363 | |
1364 | void tst_TouchMouse::touchPointDeliveryOrder() |
1365 | { |
1366 | // Touch points should be first delivered to the item under the primary finger |
1367 | QScopedPointer<QQuickView> window(createView()); |
1368 | window->setSource(testFileUrl(fileName: "touchpointdeliveryorder.qml" )); |
1369 | window->show(); |
1370 | QQuickViewTestUtil::centerOnScreen(window: window.data()); |
1371 | QVERIFY(QTest::qWaitForWindowActive(window.data())); |
1372 | |
1373 | /* |
1374 | The items are positioned from left to right: |
1375 | | background | |
1376 | | left | |
1377 | | | right | |
1378 | | middle | |
1379 | 0 150 300 450 600 |
1380 | */ |
1381 | QPoint pLeft = QPoint(100, 100); |
1382 | QPoint pRight = QPoint(500, 100); |
1383 | QPoint pLeftMiddle = QPoint(200, 100); |
1384 | QPoint pRightMiddle = QPoint(350, 100); |
1385 | |
1386 | QTest::QTouchEventSequence touchSeq = QTest::touchEvent(window: window.data(), device, autoCommit: false); |
1387 | |
1388 | QVector<QQuickItem*> events; |
1389 | EventItem *background = window->rootObject()->findChild<EventItem*>(aName: "background" ); |
1390 | EventItem *left = window->rootObject()->findChild<EventItem*>(aName: "left" ); |
1391 | EventItem *middle = window->rootObject()->findChild<EventItem*>(aName: "middle" ); |
1392 | EventItem *right = window->rootObject()->findChild<EventItem*>(aName: "right" ); |
1393 | QVERIFY(background); |
1394 | QVERIFY(left); |
1395 | QVERIFY(middle); |
1396 | QVERIFY(right); |
1397 | connect(sender: background, signal: &EventItem::onTouchEvent, slot: [&events](QQuickItem* receiver){ events.append(t: receiver); }); |
1398 | connect(sender: left, signal: &EventItem::onTouchEvent, slot: [&events](QQuickItem* receiver){ events.append(t: receiver); }); |
1399 | connect(sender: middle, signal: &EventItem::onTouchEvent, slot: [&events](QQuickItem* receiver){ events.append(t: receiver); }); |
1400 | connect(sender: right, signal: &EventItem::onTouchEvent, slot: [&events](QQuickItem* receiver){ events.append(t: receiver); }); |
1401 | |
1402 | touchSeq.press(touchId: 0, pt: pLeft, window: window.data()).commit(); |
1403 | QQuickTouchUtils::flush(window: window.data()); |
1404 | |
1405 | // Touch on left, then background |
1406 | QCOMPARE(events.size(), 2); |
1407 | QCOMPARE(events.at(0), left); |
1408 | QCOMPARE(events.at(1), background); |
1409 | events.clear(); |
1410 | |
1411 | // New press events are deliverd first, the stationary point was not accepted, thus it doesn't get delivered |
1412 | touchSeq.stationary(touchId: 0).press(touchId: 1, pt: pRightMiddle, window: window.data()).commit(); |
1413 | QQuickTouchUtils::flush(window: window.data()); |
1414 | QCOMPARE(events.size(), 3); |
1415 | QCOMPARE(events.at(0), middle); |
1416 | QCOMPARE(events.at(1), right); |
1417 | QCOMPARE(events.at(2), background); |
1418 | events.clear(); |
1419 | |
1420 | touchSeq.release(touchId: 0, pt: pLeft, window: window.data()).release(touchId: 1, pt: pRightMiddle, window: window.data()).commit(); |
1421 | QQuickTouchUtils::flush(window: window.data()); |
1422 | QCOMPARE(events.size(), 0); // no accepted events |
1423 | |
1424 | // Two presses, the first point should come first |
1425 | touchSeq.press(touchId: 0, pt: pLeft, window: window.data()).press(touchId: 1, pt: pRight, window: window.data()).commit(); |
1426 | QQuickTouchUtils::flush(window: window.data()); |
1427 | QCOMPARE(events.size(), 3); |
1428 | QCOMPARE(events.at(0), left); |
1429 | QCOMPARE(events.at(1), right); |
1430 | QCOMPARE(events.at(2), background); |
1431 | touchSeq.release(touchId: 0, pt: pLeft, window: window.data()).release(touchId: 1, pt: pRight, window: window.data()).commit(); |
1432 | events.clear(); |
1433 | |
1434 | // Again, pressing right first |
1435 | touchSeq.press(touchId: 0, pt: pRight, window: window.data()).press(touchId: 1, pt: pLeft, window: window.data()).commit(); |
1436 | QQuickTouchUtils::flush(window: window.data()); |
1437 | QCOMPARE(events.size(), 3); |
1438 | QCOMPARE(events.at(0), right); |
1439 | QCOMPARE(events.at(1), left); |
1440 | QCOMPARE(events.at(2), background); |
1441 | touchSeq.release(touchId: 0, pt: pRight, window: window.data()).release(touchId: 1, pt: pLeft, window: window.data()).commit(); |
1442 | events.clear(); |
1443 | |
1444 | // Two presses, both hitting the middle item on top, then branching left and right, then bottom |
1445 | // Each target should be offered the events exactly once, middle first, left must come before right (id 0) |
1446 | touchSeq.press(touchId: 0, pt: pLeftMiddle, window: window.data()).press(touchId: 1, pt: pRightMiddle, window: window.data()).commit(); |
1447 | QCOMPARE(events.size(), 4); |
1448 | QCOMPARE(events.at(0), middle); |
1449 | QCOMPARE(events.at(1), left); |
1450 | QCOMPARE(events.at(2), right); |
1451 | QCOMPARE(events.at(3), background); |
1452 | touchSeq.release(touchId: 0, pt: pLeftMiddle, window: window.data()).release(touchId: 1, pt: pRightMiddle, window: window.data()).commit(); |
1453 | events.clear(); |
1454 | |
1455 | touchSeq.press(touchId: 0, pt: pRightMiddle, window: window.data()).press(touchId: 1, pt: pLeftMiddle, window: window.data()).commit(); |
1456 | qDebug() << events; |
1457 | QCOMPARE(events.size(), 4); |
1458 | QCOMPARE(events.at(0), middle); |
1459 | QCOMPARE(events.at(1), right); |
1460 | QCOMPARE(events.at(2), left); |
1461 | QCOMPARE(events.at(3), background); |
1462 | touchSeq.release(touchId: 0, pt: pRightMiddle, window: window.data()).release(touchId: 1, pt: pLeftMiddle, window: window.data()).commit(); |
1463 | } |
1464 | |
1465 | void tst_TouchMouse::hoverEnabled() |
1466 | { |
1467 | // QTouchDevice *device = new QTouchDevice; |
1468 | // device->setType(QTouchDevice::TouchScreen); |
1469 | // QWindowSystemInterface::registerTouchDevice(device); |
1470 | |
1471 | QScopedPointer<QQuickView> window(createView()); |
1472 | window->setSource(testFileUrl(fileName: "hoverMouseAreas.qml" )); |
1473 | QQuickViewTestUtil::centerOnScreen(window: window.data()); |
1474 | QQuickViewTestUtil::moveMouseAway(window: window.data()); |
1475 | window->show(); |
1476 | QVERIFY(QTest::qWaitForWindowActive(window.data())); |
1477 | QQuickItem *root = window->rootObject(); |
1478 | QVERIFY(root != nullptr); |
1479 | |
1480 | QQuickMouseArea *mouseArea1 = root->findChild<QQuickMouseArea*>(aName: "mouseArea1" ); |
1481 | QVERIFY(mouseArea1 != nullptr); |
1482 | |
1483 | QQuickMouseArea *mouseArea2 = root->findChild<QQuickMouseArea*>(aName: "mouseArea2" ); |
1484 | QVERIFY(mouseArea2 != nullptr); |
1485 | |
1486 | QSignalSpy enterSpy1(mouseArea1, SIGNAL(entered())); |
1487 | QSignalSpy exitSpy1(mouseArea1, SIGNAL(exited())); |
1488 | QSignalSpy clickSpy1(mouseArea1, SIGNAL(clicked(QQuickMouseEvent *))); |
1489 | |
1490 | QSignalSpy enterSpy2(mouseArea2, SIGNAL(entered())); |
1491 | QSignalSpy exitSpy2(mouseArea2, SIGNAL(exited())); |
1492 | QSignalSpy clickSpy2(mouseArea2, SIGNAL(clicked(QQuickMouseEvent *))); |
1493 | |
1494 | QPoint p1(150, 150); |
1495 | QPoint p2(150, 250); |
1496 | |
1497 | // ------------------------- Mouse move to mouseArea1 |
1498 | QTest::mouseMove(window: window.data(), pos: p1); |
1499 | |
1500 | QVERIFY(enterSpy1.count() == 1); |
1501 | QVERIFY(mouseArea1->hovered()); |
1502 | QVERIFY(!mouseArea2->hovered()); |
1503 | |
1504 | // ------------------------- Touch click on mouseArea1 |
1505 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p1, window: window.data()); |
1506 | |
1507 | QCOMPARE(enterSpy1.count(), 1); |
1508 | QCOMPARE(enterSpy2.count(), 0); |
1509 | QVERIFY(mouseArea1->pressed()); |
1510 | QVERIFY(mouseArea1->hovered()); |
1511 | QVERIFY(!mouseArea2->hovered()); |
1512 | |
1513 | QTest::touchEvent(window: window.data(), device).release(touchId: 0, pt: p1, window: window.data()); |
1514 | QVERIFY(clickSpy1.count() == 1); |
1515 | QVERIFY(mouseArea1->hovered()); |
1516 | QVERIFY(!mouseArea2->hovered()); |
1517 | |
1518 | // ------------------------- Touch click on mouseArea2 |
1519 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p2, window: window.data()); |
1520 | |
1521 | QVERIFY(mouseArea1->hovered()); |
1522 | QVERIFY(mouseArea2->hovered()); |
1523 | QVERIFY(mouseArea2->pressed()); |
1524 | QCOMPARE(enterSpy1.count(), 1); |
1525 | QCOMPARE(enterSpy2.count(), 1); |
1526 | |
1527 | QTest::touchEvent(window: window.data(), device).release(touchId: 0, pt: p2, window: window.data()); |
1528 | |
1529 | QVERIFY(clickSpy2.count() == 1); |
1530 | QVERIFY(mouseArea1->hovered()); |
1531 | QVERIFY(!mouseArea2->hovered()); |
1532 | QCOMPARE(exitSpy1.count(), 0); |
1533 | QCOMPARE(exitSpy2.count(), 1); |
1534 | |
1535 | // ------------------------- Another touch click on mouseArea1 |
1536 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p1, window: window.data()); |
1537 | |
1538 | QCOMPARE(enterSpy1.count(), 1); |
1539 | QCOMPARE(enterSpy2.count(), 1); |
1540 | QVERIFY(mouseArea1->pressed()); |
1541 | QVERIFY(mouseArea1->hovered()); |
1542 | QVERIFY(!mouseArea2->hovered()); |
1543 | |
1544 | QTest::touchEvent(window: window.data(), device).release(touchId: 0, pt: p1, window: window.data()); |
1545 | QCOMPARE(clickSpy1.count(), 2); |
1546 | QVERIFY(mouseArea1->hovered()); |
1547 | QVERIFY(!mouseArea1->pressed()); |
1548 | QVERIFY(!mouseArea2->hovered()); |
1549 | } |
1550 | |
1551 | void tst_TouchMouse::implicitUngrab() |
1552 | { |
1553 | QScopedPointer<QQuickView> window(createView()); |
1554 | window->setSource(testFileUrl(fileName: "singleitem.qml" )); |
1555 | window->show(); |
1556 | QQuickViewTestUtil::centerOnScreen(window: window.data()); |
1557 | QQuickViewTestUtil::moveMouseAway(window: window.data()); |
1558 | QVERIFY(QTest::qWaitForWindowActive(window.data())); |
1559 | |
1560 | QQuickItem *root = window->rootObject(); |
1561 | QVERIFY(root != nullptr); |
1562 | EventItem *eventItem = root->findChild<EventItem*>(aName: "eventItem1" ); |
1563 | eventItem->acceptMouse = true; |
1564 | QPoint p1(20, 20); |
1565 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p1); |
1566 | |
1567 | QCOMPARE(window->mouseGrabberItem(), eventItem); |
1568 | eventItem->eventList.clear(); |
1569 | eventItem->setEnabled(false); |
1570 | QVERIFY(!eventItem->eventList.isEmpty()); |
1571 | QCOMPARE(eventItem->eventList.at(0).type, QEvent::UngrabMouse); |
1572 | QTest::touchEvent(window: window.data(), device).release(touchId: 0, pt: p1); // clean up potential state |
1573 | |
1574 | eventItem->setEnabled(true); |
1575 | QTest::touchEvent(window: window.data(), device).press(touchId: 0, pt: p1); |
1576 | eventItem->eventList.clear(); |
1577 | eventItem->setVisible(false); |
1578 | QVERIFY(!eventItem->eventList.isEmpty()); |
1579 | QCOMPARE(eventItem->eventList.at(0).type, QEvent::UngrabMouse); |
1580 | QTest::touchEvent(window: window.data(), device).release(touchId: 0, pt: p1); // clean up potential state |
1581 | } |
1582 | QTEST_MAIN(tst_TouchMouse) |
1583 | |
1584 | #include "tst_touchmouse.moc" |
1585 | |
1586 | |