1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2018 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtQml module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include <QtTest/QtTest> |
30 | |
31 | #include <private/qdebug_p.h> |
32 | #include <QtGui/qstylehints.h> |
33 | #include <QtQuick/private/qquickpointerhandler_p.h> |
34 | #include <QtQuick/qquickitem.h> |
35 | #include <QtQuick/qquickview.h> |
36 | |
37 | #include "../../../shared/util.h" |
38 | #include "../../shared/viewtestutil.h" |
39 | |
40 | Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests" ) |
41 | |
42 | class Event |
43 | { |
44 | Q_GADGET |
45 | public: |
46 | enum Destination { |
47 | FilterDestination, |
48 | MouseDestination, |
49 | TouchDestination, |
50 | HandlerDestination |
51 | }; |
52 | Q_ENUM(Destination) |
53 | |
54 | Event(Destination d, QEvent::Type t, Qt::TouchPointState s, int grabTransition, QPointF item, QPointF scene) |
55 | : destination(d), type(t), state(s), grabTransition(grabTransition), posWrtItem(item), posWrtScene(scene) |
56 | {} |
57 | |
58 | Destination destination; |
59 | QEvent::Type type; // if this represents a QEvent that was received |
60 | Qt::TouchPointState state; // if this represents an event (pointer, touch or mouse) |
61 | int grabTransition; // if this represents an onGrabChanged() notification (QQuickEventPoint::GrabTransition) |
62 | QPointF posWrtItem; |
63 | QPointF posWrtScene; |
64 | }; |
65 | |
66 | #ifndef QT_NO_DEBUG_STREAM |
67 | QDebug operator<<(QDebug dbg, const class Event &event) { |
68 | QDebugStateSaver saver(dbg); |
69 | dbg.nospace(); |
70 | dbg << "Event(" ; |
71 | QtDebugUtils::formatQEnum(debug&: dbg, value: event.destination); |
72 | dbg << ' '; |
73 | QtDebugUtils::formatQEnum(debug&: dbg, value: event.type); |
74 | dbg << ' '; |
75 | QtDebugUtils::formatQEnum(debug&: dbg, value: event.state); |
76 | if (event.grabTransition) { |
77 | dbg << ' '; |
78 | QtDebugUtils::formatQEnum(debug&: dbg, value: QQuickEventPoint::GrabTransition(event.grabTransition)); |
79 | } |
80 | dbg << " @ " ; |
81 | QtDebugUtils::formatQPoint(debug&: dbg, point: event.posWrtItem); |
82 | dbg << " S " ; |
83 | QtDebugUtils::formatQPoint(debug&: dbg, point: event.posWrtScene); |
84 | dbg << ')'; |
85 | return dbg; |
86 | } |
87 | #endif |
88 | |
89 | enum { |
90 | NoGrab = 0, |
91 | }; |
92 | |
93 | class EventItem : public QQuickItem |
94 | { |
95 | Q_OBJECT |
96 | public: |
97 | EventItem(QQuickItem *parent = nullptr) |
98 | : QQuickItem(parent), acceptPointer(false), grabPointer(false), acceptMouse(false), acceptTouch(false), filterTouch(false) |
99 | {} |
100 | |
101 | inline int grabTransition(bool accept, Qt::TouchPointState state) { |
102 | return (accept && (state != Qt::TouchPointReleased)) ? (int)QQuickEventPoint::GrabExclusive : (int)NoGrab; |
103 | } |
104 | |
105 | void touchEvent(QTouchEvent *event) |
106 | { |
107 | qCDebug(lcPointerTests) << event << "will accept?" << acceptTouch; |
108 | for (const QTouchEvent::TouchPoint &tp : event->touchPoints()) |
109 | eventList.append(t: Event(Event::TouchDestination, event->type(), tp.state(), grabTransition(accept: acceptTouch, state: tp.state()), tp.pos(), tp.scenePos())); |
110 | event->setAccepted(acceptTouch); |
111 | } |
112 | void mousePressEvent(QMouseEvent *event) |
113 | { |
114 | qCDebug(lcPointerTests) << event; |
115 | eventList.append(t: Event(Event::MouseDestination, event->type(), Qt::TouchPointPressed, grabTransition(accept: acceptMouse, state: Qt::TouchPointPressed), event->pos(), event->windowPos())); |
116 | event->setAccepted(acceptMouse); |
117 | } |
118 | void mouseMoveEvent(QMouseEvent *event) |
119 | { |
120 | qCDebug(lcPointerTests) << event; |
121 | eventList.append(t: Event(Event::MouseDestination, event->type(), Qt::TouchPointMoved, grabTransition(accept: acceptMouse, state: Qt::TouchPointMoved), event->pos(), event->windowPos())); |
122 | event->setAccepted(acceptMouse); |
123 | } |
124 | void mouseReleaseEvent(QMouseEvent *event) |
125 | { |
126 | qCDebug(lcPointerTests) << event; |
127 | eventList.append(t: Event(Event::MouseDestination, event->type(), Qt::TouchPointReleased, grabTransition(accept: acceptMouse, state: Qt::TouchPointReleased), event->pos(), event->windowPos())); |
128 | event->setAccepted(acceptMouse); |
129 | } |
130 | void mouseDoubleClickEvent(QMouseEvent *event) |
131 | { |
132 | qCDebug(lcPointerTests) << event; |
133 | eventList.append(t: Event(Event::MouseDestination, event->type(), Qt::TouchPointPressed, grabTransition(accept: acceptMouse, state: Qt::TouchPointPressed), event->pos(), event->windowPos())); |
134 | event->setAccepted(acceptMouse); |
135 | } |
136 | |
137 | void mouseUngrabEvent() |
138 | { |
139 | qCDebug(lcPointerTests); |
140 | eventList.append(t: Event(Event::MouseDestination, QEvent::UngrabMouse, Qt::TouchPointReleased, QQuickEventPoint::UngrabExclusive, QPoint(0,0), QPoint(0,0))); |
141 | } |
142 | |
143 | bool event(QEvent *event) |
144 | { |
145 | qCDebug(lcPointerTests) << event; |
146 | return QQuickItem::event(event); |
147 | } |
148 | |
149 | QList<Event> eventList; |
150 | bool acceptPointer; |
151 | bool grabPointer; |
152 | bool acceptMouse; |
153 | bool acceptTouch; |
154 | bool filterTouch; // when used as event filter |
155 | |
156 | bool eventFilter(QObject *o, QEvent *event) |
157 | { |
158 | qCDebug(lcPointerTests) << event << o; |
159 | if (event->type() == QEvent::TouchBegin || |
160 | event->type() == QEvent::TouchUpdate || |
161 | event->type() == QEvent::TouchCancel || |
162 | event->type() == QEvent::TouchEnd) { |
163 | QTouchEvent *touch = static_cast<QTouchEvent*>(event); |
164 | for (const QTouchEvent::TouchPoint &tp : touch->touchPoints()) |
165 | eventList.append(t: Event(Event::FilterDestination, event->type(), tp.state(), QQuickEventPoint::GrabExclusive, tp.pos(), tp.scenePos())); |
166 | if (filterTouch) |
167 | event->accept(); |
168 | return true; |
169 | } |
170 | return false; |
171 | } |
172 | }; |
173 | |
174 | #define QCOMPARE_EVENT(i, d, t, s, g) \ |
175 | {\ |
176 | const Event &event = eventItem1->eventList.at(i);\ |
177 | QCOMPARE(event.destination, d);\ |
178 | QCOMPARE(event.type, t);\ |
179 | QCOMPARE(event.state, s);\ |
180 | QCOMPARE(event.grabTransition, g);\ |
181 | }\ |
182 | |
183 | class EventHandler : public QQuickPointerHandler |
184 | { |
185 | public: |
186 | void handlePointerEventImpl(QQuickPointerEvent *event) override |
187 | { |
188 | QQuickPointerHandler::handlePointerEventImpl(event); |
189 | if (!enabled()) |
190 | return; |
191 | if (event->isPressEvent()) |
192 | ++pressEventCount; |
193 | if (event->isReleaseEvent()) |
194 | ++releaseEventCount; |
195 | EventItem *item = qmlobject_cast<EventItem *>(object: target()); |
196 | if (!item) { |
197 | event->point(i: 0)->setGrabberPointerHandler(exclusiveGrabber: this); |
198 | return; |
199 | } |
200 | qCDebug(lcPointerTests) << item->objectName() << event; |
201 | int c = event->pointCount(); |
202 | for (int i = 0; i < c; ++i) { |
203 | QQuickEventPoint *point = event->point(i); |
204 | if (item->acceptPointer) |
205 | point->setAccepted(item->acceptPointer); // does NOT imply a grab |
206 | if (item->grabPointer) |
207 | setExclusiveGrab(point, grab: true); |
208 | qCDebug(lcPointerTests) << " " << i << ":" << point << "accepted?" << item->acceptPointer << "grabbed?" << (point->exclusiveGrabber() == this); |
209 | item->eventList.append(t: Event(Event::HandlerDestination, QEvent::Pointer, |
210 | static_cast<Qt::TouchPointState>(point->state()), |
211 | item->grabPointer ? (int)QQuickEventPoint::GrabExclusive : (int)NoGrab, |
212 | eventPos(point), point->scenePosition())); |
213 | } |
214 | } |
215 | |
216 | void onGrabChanged(QQuickPointerHandler *, QQuickEventPoint::GrabTransition stateChange, QQuickEventPoint *point) override |
217 | { |
218 | EventItem *item = qmlobject_cast<EventItem *>(object: target()); |
219 | if (item) |
220 | item->eventList.append(t: Event(Event::HandlerDestination, QEvent::None, |
221 | static_cast<Qt::TouchPointState>(point->state()), stateChange, eventPos(point), point->scenePosition())); |
222 | } |
223 | |
224 | int pressEventCount = 0; |
225 | int releaseEventCount = 0; |
226 | }; |
227 | |
228 | class tst_PointerHandlers : public QQmlDataTest |
229 | { |
230 | Q_OBJECT |
231 | public: |
232 | tst_PointerHandlers() |
233 | :touchDevice(QTest::createTouchDevice()) |
234 | {} |
235 | |
236 | private slots: |
237 | void initTestCase(); |
238 | |
239 | void touchEventDelivery_data(); |
240 | void touchEventDelivery(); |
241 | void mouseEventDelivery(); |
242 | void touchReleaseOutside_data(); |
243 | void touchReleaseOutside(); |
244 | void dynamicCreation(); |
245 | void handlerInWindow(); |
246 | void dynamicCreationInWindow(); |
247 | |
248 | protected: |
249 | bool eventFilter(QObject *, QEvent *event) |
250 | { |
251 | Qt::TouchPointState tpState; |
252 | switch (event->type()) { |
253 | case QEvent::MouseButtonPress: |
254 | tpState = Qt::TouchPointPressed; |
255 | break; |
256 | case QEvent::MouseMove: |
257 | tpState = Qt::TouchPointMoved; |
258 | break; |
259 | case QEvent::MouseButtonRelease: |
260 | tpState = Qt::TouchPointReleased; |
261 | break; |
262 | default: |
263 | // So far we aren't recording filtered touch events here - they would be quite numerous in some cases |
264 | return false; |
265 | } |
266 | QMouseEvent *me = static_cast<QMouseEvent*>(event); |
267 | filteredEventList.append(t: Event(Event::FilterDestination, event->type(), tpState, |
268 | 0, me->pos(), me->globalPos())); |
269 | return false; |
270 | } |
271 | |
272 | private: |
273 | void createView(QScopedPointer<QQuickView> &window, const char *fileName); |
274 | QTouchDevice *touchDevice; |
275 | QList<Event> filteredEventList; |
276 | }; |
277 | |
278 | void tst_PointerHandlers::createView(QScopedPointer<QQuickView> &window, const char *fileName) |
279 | { |
280 | window.reset(other: new QQuickView); |
281 | // window->setGeometry(0,0,240,320); |
282 | window->setSource(testFileUrl(fileName)); |
283 | QTRY_COMPARE(window->status(), QQuickView::Ready); |
284 | QQuickViewTestUtil::centerOnScreen(window: window.data()); |
285 | QQuickViewTestUtil::moveMouseAway(window: window.data()); |
286 | |
287 | window->show(); |
288 | QVERIFY(QTest::qWaitForWindowActive(window.data())); |
289 | QVERIFY(window->rootObject() != nullptr); |
290 | } |
291 | |
292 | void tst_PointerHandlers::initTestCase() |
293 | { |
294 | QQmlDataTest::initTestCase(); |
295 | qmlRegisterType<EventItem>(uri: "Qt.test" , versionMajor: 1, versionMinor: 0, qmlName: "EventItem" ); |
296 | qmlRegisterType<EventHandler>(uri: "Qt.test" , versionMajor: 1, versionMinor: 0, qmlName: "EventHandler" ); |
297 | } |
298 | |
299 | void tst_PointerHandlers::touchEventDelivery_data() |
300 | { |
301 | QTest::addColumn<bool>(name: "synthMouse" ); // AA_SynthesizeMouseForUnhandledTouchEvents |
302 | QTest::newRow(dataTag: "no synth" ) << false; |
303 | QTest::newRow(dataTag: "synth" ) << true; |
304 | } |
305 | |
306 | void tst_PointerHandlers::touchEventDelivery() |
307 | { |
308 | QFETCH(bool, synthMouse); |
309 | qApp->setAttribute(attribute: Qt::AA_SynthesizeMouseForUnhandledTouchEvents, on: synthMouse); |
310 | |
311 | QScopedPointer<QQuickView> windowPtr; |
312 | createView(window&: windowPtr, fileName: "singleitem.qml" ); |
313 | QQuickView * window = windowPtr.data(); |
314 | |
315 | EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>(aName: "eventItem1" ); |
316 | QVERIFY(eventItem1); |
317 | |
318 | // Do not accept anything |
319 | QPoint p1 = QPoint(20, 20); |
320 | QTest::touchEvent(window, device: touchDevice).press(touchId: 0, pt: p1, window); |
321 | QQuickTouchUtils::flush(window); |
322 | QTRY_COMPARE(eventItem1->eventList.size(), synthMouse ? 3 : 2); |
323 | QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointPressed, NoGrab); |
324 | QCOMPARE_EVENT(1, Event::TouchDestination, QEvent::TouchBegin, Qt::TouchPointPressed, NoGrab); |
325 | if (synthMouse) |
326 | QCOMPARE_EVENT(2, Event::MouseDestination, QEvent::MouseButtonPress, Qt::TouchPointPressed, NoGrab); |
327 | p1 += QPoint(10, 0); |
328 | QTest::touchEvent(window, device: touchDevice).move(touchId: 0, pt: p1, window); |
329 | QQuickTouchUtils::flush(window); |
330 | QCOMPARE(eventItem1->eventList.size(), synthMouse ? 4 : 3); |
331 | QCOMPARE_EVENT(eventItem1->eventList.size() - 1, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointMoved, NoGrab); |
332 | QTest::touchEvent(window, device: touchDevice).release(touchId: 0, pt: p1, window); |
333 | QQuickTouchUtils::flush(window); |
334 | QCOMPARE(eventItem1->eventList.size(), synthMouse ? 5 : 4); |
335 | QCOMPARE_EVENT(eventItem1->eventList.size() - 1, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointReleased, NoGrab); |
336 | eventItem1->eventList.clear(); |
337 | |
338 | // Accept touch |
339 | eventItem1->acceptTouch = true; |
340 | p1 = QPoint(20, 20); |
341 | QTest::touchEvent(window, device: touchDevice).press(touchId: 0, pt: p1, window); |
342 | QQuickTouchUtils::flush(window); |
343 | QCOMPARE(eventItem1->eventList.size(), 2); |
344 | QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointPressed, NoGrab); |
345 | QCOMPARE_EVENT(1, Event::TouchDestination, QEvent::TouchBegin, Qt::TouchPointPressed, QQuickEventPoint::GrabExclusive); |
346 | auto pointerEvent = QQuickWindowPrivate::get(c: window)->pointerEventInstance(device: QQuickPointerDevice::touchDevices().at(i: 0)); |
347 | QCOMPARE(pointerEvent->point(0)->exclusiveGrabber(), eventItem1); |
348 | p1 += QPoint(10, 0); |
349 | QTest::touchEvent(window, device: touchDevice).move(touchId: 0, pt: p1, window); |
350 | QQuickTouchUtils::flush(window); |
351 | QCOMPARE(eventItem1->eventList.size(), 4); |
352 | QCOMPARE_EVENT(2, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointMoved, NoGrab); |
353 | QCOMPARE_EVENT(3, Event::TouchDestination, QEvent::TouchUpdate, Qt::TouchPointMoved, QQuickEventPoint::GrabExclusive); |
354 | QTest::touchEvent(window, device: touchDevice).release(touchId: 0, pt: p1, window); |
355 | QQuickTouchUtils::flush(window); |
356 | QCOMPARE(eventItem1->eventList.size(), 6); |
357 | QCOMPARE_EVENT(4, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointReleased, NoGrab); |
358 | QCOMPARE_EVENT(5, Event::TouchDestination, QEvent::TouchEnd, Qt::TouchPointReleased, NoGrab); |
359 | eventItem1->eventList.clear(); |
360 | |
361 | // wait to avoid getting a double click event |
362 | QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); |
363 | |
364 | // Accept mouse |
365 | eventItem1->acceptTouch = false; |
366 | eventItem1->acceptMouse = true; |
367 | eventItem1->setAcceptedMouseButtons(Qt::LeftButton); |
368 | p1 = QPoint(20, 20); |
369 | QTest::touchEvent(window, device: touchDevice).press(touchId: 0, pt: p1, window); |
370 | QQuickTouchUtils::flush(window); |
371 | QCOMPARE(eventItem1->eventList.size(), synthMouse ? 3 : 2); |
372 | QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointPressed, NoGrab); |
373 | QCOMPARE_EVENT(1, Event::TouchDestination, QEvent::TouchBegin, Qt::TouchPointPressed, NoGrab); |
374 | if (synthMouse) |
375 | QCOMPARE_EVENT(2, Event::MouseDestination, QEvent::MouseButtonPress, Qt::TouchPointPressed, QQuickEventPoint::GrabExclusive); |
376 | QCOMPARE(window->mouseGrabberItem(), synthMouse ? eventItem1 : nullptr); |
377 | |
378 | QPointF localPos = eventItem1->mapFromScene(point: p1); |
379 | QPointF scenePos = p1; // item is at 0,0 |
380 | QCOMPARE(eventItem1->eventList.at(1).posWrtItem, localPos); |
381 | QCOMPARE(eventItem1->eventList.at(1).posWrtScene, scenePos); |
382 | if (synthMouse) { |
383 | QCOMPARE(eventItem1->eventList.at(2).posWrtItem, localPos); |
384 | QCOMPARE(eventItem1->eventList.at(2).posWrtScene, scenePos); |
385 | } |
386 | |
387 | p1 += QPoint(10, 0); |
388 | QTest::touchEvent(window, device: touchDevice).move(touchId: 0, pt: p1, window); |
389 | QQuickTouchUtils::flush(window); |
390 | QCOMPARE(eventItem1->eventList.size(), synthMouse ? 6 : 3); |
391 | if (synthMouse) { |
392 | QCOMPARE_EVENT(3, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointMoved, NoGrab); |
393 | QCOMPARE_EVENT(4, Event::TouchDestination, QEvent::TouchUpdate, Qt::TouchPointMoved, NoGrab); |
394 | QCOMPARE_EVENT(5, Event::MouseDestination, QEvent::MouseMove, Qt::TouchPointMoved, QQuickEventPoint::GrabExclusive); |
395 | } |
396 | QTest::touchEvent(window, device: touchDevice).release(touchId: 0, pt: p1, window); |
397 | QQuickTouchUtils::flush(window); |
398 | QCOMPARE(eventItem1->eventList.size(), synthMouse ? 10 : 4); |
399 | if (synthMouse) { |
400 | QCOMPARE_EVENT(6, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointReleased, NoGrab); |
401 | QCOMPARE_EVENT(7, Event::TouchDestination, QEvent::TouchEnd, Qt::TouchPointReleased, NoGrab); |
402 | QCOMPARE_EVENT(8, Event::MouseDestination, QEvent::MouseButtonRelease, Qt::TouchPointReleased, NoGrab); |
403 | QCOMPARE_EVENT(9, Event::MouseDestination, QEvent::UngrabMouse, Qt::TouchPointReleased, QQuickEventPoint::UngrabExclusive); |
404 | } else { |
405 | QCOMPARE_EVENT(3, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointReleased, NoGrab); |
406 | } |
407 | eventItem1->eventList.clear(); |
408 | |
409 | // wait to avoid getting a double click event |
410 | QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); |
411 | |
412 | // Accept mouse buttons but not the touch event |
413 | eventItem1->acceptTouch = false; |
414 | eventItem1->acceptMouse = false; |
415 | eventItem1->setAcceptedMouseButtons(Qt::LeftButton); |
416 | p1 = QPoint(20, 20); |
417 | QTest::touchEvent(window, device: touchDevice).press(touchId: 0, pt: p1, window); |
418 | QQuickTouchUtils::flush(window); |
419 | QCOMPARE(eventItem1->eventList.size(), synthMouse ? 3 : 2); |
420 | QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointPressed, NoGrab); |
421 | QCOMPARE_EVENT(1, Event::TouchDestination, QEvent::TouchBegin, Qt::TouchPointPressed, NoGrab); |
422 | if (synthMouse) |
423 | QCOMPARE_EVENT(2, Event::MouseDestination, QEvent::MouseButtonPress, Qt::TouchPointPressed, NoGrab); |
424 | QCOMPARE(pointerEvent->point(0)->exclusiveGrabber(), nullptr); |
425 | p1 += QPoint(10, 0); |
426 | QTest::touchEvent(window, device: touchDevice).move(touchId: 0, pt: p1, window); |
427 | QQuickTouchUtils::flush(window); |
428 | QCOMPARE(eventItem1->eventList.size(), synthMouse ? 4 : 3); |
429 | QTest::touchEvent(window, device: touchDevice).release(touchId: 0, pt: p1, window); |
430 | QQuickTouchUtils::flush(window); |
431 | QCOMPARE(eventItem1->eventList.size(), synthMouse ? 5 : 4); |
432 | eventItem1->eventList.clear(); |
433 | |
434 | // wait to avoid getting a double click event |
435 | QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); |
436 | |
437 | // Accept touch |
438 | eventItem1->acceptTouch = true; |
439 | eventItem1->setAcceptedMouseButtons(Qt::LeftButton); |
440 | p1 = QPoint(20, 20); |
441 | QTest::touchEvent(window, device: touchDevice).press(touchId: 0, pt: p1, window); |
442 | QQuickTouchUtils::flush(window); |
443 | QCOMPARE(eventItem1->eventList.size(), 2); |
444 | QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointPressed, NoGrab); |
445 | QCOMPARE_EVENT(1, Event::TouchDestination, QEvent::TouchBegin, Qt::TouchPointPressed, QQuickEventPoint::GrabExclusive); |
446 | p1 += QPoint(10, 0); |
447 | QTest::touchEvent(window, device: touchDevice).move(touchId: 0, pt: p1, window); |
448 | QQuickTouchUtils::flush(window); |
449 | QCOMPARE(eventItem1->eventList.size(), 4); |
450 | QCOMPARE_EVENT(2, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointMoved, NoGrab); |
451 | QCOMPARE_EVENT(3, Event::TouchDestination, QEvent::TouchUpdate, Qt::TouchPointMoved, QQuickEventPoint::GrabExclusive); |
452 | QTest::touchEvent(window, device: touchDevice).release(touchId: 0, pt: p1, window); |
453 | QQuickTouchUtils::flush(window); |
454 | QCOMPARE(eventItem1->eventList.size(), 6); |
455 | QCOMPARE_EVENT(4, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointReleased, NoGrab); |
456 | QCOMPARE_EVENT(5, Event::TouchDestination, QEvent::TouchEnd, Qt::TouchPointReleased, NoGrab); |
457 | eventItem1->eventList.clear(); |
458 | |
459 | // Accept pointer events |
460 | eventItem1->acceptPointer = true; |
461 | eventItem1->grabPointer = true; |
462 | p1 = QPoint(20, 20); |
463 | QTest::touchEvent(window, device: touchDevice).press(touchId: 0, pt: p1, window); |
464 | QQuickTouchUtils::flush(window); |
465 | QCOMPARE(eventItem1->eventList.size(), 2); |
466 | QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::None, Qt::TouchPointPressed, QQuickEventPoint::GrabExclusive); |
467 | QCOMPARE_EVENT(1, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointPressed, QQuickEventPoint::GrabExclusive); |
468 | p1 += QPoint(10, 0); |
469 | QTest::touchEvent(window, device: touchDevice).move(touchId: 0, pt: p1, window); |
470 | QQuickTouchUtils::flush(window); |
471 | QCOMPARE(eventItem1->eventList.size(), 3); |
472 | QCOMPARE_EVENT(2, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointMoved, QQuickEventPoint::GrabExclusive); |
473 | QTest::touchEvent(window, device: touchDevice).release(touchId: 0, pt: p1, window); |
474 | QQuickTouchUtils::flush(window); |
475 | QCOMPARE(eventItem1->eventList.size(), 5); |
476 | qCDebug(lcPointerTests) << eventItem1->eventList; |
477 | QCOMPARE_EVENT(3, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointReleased, QQuickEventPoint::GrabExclusive); |
478 | QCOMPARE_EVENT(4, Event::HandlerDestination, QEvent::None, Qt::TouchPointReleased, QQuickEventPoint::UngrabExclusive); |
479 | eventItem1->eventList.clear(); |
480 | } |
481 | |
482 | void tst_PointerHandlers::mouseEventDelivery() |
483 | { |
484 | QScopedPointer<QQuickView> windowPtr; |
485 | createView(window&: windowPtr, fileName: "singleitem.qml" ); |
486 | QQuickView * window = windowPtr.data(); |
487 | |
488 | EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>(aName: "eventItem1" ); |
489 | QVERIFY(eventItem1); |
490 | |
491 | // Do not accept anything |
492 | QPoint p1 = QPoint(20, 20); |
493 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
494 | QCOMPARE(eventItem1->eventList.size(), 2); |
495 | p1 += QPoint(10, 0); |
496 | QTest::mouseMove(window, pos: p1); |
497 | QCOMPARE(eventItem1->eventList.size(), 3); |
498 | QTest::mouseRelease(window, button: Qt::LeftButton); |
499 | QCOMPARE(eventItem1->eventList.size(), 3); |
500 | eventItem1->eventList.clear(); |
501 | |
502 | // wait to avoid getting a double click event |
503 | QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); |
504 | |
505 | // Accept mouse |
506 | eventItem1->acceptTouch = false; |
507 | eventItem1->acceptMouse = true; |
508 | eventItem1->setAcceptedMouseButtons(Qt::LeftButton); |
509 | p1 = QPoint(20, 20); |
510 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
511 | QCOMPARE(eventItem1->eventList.size(), 2); |
512 | QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointPressed, NoGrab); |
513 | QCOMPARE_EVENT(1, Event::MouseDestination, QEvent::MouseButtonPress, Qt::TouchPointPressed, QQuickEventPoint::GrabExclusive); |
514 | QCOMPARE(window->mouseGrabberItem(), eventItem1); |
515 | |
516 | QPointF localPos = eventItem1->mapFromScene(point: p1); |
517 | QPointF scenePos = p1; // item is at 0,0 |
518 | QCOMPARE(eventItem1->eventList.at(0).posWrtItem, localPos); |
519 | QCOMPARE(eventItem1->eventList.at(0).posWrtScene, scenePos); |
520 | QCOMPARE(eventItem1->eventList.at(1).posWrtItem, localPos); |
521 | QCOMPARE(eventItem1->eventList.at(1).posWrtScene, scenePos); |
522 | |
523 | p1 += QPoint(10, 0); |
524 | QTest::mouseMove(window, pos: p1); |
525 | QCOMPARE(eventItem1->eventList.size(), 3); |
526 | QCOMPARE_EVENT(2, Event::MouseDestination, QEvent::MouseMove, Qt::TouchPointMoved, QQuickEventPoint::GrabExclusive); |
527 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
528 | QCOMPARE(eventItem1->eventList.size(), 5); |
529 | QCOMPARE_EVENT(3, Event::MouseDestination, QEvent::MouseButtonRelease, Qt::TouchPointReleased, NoGrab); |
530 | QCOMPARE_EVENT(4, Event::MouseDestination, QEvent::UngrabMouse, Qt::TouchPointReleased, QQuickEventPoint::UngrabExclusive); |
531 | eventItem1->eventList.clear(); |
532 | |
533 | // wait to avoid getting a double click event |
534 | QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); |
535 | |
536 | // Grab pointer events |
537 | eventItem1->acceptMouse = false; |
538 | eventItem1->acceptPointer = true; |
539 | eventItem1->grabPointer = true; |
540 | p1 = QPoint(20, 20); |
541 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
542 | QTRY_COMPARE(eventItem1->eventList.size(), 3); |
543 | QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::None, Qt::TouchPointPressed, QQuickEventPoint::GrabExclusive); |
544 | QCOMPARE_EVENT(1, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointPressed, QQuickEventPoint::GrabExclusive); |
545 | QCOMPARE_EVENT(2, Event::MouseDestination, QEvent::MouseButtonPress, Qt::TouchPointPressed, 0); |
546 | p1 += QPoint(10, 0); |
547 | QTest::mouseMove(window, pos: p1); |
548 | QCOMPARE(eventItem1->eventList.size(), 4); |
549 | QCOMPARE_EVENT(3, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointMoved, QQuickEventPoint::GrabExclusive); |
550 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
551 | QCOMPARE(eventItem1->eventList.size(), 6); |
552 | QCOMPARE_EVENT(4, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointReleased, QQuickEventPoint::GrabExclusive); |
553 | QCOMPARE_EVENT(5, Event::HandlerDestination, QEvent::None, Qt::TouchPointReleased, QQuickEventPoint::UngrabExclusive); |
554 | eventItem1->eventList.clear(); |
555 | } |
556 | |
557 | void tst_PointerHandlers::touchReleaseOutside_data() |
558 | { |
559 | QTest::addColumn<bool>(name: "acceptPointer" ); |
560 | QTest::addColumn<bool>(name: "grabPointer" ); |
561 | QTest::addColumn<int>(name: "eventCount" ); |
562 | QTest::addColumn<int>(name: "endIndexToTest" ); |
563 | QTest::addColumn<int>(name: "endDestination" ); // Event::Destination |
564 | QTest::addColumn<int>(name: "endType" ); // QEvent::Type |
565 | QTest::addColumn<int>(name: "endState" ); // Qt::TouchPointState |
566 | QTest::addColumn<int>(name: "endGrabState" ); // Qt::TouchPointState |
567 | |
568 | QTest::newRow(dataTag: "reject and ignore" ) << false << false << 6 << 5 << (int)Event::TouchDestination |
569 | << (int)QEvent::TouchEnd << (int)Qt::TouchPointReleased << (int)NoGrab; |
570 | QTest::newRow(dataTag: "reject and grab" ) << false << true << 5 << 4 << (int)Event::HandlerDestination |
571 | << (int)QEvent::None << (int)Qt::TouchPointReleased << (int)QQuickEventPoint::UngrabExclusive; |
572 | QTest::newRow(dataTag: "accept and ignore" ) << true << false << 1 << 0 << (int)Event::HandlerDestination |
573 | << (int)QEvent::Pointer << (int)Qt::TouchPointPressed << (int)NoGrab; |
574 | QTest::newRow(dataTag: "accept and grab" ) << true << true << 5 << 4 << (int)Event::HandlerDestination |
575 | << (int)QEvent::None << (int)Qt::TouchPointReleased << (int)QQuickEventPoint::UngrabExclusive; |
576 | } |
577 | |
578 | void tst_PointerHandlers::touchReleaseOutside() |
579 | { |
580 | QScopedPointer<QQuickView> windowPtr; |
581 | createView(window&: windowPtr, fileName: "singleitem.qml" ); |
582 | QQuickView * window = windowPtr.data(); |
583 | |
584 | QFETCH(bool, acceptPointer); |
585 | QFETCH(bool, grabPointer); |
586 | QFETCH(int, eventCount); |
587 | QFETCH(int, endIndexToTest); |
588 | QFETCH(int, endDestination); |
589 | QFETCH(int, endType); |
590 | QFETCH(int, endState); |
591 | QFETCH(int, endGrabState); |
592 | |
593 | EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>(aName: "eventItem1" ); |
594 | QVERIFY(eventItem1); |
595 | |
596 | eventItem1->acceptTouch = true; |
597 | eventItem1->acceptPointer = acceptPointer; |
598 | eventItem1->grabPointer = grabPointer; |
599 | |
600 | QPoint p1 = QPoint(20, 20); |
601 | QTest::touchEvent(window, device: touchDevice).press(touchId: 0, pt: p1, window); |
602 | QQuickTouchUtils::flush(window); |
603 | p1.setX(eventItem1->mapToScene(point: eventItem1->clipRect().bottomRight()).x() + 10); |
604 | QTest::touchEvent(window, device: touchDevice).move(touchId: 0, pt: p1, window); |
605 | QTest::touchEvent(window, device: touchDevice).release(touchId: 0, pt: p1, window); |
606 | QQuickTouchUtils::flush(window); |
607 | qCDebug(lcPointerTests) << eventItem1->eventList; |
608 | QCOMPARE(eventItem1->eventList.size(), eventCount); |
609 | QCOMPARE_EVENT(endIndexToTest, endDestination, endType, endState, endGrabState); |
610 | } |
611 | |
612 | void tst_PointerHandlers::dynamicCreation() |
613 | { |
614 | QScopedPointer<QQuickView> windowPtr; |
615 | createView(window&: windowPtr, fileName: "dynamicallyCreated.qml" ); |
616 | QQuickView * window = windowPtr.data(); |
617 | |
618 | EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>(aName: "eventItem1" ); |
619 | QVERIFY(eventItem1); |
620 | EventHandler *handler = window->rootObject()->findChild<EventHandler*>(aName: "eventHandler" ); |
621 | QVERIFY(handler); |
622 | |
623 | QCOMPARE(handler->parentItem(), eventItem1); |
624 | QCOMPARE(handler->target(), eventItem1); |
625 | |
626 | QPoint p1(20, 20); |
627 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
628 | QTRY_COMPARE(eventItem1->eventList.size(), 2); |
629 | QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointPressed, NoGrab); |
630 | QCOMPARE_EVENT(1, Event::MouseDestination, QEvent::MouseButtonPress, Qt::TouchPointPressed, NoGrab); |
631 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
632 | } |
633 | |
634 | void tst_PointerHandlers::handlerInWindow() |
635 | { |
636 | QQmlEngine engine; |
637 | QQmlComponent component(&engine); |
638 | component.loadUrl(url: testFileUrl(fileName: "handlerInWindow.qml" )); |
639 | QQuickWindow *window = qobject_cast<QQuickWindow*>(object: component.create()); |
640 | QScopedPointer<QQuickWindow> cleanup(window); |
641 | QVERIFY(window); |
642 | window->show(); |
643 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
644 | |
645 | EventHandler *handler = window->contentItem()->findChild<EventHandler*>(aName: "eventHandler" ); |
646 | QVERIFY(handler); |
647 | |
648 | QCOMPARE(handler->parentItem(), window->contentItem()); |
649 | QCOMPARE(handler->target(), window->contentItem()); |
650 | |
651 | QPoint p1(20, 20); |
652 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
653 | QTRY_COMPARE(handler->pressEventCount, 1); |
654 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
655 | QTRY_COMPARE(handler->releaseEventCount, 1); |
656 | } |
657 | |
658 | void tst_PointerHandlers::dynamicCreationInWindow() |
659 | { |
660 | QQmlEngine engine; |
661 | QQmlComponent component(&engine); |
662 | component.loadUrl(url: testFileUrl(fileName: "dynamicallyCreatedInWindow.qml" )); |
663 | QQuickWindow *window = qobject_cast<QQuickWindow*>(object: component.create()); |
664 | QScopedPointer<QQuickWindow> cleanup(window); |
665 | QVERIFY(window); |
666 | window->show(); |
667 | QVERIFY(QTest::qWaitForWindowExposed(window)); |
668 | |
669 | EventHandler *handler = window->contentItem()->findChild<EventHandler*>(aName: "eventHandler" ); |
670 | QVERIFY(handler); |
671 | |
672 | QCOMPARE(handler->parentItem(), window->contentItem()); |
673 | QCOMPARE(handler->target(), window->contentItem()); |
674 | |
675 | QPoint p1(20, 20); |
676 | QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
677 | QTRY_COMPARE(handler->pressEventCount, 1); |
678 | QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1); |
679 | QTRY_COMPARE(handler->releaseEventCount, 1); |
680 | } |
681 | |
682 | QTEST_MAIN(tst_PointerHandlers) |
683 | |
684 | #include "tst_qquickpointerhandler.moc" |
685 | |
686 | |