1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
44 #include <QTouchEvent>
45 #include <QtQuick/QQuickItem>
46 #include <QtQuick/QQuickWindow>
47 #include <QtQml/QQmlEngine>
48 #include <QtQml/QQmlComponent>
49 #include <QtQuick/private/qquickrectangle_p.h>
50 #include "../../shared/util.h"
52 #include <private/qquickwindow_p.h>
53 #include <private/qguiapplication_p.h>
55 struct TouchEventData {
59 Qt::TouchPointStates states;
60 QList<QTouchEvent::TouchPoint> touchPoints;
63 static QTouchEvent::TouchPoint makeTouchPoint(QQuickItem *item, const QPointF &p, const QPointF &lastPoint = QPointF())
65 QPointF last = lastPoint.isNull() ? p : lastPoint;
67 QTouchEvent::TouchPoint tp;
71 tp.setScenePos(item->mapToScene(p));
72 tp.setLastScenePos(item->mapToScene(last));
73 tp.setScreenPos(item->window()->mapToGlobal(tp.scenePos().toPoint()));
74 tp.setLastScreenPos(item->window()->mapToGlobal(tp.lastScenePos().toPoint()));
78 static TouchEventData makeTouchData(QEvent::Type type, QWindow *w, Qt::TouchPointStates states = 0,
79 const QList<QTouchEvent::TouchPoint>& touchPoints = QList<QTouchEvent::TouchPoint>())
81 TouchEventData d = { type, 0, w, states, touchPoints };
84 static TouchEventData makeTouchData(QEvent::Type type, QWindow *w, Qt::TouchPointStates states, const QTouchEvent::TouchPoint &touchPoint)
86 QList<QTouchEvent::TouchPoint> points;
88 return makeTouchData(type, w, states, points);
91 #define COMPARE_TOUCH_POINTS(tp1, tp2) \
93 QCOMPARE(tp1.pos(), tp2.pos()); \
94 QCOMPARE(tp1.lastPos(), tp2.lastPos()); \
95 QCOMPARE(tp1.scenePos(), tp2.scenePos()); \
96 QCOMPARE(tp1.lastScenePos(), tp2.lastScenePos()); \
97 QCOMPARE(tp1.screenPos(), tp2.screenPos()); \
98 QCOMPARE(tp1.lastScreenPos(), tp2.lastScreenPos()); \
101 #define COMPARE_TOUCH_DATA(d1, d2) \
103 QCOMPARE((int)d1.type, (int)d2.type); \
104 QCOMPARE(d1.widget, d2.widget); \
105 QCOMPARE((int)d1.states, (int)d2.states); \
106 QCOMPARE(d1.touchPoints.count(), d2.touchPoints.count()); \
107 for (int i=0; i<d1.touchPoints.count(); i++) { \
108 COMPARE_TOUCH_POINTS(d1.touchPoints[i], d2.touchPoints[i]); \
113 class RootItemAccessor : public QQuickItem
118 : m_rootItemDestroyed(false)
122 Q_INVOKABLE QQuickItem *rootItem()
125 QQuickWindowPrivate *c = QQuickWindowPrivate::get(window());
126 m_rootItem = c->rootItem;
127 QObject::connect(m_rootItem, SIGNAL(destroyed()), this, SLOT(rootItemDestroyed()));
131 bool isRootItemDestroyed() {return m_rootItemDestroyed;}
133 void rootItemDestroyed() {
134 m_rootItemDestroyed = true;
138 bool m_rootItemDestroyed;
139 QQuickItem *m_rootItem;
142 class TestTouchItem : public QQuickRectangle
146 TestTouchItem(QQuickItem *parent = 0)
147 : QQuickRectangle(parent), acceptTouchEvents(true), acceptMouseEvents(true),
149 spinLoopWhenPressed(false), touchEventCount(0)
151 border()->setWidth(1);
152 setAcceptedMouseButtons(Qt::LeftButton);
153 setFiltersChildMouseEvents(true);
157 acceptTouchEvents = acceptMouseEvents = true;
161 lastEvent = makeTouchData(QEvent::None, window(), 0, QList<QTouchEvent::TouchPoint>());//CHECK_VALID
163 lastVelocity = lastVelocityFromMouseMove = QVector2D();
164 lastMousePos = QPointF();
165 lastMouseCapabilityFlags = 0;
168 static void clearMousePressCounter()
170 mousePressNum = mouseMoveNum = mouseReleaseNum = 0;
173 void clearTouchEventCounter()
178 bool acceptTouchEvents;
179 bool acceptMouseEvents;
180 TouchEventData lastEvent;
182 bool spinLoopWhenPressed;
184 QVector2D lastVelocity;
185 QVector2D lastVelocityFromMouseMove;
186 QPointF lastMousePos;
187 int lastMouseCapabilityFlags;
189 void touchEvent(QTouchEvent *event) {
190 if (!acceptTouchEvents) {
195 lastEvent = makeTouchData(event->type(), event->window(), event->touchPointStates(), event->touchPoints());
196 if (event->device()->capabilities().testFlag(QTouchDevice::Velocity) && !event->touchPoints().isEmpty()) {
197 lastVelocity = event->touchPoints().first().velocity();
199 lastVelocity = QVector2D();
201 if (spinLoopWhenPressed && event->touchPointStates().testFlag(Qt::TouchPointPressed)) {
202 QCoreApplication::processEvents();
206 void mousePressEvent(QMouseEvent *e) {
207 if (!acceptMouseEvents) {
211 mousePressId = ++mousePressNum;
212 lastMousePos = e->pos();
213 lastMouseCapabilityFlags = QGuiApplicationPrivate::mouseEventCaps(e);
216 void mouseMoveEvent(QMouseEvent *e) {
217 if (!acceptMouseEvents) {
222 lastVelocityFromMouseMove = QGuiApplicationPrivate::mouseEventVelocity(e);
223 lastMouseCapabilityFlags = QGuiApplicationPrivate::mouseEventCaps(e);
224 lastMousePos = e->pos();
227 void mouseReleaseEvent(QMouseEvent *e) {
228 if (!acceptMouseEvents) {
233 lastMousePos = e->pos();
234 lastMouseCapabilityFlags = QGuiApplicationPrivate::mouseEventCaps(e);
237 bool childMouseEventFilter(QQuickItem *, QEvent *event) {
238 // TODO Is it a bug if a QTouchEvent comes here?
239 if (event->type() == QEvent::MouseButtonPress)
240 mousePressId = ++mousePressNum;
244 static int mousePressNum, mouseMoveNum, mouseReleaseNum;
247 int TestTouchItem::mousePressNum = 0;
248 int TestTouchItem::mouseMoveNum = 0;
249 int TestTouchItem::mouseReleaseNum = 0;
251 class ConstantUpdateItem : public QQuickItem
255 ConstantUpdateItem(QQuickItem *parent = 0) : QQuickItem(parent), iterations(0) {setFlag(ItemHasContents);}
259 QSGNode* updatePaintNode(QSGNode *, UpdatePaintNodeData *){
266 class tst_qquickwindow : public QQmlDataTest
274 QQmlDataTest::initTestCase();
275 touchDevice = new QTouchDevice;
276 touchDevice->setType(QTouchDevice::TouchScreen);
277 QWindowSystemInterface::registerTouchDevice(touchDevice);
278 touchDeviceWithVelocity = new QTouchDevice;
279 touchDeviceWithVelocity->setType(QTouchDevice::TouchScreen);
280 touchDeviceWithVelocity->setCapabilities(QTouchDevice::Position | QTouchDevice::Velocity);
281 QWindowSystemInterface::registerTouchDevice(touchDeviceWithVelocity);
285 void constantUpdates();
286 void mouseFiltering();
289 void touchEvent_basic();
290 void touchEvent_propagation();
291 void touchEvent_propagation_data();
292 void touchEvent_cancel();
293 void touchEvent_reentrant();
294 void touchEvent_velocity();
296 void mouseFromTouch_basic();
304 void multipleWindows();
306 void animationsWhileHidden();
310 void ignoreUnhandledMouseEvents();
312 void ownershipRootItem();
314 QTouchDevice *touchDevice;
315 QTouchDevice *touchDeviceWithVelocity;
318 //If the item calls update inside updatePaintNode, it should schedule another update
319 void tst_qquickwindow::constantUpdates()
322 window.resize(250, 250);
323 ConstantUpdateItem item(window.rootItem());
325 QTRY_VERIFY(item.iterations > 60);
328 void tst_qquickwindow::touchEvent_basic()
330 TestTouchItem::clearMousePressCounter();
332 QQuickWindow *window = new QQuickWindow;
333 window->resize(250, 250);
334 window->setPos(100, 100);
336 QTest::qWaitForWindowShown(window);
338 TestTouchItem *bottomItem = new TestTouchItem(window->rootItem());
339 bottomItem->setObjectName("Bottom Item");
340 bottomItem->setSize(QSizeF(150, 150));
342 TestTouchItem *middleItem = new TestTouchItem(bottomItem);
343 middleItem->setObjectName("Middle Item");
344 middleItem->setPos(QPointF(50, 50));
345 middleItem->setSize(QSizeF(150, 150));
347 TestTouchItem *topItem = new TestTouchItem(middleItem);
348 topItem->setObjectName("Top Item");
349 topItem->setPos(QPointF(50, 50));
350 topItem->setSize(QSizeF(150, 150));
354 // press single point
355 QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),window);
358 QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
360 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
361 QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
362 // At one point this was failing with kwin (KDE window manager) because window->setPos(100, 100)
363 // would put the decorated window at that position rather than the window itself.
364 COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(topItem, pos)));
367 // press multiple points
368 QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),window)
369 .press(1, bottomItem->mapToScene(pos).toPoint(), window);
371 QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
372 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
373 QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
374 COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(topItem, pos)));
375 COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos)));
379 // touch point on top item moves to bottom item, but top item should still receive the event
380 QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(), window);
382 QTest::touchEvent(window, touchDevice).move(0, bottomItem->mapToScene(pos).toPoint(), window);
384 QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
385 COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchUpdate, window, Qt::TouchPointMoved,
386 makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos), pos)));
389 // touch point on bottom item moves to top item, but bottom item should still receive the event
390 QTest::touchEvent(window, touchDevice).press(0, bottomItem->mapToScene(pos).toPoint(), window);
392 QTest::touchEvent(window, touchDevice).move(0, topItem->mapToScene(pos).toPoint(), window);
394 QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
395 COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchUpdate, window, Qt::TouchPointMoved,
396 makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos), pos)));
399 // a single stationary press on an item shouldn't cause an event
400 QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(), window);
402 QTest::touchEvent(window, touchDevice).stationary(0)
403 .press(1, bottomItem->mapToScene(pos).toPoint(), window);
405 QCOMPARE(topItem->lastEvent.touchPoints.count(), 1); // received press only, not stationary
406 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
407 QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
408 COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(topItem, pos)));
409 COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos)));
412 // cleanup: what is pressed must be released
413 // Otherwise you will get an assertion failure:
414 // ASSERT: "itemForTouchPointId.isEmpty()" in file items/qquickwindow.cpp
415 QTest::touchEvent(window, touchDevice).release(0, pos.toPoint(), window).release(1, pos.toPoint(), window);
417 // move touch point from top item to bottom, and release
418 QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),window);
420 QTest::touchEvent(window, touchDevice).release(0, bottomItem->mapToScene(pos).toPoint(),window);
422 QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
423 COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchEnd, window, Qt::TouchPointReleased,
424 makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos), pos)));
427 // release while another point is pressed
428 QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),window)
429 .press(1, bottomItem->mapToScene(pos).toPoint(), window);
431 QTest::touchEvent(window, touchDevice).move(0, bottomItem->mapToScene(pos).toPoint(), window);
433 QTest::touchEvent(window, touchDevice).release(0, bottomItem->mapToScene(pos).toPoint(), window)
436 QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
437 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
438 QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
439 COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchEnd, window, Qt::TouchPointReleased,
440 makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos))));
441 COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos)));
451 void tst_qquickwindow::touchEvent_propagation()
453 TestTouchItem::clearMousePressCounter();
455 QFETCH(bool, acceptTouchEvents);
456 QFETCH(bool, acceptMouseEvents);
457 QFETCH(bool, enableItem);
458 QFETCH(bool, showItem);
460 QQuickWindow *window = new QQuickWindow;
461 window->resize(250, 250);
462 window->setPos(100, 100);
464 QTest::qWaitForWindowShown(window);
466 TestTouchItem *bottomItem = new TestTouchItem(window->rootItem());
467 bottomItem->setObjectName("Bottom Item");
468 bottomItem->setSize(QSizeF(150, 150));
470 TestTouchItem *middleItem = new TestTouchItem(bottomItem);
471 middleItem->setObjectName("Middle Item");
472 middleItem->setPos(QPointF(50, 50));
473 middleItem->setSize(QSizeF(150, 150));
475 TestTouchItem *topItem = new TestTouchItem(middleItem);
476 topItem->setObjectName("Top Item");
477 topItem->setPos(QPointF(50, 50));
478 topItem->setSize(QSizeF(150, 150));
481 QPoint pointInBottomItem = bottomItem->mapToScene(pos).toPoint(); // (10, 10)
482 QPoint pointInMiddleItem = middleItem->mapToScene(pos).toPoint(); // (60, 60) overlaps with bottomItem
483 QPoint pointInTopItem = topItem->mapToScene(pos).toPoint(); // (110, 110) overlaps with bottom & top items
486 topItem->acceptTouchEvents = acceptTouchEvents;
487 topItem->acceptMouseEvents = acceptMouseEvents;
488 topItem->setEnabled(enableItem);
489 topItem->setVisible(showItem);
491 // single touch to top item, should be received by middle item
492 QTest::touchEvent(window, touchDevice).press(0, pointInTopItem, window);
494 QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
495 QCOMPARE(middleItem->lastEvent.touchPoints.count(), 1);
496 QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
497 COMPARE_TOUCH_DATA(middleItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed,
498 makeTouchPoint(middleItem, middleItem->mapFromItem(topItem, pos))));
500 // touch top and middle items, middle item should get both events
501 QTest::touchEvent(window, touchDevice).press(0, pointInTopItem, window)
502 .press(1, pointInMiddleItem, window);
504 QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
505 QCOMPARE(middleItem->lastEvent.touchPoints.count(), 2);
506 QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
507 COMPARE_TOUCH_DATA(middleItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed,
508 (QList<QTouchEvent::TouchPoint>() << makeTouchPoint(middleItem, middleItem->mapFromItem(topItem, pos))
509 << makeTouchPoint(middleItem, pos) )));
512 // disable middleItem as well
513 middleItem->acceptTouchEvents = acceptTouchEvents;
514 middleItem->acceptMouseEvents = acceptMouseEvents;
515 middleItem->setEnabled(enableItem);
516 middleItem->setVisible(showItem);
518 // touch top and middle items, bottom item should get all events
519 QTest::touchEvent(window, touchDevice).press(0, pointInTopItem, window)
520 .press(1, pointInMiddleItem, window);
522 QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
523 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
524 QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 2);
525 COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed,
526 (QList<QTouchEvent::TouchPoint>() << makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos))
527 << makeTouchPoint(bottomItem, bottomItem->mapFromItem(middleItem, pos)) )));
530 // disable bottom item as well
531 bottomItem->acceptTouchEvents = acceptTouchEvents;
532 bottomItem->setEnabled(enableItem);
533 bottomItem->setVisible(showItem);
535 // no events should be received
536 QTest::touchEvent(window, touchDevice).press(0, pointInTopItem, window)
537 .press(1, pointInMiddleItem, window)
538 .press(2, pointInBottomItem, window);
540 QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
541 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
542 QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
548 // disable middle item, touch on top item
549 middleItem->acceptTouchEvents = acceptTouchEvents;
550 middleItem->setEnabled(enableItem);
551 middleItem->setVisible(showItem);
552 QTest::touchEvent(window, touchDevice).press(0, pointInTopItem, window);
554 if (!enableItem || !showItem) {
555 // middle item is disabled or has 0 opacity, bottom item receives the event
556 QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
557 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
558 QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
559 COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed,
560 makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos))));
562 // middle item ignores event, sends it to the top item (top-most child)
563 QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
564 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
565 QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
566 COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed,
567 makeTouchPoint(topItem, pos)));
576 void tst_qquickwindow::touchEvent_propagation_data()
578 QTest::addColumn<bool>("acceptTouchEvents");
579 QTest::addColumn<bool>("acceptMouseEvents");
580 QTest::addColumn<bool>("enableItem");
581 QTest::addColumn<bool>("showItem");
583 QTest::newRow("disable events") << false << false << true << true;
584 QTest::newRow("disable item") << true << true << false << true;
585 QTest::newRow("hide item") << true << true << true << false;
588 void tst_qquickwindow::touchEvent_cancel()
590 TestTouchItem::clearMousePressCounter();
592 QQuickWindow *window = new QQuickWindow;
593 window->resize(250, 250);
594 window->setPos(100, 100);
596 QTest::qWaitForWindowShown(window);
598 TestTouchItem *item = new TestTouchItem(window->rootItem());
599 item->setPos(QPointF(50, 50));
600 item->setSize(QSizeF(150, 150));
603 QTest::touchEvent(window, touchDevice).press(0, item->mapToScene(pos).toPoint(),window);
604 QCoreApplication::processEvents();
606 QTRY_COMPARE(item->lastEvent.touchPoints.count(), 1);
607 TouchEventData d = makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(item,pos));
608 COMPARE_TOUCH_DATA(item->lastEvent, d);
611 QWindowSystemInterface::handleTouchCancelEvent(0, touchDevice);
612 QCoreApplication::processEvents();
613 d = makeTouchData(QEvent::TouchCancel, window);
614 COMPARE_TOUCH_DATA(item->lastEvent, d);
620 void tst_qquickwindow::touchEvent_reentrant()
622 TestTouchItem::clearMousePressCounter();
624 QQuickWindow *window = new QQuickWindow;
625 window->resize(250, 250);
626 window->setPos(100, 100);
628 QTest::qWaitForWindowShown(window);
630 TestTouchItem *item = new TestTouchItem(window->rootItem());
632 item->spinLoopWhenPressed = true; // will call processEvents() from the touch handler
634 item->setPos(QPointF(50, 50));
635 item->setSize(QSizeF(150, 150));
638 // None of these should commit from the dtor.
639 QTest::QTouchEventSequence press = QTest::touchEvent(window, touchDevice, false).press(0, pos.toPoint(), window);
640 pos += QPointF(2, 2);
641 QTest::QTouchEventSequence move = QTest::touchEvent(window, touchDevice, false).move(0, pos.toPoint(), window);
642 QTest::QTouchEventSequence release = QTest::touchEvent(window, touchDevice, false).release(0, pos.toPoint(), window);
644 // Now commit (i.e. call QWindowSystemInterface::handleTouchEvent), but do not process the events yet.
647 release.commit(false);
649 QCoreApplication::processEvents();
651 QTRY_COMPARE(item->touchEventCount, 3);
657 void tst_qquickwindow::touchEvent_velocity()
659 TestTouchItem::clearMousePressCounter();
661 QQuickWindow *window = new QQuickWindow;
662 window->resize(250, 250);
663 window->setPos(100, 100);
665 QTest::qWaitForWindowShown(window);
668 TestTouchItem *item = new TestTouchItem(window->rootItem());
669 item->setPos(QPointF(50, 50));
670 item->setSize(QSizeF(150, 150));
672 QList<QWindowSystemInterface::TouchPoint> points;
673 QWindowSystemInterface::TouchPoint tp;
675 tp.state = Qt::TouchPointPressed;
676 QPoint pos = window->mapToGlobal(item->mapToScene(QPointF(10, 10)).toPoint());
677 tp.area = QRectF(pos, QSizeF(4, 4));
679 QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points);
680 points[0].state = Qt::TouchPointMoved;
681 points[0].area.adjust(5, 5, 5, 5);
682 QVector2D velocity(1.5, 2.5);
683 points[0].velocity = velocity;
684 QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points);
685 QCoreApplication::processEvents();
686 QCOMPARE(item->touchEventCount, 2);
687 QCOMPARE(item->lastEvent.touchPoints.count(), 1);
688 QCOMPARE(item->lastVelocity, velocity);
690 // Now have a transformation on the item and check if velocity and position are transformed accordingly.
691 item->setRotation(90); // clockwise
692 QMatrix4x4 transformMatrix;
693 transformMatrix.rotate(-90, 0, 0, 1); // counterclockwise
694 QVector2D transformedVelocity = transformMatrix.mapVector(velocity).toVector2D();
695 points[0].area.adjust(5, 5, 5, 5);
696 QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points);
697 QCoreApplication::processEvents();
698 QCOMPARE(item->lastVelocity, transformedVelocity);
699 QPoint itemLocalPos = item->mapFromScene(window->mapFromGlobal(points[0].area.center().toPoint())).toPoint();
700 QPoint itemLocalPosFromEvent = item->lastEvent.touchPoints[0].pos().toPoint();
701 QCOMPARE(itemLocalPos, itemLocalPosFromEvent);
703 points[0].state = Qt::TouchPointReleased;
704 QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points);
705 QCoreApplication::processEvents();
710 void tst_qquickwindow::mouseFromTouch_basic()
712 // Turn off accepting touch events with acceptTouchEvents. This
713 // should result in sending mouse events generated from the touch
714 // with the new event propagation system.
716 TestTouchItem::clearMousePressCounter();
717 QQuickWindow *window = new QQuickWindow;
718 window->resize(250, 250);
719 window->setPos(100, 100);
721 QTest::qWaitForWindowShown(window);
724 TestTouchItem *item = new TestTouchItem(window->rootItem());
725 item->setPos(QPointF(50, 50));
726 item->setSize(QSizeF(150, 150));
727 item->acceptTouchEvents = false;
729 QList<QWindowSystemInterface::TouchPoint> points;
730 QWindowSystemInterface::TouchPoint tp;
732 tp.state = Qt::TouchPointPressed;
733 QPoint pos = window->mapToGlobal(item->mapToScene(QPointF(10, 10)).toPoint());
734 tp.area = QRectF(pos, QSizeF(4, 4));
736 QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points);
737 points[0].state = Qt::TouchPointMoved;
738 points[0].area.adjust(5, 5, 5, 5);
739 QVector2D velocity(1.5, 2.5);
740 points[0].velocity = velocity;
741 QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points);
742 points[0].state = Qt::TouchPointReleased;
743 QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points);
744 QCoreApplication::processEvents();
746 // The item should have received a mouse press, move, and release.
747 QCOMPARE(item->mousePressNum, 1);
748 QCOMPARE(item->mouseMoveNum, 1);
749 QCOMPARE(item->mouseReleaseNum, 1);
750 QCOMPARE(item->lastMousePos.toPoint(), item->mapFromScene(window->mapFromGlobal(points[0].area.center().toPoint())).toPoint());
751 QCOMPARE(item->lastVelocityFromMouseMove, velocity);
752 QVERIFY((item->lastMouseCapabilityFlags & QTouchDevice::Velocity) != 0);
754 // Now the same with a transformation.
755 item->setRotation(90); // clockwise
756 QMatrix4x4 transformMatrix;
757 transformMatrix.rotate(-90, 0, 0, 1); // counterclockwise
758 QVector2D transformedVelocity = transformMatrix.mapVector(velocity).toVector2D();
759 points[0].state = Qt::TouchPointPressed;
760 points[0].velocity = velocity;
761 points[0].area = QRectF(pos, QSizeF(4, 4));
762 QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points);
763 points[0].state = Qt::TouchPointMoved;
764 points[0].area.adjust(5, 5, 5, 5);
765 QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points);
766 QCoreApplication::processEvents();
767 QCOMPARE(item->lastMousePos.toPoint(), item->mapFromScene(window->mapFromGlobal(points[0].area.center().toPoint())).toPoint());
768 QCOMPARE(item->lastVelocityFromMouseMove, transformedVelocity);
770 points[0].state = Qt::TouchPointReleased;
771 QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points);
772 QCoreApplication::processEvents();
777 void tst_qquickwindow::clearWindow()
779 QQuickWindow *window = new QQuickWindow;
780 QQuickItem *item = new QQuickItem;
781 item->setParentItem(window->rootItem());
783 QVERIFY(item->window() == window);
787 QVERIFY(item->window() == 0);
792 void tst_qquickwindow::mouseFiltering()
794 TestTouchItem::clearMousePressCounter();
796 QQuickWindow *window = new QQuickWindow;
797 window->resize(250, 250);
798 window->setPos(100, 100);
800 QTest::qWaitForWindowShown(window);
802 TestTouchItem *bottomItem = new TestTouchItem(window->rootItem());
803 bottomItem->setObjectName("Bottom Item");
804 bottomItem->setSize(QSizeF(150, 150));
806 TestTouchItem *middleItem = new TestTouchItem(bottomItem);
807 middleItem->setObjectName("Middle Item");
808 middleItem->setPos(QPointF(50, 50));
809 middleItem->setSize(QSizeF(150, 150));
811 TestTouchItem *topItem = new TestTouchItem(middleItem);
812 topItem->setObjectName("Top Item");
813 topItem->setPos(QPointF(50, 50));
814 topItem->setSize(QSizeF(150, 150));
816 QPoint pos(100, 100);
818 QTest::mousePress(window, Qt::LeftButton, 0, pos);
820 // Mouse filtering propagates down the stack, so the
822 // 1. middleItem filters event
823 // 2. bottomItem filters event
824 // 3. topItem receives event
825 QTRY_COMPARE(middleItem->mousePressId, 1);
826 QTRY_COMPARE(bottomItem->mousePressId, 2);
827 QTRY_COMPARE(topItem->mousePressId, 3);
832 void tst_qquickwindow::qmlCreation()
835 QQmlComponent component(&engine);
836 component.loadUrl(testFileUrl("window.qml"));
837 QObject* created = component.create();
840 QQuickWindow* window = qobject_cast<QQuickWindow*>(created);
842 QCOMPARE(window->color(), QColor(Qt::green));
844 QQuickItem* item = window->findChild<QQuickItem*>("item");
846 QCOMPARE(item->window(), window);
851 void tst_qquickwindow::clearColor()
853 //::grab examines rendering to make sure it works visually
854 QQuickWindow *window = new QQuickWindow;
855 window->resize(250, 250);
856 window->setPos(100, 100);
857 window->setColor(Qt::blue);
859 QTest::qWaitForWindowShown(window);
860 QCOMPARE(window->color(), QColor(Qt::blue));
864 void tst_qquickwindow::grab()
867 window.setColor(Qt::red);
869 window.resize(250, 250);
872 QTest::qWaitForWindowShown(&window);
874 QImage content = window.grabWindow();
875 QCOMPARE(content.width(), window.width());
876 QCOMPARE(content.height(), window.height());
877 QCOMPARE((uint) content.convertToFormat(QImage::Format_RGB32).pixel(0, 0), (uint) 0xffff0000);
880 void tst_qquickwindow::multipleWindows()
882 QList<QQuickWindow *> windows;
883 for (int i=0; i<6; ++i) {
884 QQuickWindow *c = new QQuickWindow();
885 c->setColor(Qt::GlobalColor(Qt::red + i));
887 c->setPos(100 + i * 30, 100 + i * 20);
890 QVERIFY(c->isVisible());
894 for (int i=0; i<windows.size(); ++i) {
895 QQuickWindow *c = windows.at(i);
896 c->setPos(c->x() - 10, c->y() - 10);
900 for (int i=0; i<windows.size(); ++i) {
901 QQuickWindow *c = windows.at(i);
908 void tst_qquickwindow::animationsWhileHidden()
911 QQmlComponent component(&engine);
912 component.loadUrl(testFileUrl("AnimationsWhileHidden.qml"));
913 QObject* created = component.create();
915 QQuickWindow* window = qobject_cast<QQuickWindow*>(created);
917 QVERIFY(window->isVisible());
919 // Now hide the window and verify that it went off screen
922 QVERIFY(!window->isVisible());
924 // Running animaiton should cause it to become visible again shortly.
925 QTRY_VERIFY(window->isVisible());
931 void tst_qquickwindow::headless()
934 QQmlComponent component(&engine);
935 component.loadUrl(testFileUrl("Headless.qml"));
936 QObject* created = component.create();
938 QQuickWindow* window = qobject_cast<QQuickWindow*>(created);
941 QTest::qWaitForWindowShown(window);
942 QVERIFY(window->isVisible());
944 QSignalSpy initialized(window, SIGNAL(sceneGraphInitialized()));
945 QSignalSpy invalidated(window, SIGNAL(sceneGraphInvalidated()));
947 // Verify that the window is alive and kicking
948 QVERIFY(window->openglContext() != 0);
950 // Store the visual result
951 QImage originalContent = window->grabWindow();
953 // Hide the window and verify signal emittion and GL context deletion
955 window->releaseResources();
957 QTRY_COMPARE(invalidated.size(), 1);
958 QVERIFY(window->openglContext() == 0);
960 // Destroy the native windowing system buffers
962 QVERIFY(window->handle() == 0);
964 // Show and verify that we are back and running
966 QTest::qWaitForWindowShown(window);
968 QTRY_COMPARE(initialized.size(), 1);
969 QVERIFY(window->openglContext() != 0);
971 // Verify that the visual output is the same
972 QImage newContent = window->grabWindow();
974 QCOMPARE(originalContent, newContent);
979 void tst_qquickwindow::focusObject()
982 QQmlComponent component(&engine);
983 component.loadUrl(testFileUrl("focus.qml"));
984 QObject *created = component.create();
987 QQuickWindow *window = qobject_cast<QQuickWindow*>(created);
990 QQuickItem *item1 = window->findChild<QQuickItem*>("item1");
992 item1->setFocus(true);
993 QCOMPARE(item1, window->focusObject());
995 QQuickItem *item2 = window->findChild<QQuickItem*>("item2");
997 item2->setFocus(true);
998 QCOMPARE(item2, window->focusObject());
1003 void tst_qquickwindow::ignoreUnhandledMouseEvents()
1005 QQuickWindow* window = new QQuickWindow;
1006 window->resize(100, 100);
1008 QTest::qWaitForWindowShown(window);
1010 QQuickItem* item = new QQuickItem;
1011 item->setSize(QSizeF(100, 100));
1012 item->setParentItem(window->rootItem());
1015 QMouseEvent me(QEvent::MouseButtonPress, QPointF(50, 50), Qt::LeftButton, Qt::LeftButton,
1017 me.setAccepted(true);
1018 QVERIFY(QCoreApplication::sendEvent(window, &me));
1019 QVERIFY(!me.isAccepted());
1023 QMouseEvent me(QEvent::MouseMove, QPointF(51, 51), Qt::LeftButton, Qt::LeftButton,
1025 me.setAccepted(true);
1026 QVERIFY(QCoreApplication::sendEvent(window, &me));
1027 QVERIFY(!me.isAccepted());
1031 QMouseEvent me(QEvent::MouseButtonRelease, QPointF(51, 51), Qt::LeftButton, Qt::LeftButton,
1033 me.setAccepted(true);
1034 QVERIFY(QCoreApplication::sendEvent(window, &me));
1035 QVERIFY(!me.isAccepted());
1042 void tst_qquickwindow::ownershipRootItem()
1044 qmlRegisterType<RootItemAccessor>("QtQuick", 2, 0, "RootItemAccessor");
1047 QQmlComponent component(&engine);
1048 component.loadUrl(testFileUrl("ownershipRootItem.qml"));
1049 QObject* created = component.create();
1051 QQuickWindow* window = qobject_cast<QQuickWindow*>(created);
1053 QTest::qWaitForWindowShown(window);
1055 RootItemAccessor* accessor = window->findChild<RootItemAccessor*>("accessor");
1057 engine.collectGarbage();
1059 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1060 QCoreApplication::processEvents();
1061 QVERIFY(!accessor->isRootItemDestroyed());
1063 QTEST_MAIN(tst_qquickwindow)
1065 #include "tst_qquickwindow.moc"