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
51Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
52
53struct 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
70QDebug 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
84class EventItem : public QQuickItem
85{
86 Q_OBJECT
87
88Q_SIGNALS:
89 void onTouchEvent(QQuickItem *receiver);
90
91public:
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
171class tst_TouchMouse : public QQmlDataTest
172{
173 Q_OBJECT
174public:
175 tst_TouchMouse()
176 :device(QTest::createTouchDevice())
177 {}
178
179private 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
207protected:
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
219private:
220 QQuickView *createView();
221 QTouchDevice *device;
222 QList<Event> filteredEventList;
223};
224
225QQuickView *tst_TouchMouse::createView()
226{
227 QQuickView *window = new QQuickView(nullptr);
228 return window;
229}
230
231void tst_TouchMouse::initTestCase()
232{
233 QQmlDataTest::initTestCase();
234 qmlRegisterType<EventItem>(uri: "Qt.test", versionMajor: 1, versionMinor: 0, qmlName: "EventItem");
235}
236
237void 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
244void 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
387void 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
417void 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
448void 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
488void 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
528void 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
646void 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
706void 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
723void 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
842void 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
987void 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
1068void 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
1147void 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 */
1280void 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 */
1320void 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
1364void 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
1465void 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
1551void 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}
1582QTEST_MAIN(tst_TouchMouse)
1583
1584#include "tst_touchmouse.moc"
1585
1586

source code of qtdeclarative/tests/auto/quick/touchmouse/tst_touchmouse.cpp