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/QQuickCanvas>
47 #include <QtQml/QQmlEngine>
48 #include <QtQml/QQmlComponent>
49 #include <QtQuick/private/qquickrectangle_p.h>
50 #include <QtGui/QWindowSystemInterface>
51 #include "../../shared/util.h"
53 #include <private/qquickcanvas_p.h>
54 #include <private/qguiapplication_p.h>
56 struct TouchEventData {
60 Qt::TouchPointStates states;
61 QList<QTouchEvent::TouchPoint> touchPoints;
64 static QTouchEvent::TouchPoint makeTouchPoint(QQuickItem *item, const QPointF &p, const QPointF &lastPoint = QPointF())
66 QPointF last = lastPoint.isNull() ? p : lastPoint;
68 QTouchEvent::TouchPoint tp;
72 tp.setScenePos(item->mapToScene(p));
73 tp.setLastScenePos(item->mapToScene(last));
74 tp.setScreenPos(item->canvas()->mapToGlobal(tp.scenePos().toPoint()));
75 tp.setLastScreenPos(item->canvas()->mapToGlobal(tp.lastScenePos().toPoint()));
79 static TouchEventData makeTouchData(QEvent::Type type, QWindow *w, Qt::TouchPointStates states = 0,
80 const QList<QTouchEvent::TouchPoint>& touchPoints = QList<QTouchEvent::TouchPoint>())
82 TouchEventData d = { type, 0, w, states, touchPoints };
85 static TouchEventData makeTouchData(QEvent::Type type, QWindow *w, Qt::TouchPointStates states, const QTouchEvent::TouchPoint &touchPoint)
87 QList<QTouchEvent::TouchPoint> points;
89 return makeTouchData(type, w, states, points);
92 #define COMPARE_TOUCH_POINTS(tp1, tp2) \
94 QCOMPARE(tp1.pos(), tp2.pos()); \
95 QCOMPARE(tp1.lastPos(), tp2.lastPos()); \
96 QCOMPARE(tp1.scenePos(), tp2.scenePos()); \
97 QCOMPARE(tp1.lastScenePos(), tp2.lastScenePos()); \
98 QCOMPARE(tp1.screenPos(), tp2.screenPos()); \
99 QCOMPARE(tp1.lastScreenPos(), tp2.lastScreenPos()); \
102 #define COMPARE_TOUCH_DATA(d1, d2) \
104 QCOMPARE((int)d1.type, (int)d2.type); \
105 QCOMPARE(d1.widget, d2.widget); \
106 QCOMPARE((int)d1.states, (int)d2.states); \
107 QCOMPARE(d1.touchPoints.count(), d2.touchPoints.count()); \
108 for (int i=0; i<d1.touchPoints.count(); i++) { \
109 COMPARE_TOUCH_POINTS(d1.touchPoints[i], d2.touchPoints[i]); \
114 class RootItemAccessor : public QQuickItem
119 : m_rootItemDestroyed(false)
123 Q_INVOKABLE QQuickItem *rootItem()
126 QQuickCanvasPrivate *c = QQuickCanvasPrivate::get(canvas());
127 m_rootItem = c->rootItem;
128 QObject::connect(m_rootItem, SIGNAL(destroyed()), this, SLOT(rootItemDestroyed()));
132 bool isRootItemDestroyed() {return m_rootItemDestroyed;}
134 void rootItemDestroyed() {
135 m_rootItemDestroyed = true;
139 bool m_rootItemDestroyed;
140 QQuickItem *m_rootItem;
143 class TestTouchItem : public QQuickRectangle
147 TestTouchItem(QQuickItem *parent = 0)
148 : QQuickRectangle(parent), acceptTouchEvents(true), acceptMouseEvents(true),
150 spinLoopWhenPressed(false), touchEventCount(0)
152 border()->setWidth(1);
153 setAcceptedMouseButtons(Qt::LeftButton);
154 setFiltersChildMouseEvents(true);
158 acceptTouchEvents = acceptMouseEvents = true;
162 lastEvent = makeTouchData(QEvent::None, canvas(), 0, QList<QTouchEvent::TouchPoint>());//CHECK_VALID
164 lastVelocity = lastVelocityFromMouseMove = QVector2D();
165 lastMousePos = QPointF();
166 lastMouseCapabilityFlags = 0;
169 static void clearMousePressCounter()
171 mousePressNum = mouseMoveNum = mouseReleaseNum = 0;
174 void clearTouchEventCounter()
179 bool acceptTouchEvents;
180 bool acceptMouseEvents;
181 TouchEventData lastEvent;
183 bool spinLoopWhenPressed;
185 QVector2D lastVelocity;
186 QVector2D lastVelocityFromMouseMove;
187 QPointF lastMousePos;
188 int lastMouseCapabilityFlags;
190 void touchEvent(QTouchEvent *event) {
191 if (!acceptTouchEvents) {
196 lastEvent = makeTouchData(event->type(), event->window(), event->touchPointStates(), event->touchPoints());
197 if (event->device()->capabilities().testFlag(QTouchDevice::Velocity) && !event->touchPoints().isEmpty()) {
198 lastVelocity = event->touchPoints().first().velocity();
200 lastVelocity = QVector2D();
202 if (spinLoopWhenPressed && event->touchPointStates().testFlag(Qt::TouchPointPressed)) {
203 QCoreApplication::processEvents();
207 void mousePressEvent(QMouseEvent *e) {
208 if (!acceptMouseEvents) {
212 mousePressId = ++mousePressNum;
213 lastMousePos = e->pos();
214 lastMouseCapabilityFlags = QGuiApplicationPrivate::mouseEventCaps(e);
217 void mouseMoveEvent(QMouseEvent *e) {
218 if (!acceptMouseEvents) {
223 lastVelocityFromMouseMove = QGuiApplicationPrivate::mouseEventVelocity(e);
224 lastMouseCapabilityFlags = QGuiApplicationPrivate::mouseEventCaps(e);
225 lastMousePos = e->pos();
228 void mouseReleaseEvent(QMouseEvent *e) {
229 if (!acceptMouseEvents) {
234 lastMousePos = e->pos();
235 lastMouseCapabilityFlags = QGuiApplicationPrivate::mouseEventCaps(e);
238 bool childMouseEventFilter(QQuickItem *, QEvent *event) {
239 // TODO Is it a bug if a QTouchEvent comes here?
240 if (event->type() == QEvent::MouseButtonPress)
241 mousePressId = ++mousePressNum;
245 static int mousePressNum, mouseMoveNum, mouseReleaseNum;
248 int TestTouchItem::mousePressNum = 0;
249 int TestTouchItem::mouseMoveNum = 0;
250 int TestTouchItem::mouseReleaseNum = 0;
252 class ConstantUpdateItem : public QQuickItem
256 ConstantUpdateItem(QQuickItem *parent = 0) : QQuickItem(parent), iterations(0) {setFlag(ItemHasContents);}
260 QSGNode* updatePaintNode(QSGNode *, UpdatePaintNodeData *){
267 class tst_qquickcanvas : public QQmlDataTest
275 QQmlDataTest::initTestCase();
276 touchDevice = new QTouchDevice;
277 touchDevice->setType(QTouchDevice::TouchScreen);
278 QWindowSystemInterface::registerTouchDevice(touchDevice);
279 touchDeviceWithVelocity = new QTouchDevice;
280 touchDeviceWithVelocity->setType(QTouchDevice::TouchScreen);
281 touchDeviceWithVelocity->setCapabilities(QTouchDevice::Position | QTouchDevice::Velocity);
282 QWindowSystemInterface::registerTouchDevice(touchDeviceWithVelocity);
286 void constantUpdates();
287 void mouseFiltering();
290 void touchEvent_basic();
291 void touchEvent_propagation();
292 void touchEvent_propagation_data();
293 void touchEvent_cancel();
294 void touchEvent_reentrant();
295 void touchEvent_velocity();
297 void mouseFromTouch_basic();
305 void multipleWindows();
307 void animationsWhileHidden();
311 void ignoreUnhandledMouseEvents();
313 void ownershipRootItem();
315 QTouchDevice *touchDevice;
316 QTouchDevice *touchDeviceWithVelocity;
319 //If the item calls update inside updatePaintNode, it should schedule another update
320 void tst_qquickcanvas::constantUpdates()
323 canvas.resize(250, 250);
324 ConstantUpdateItem item(canvas.rootItem());
326 QTRY_VERIFY(item.iterations > 60);
329 void tst_qquickcanvas::touchEvent_basic()
331 TestTouchItem::clearMousePressCounter();
333 QQuickCanvas *canvas = new QQuickCanvas;
334 canvas->resize(250, 250);
335 canvas->setPos(100, 100);
337 QTest::qWaitForWindowShown(canvas);
339 TestTouchItem *bottomItem = new TestTouchItem(canvas->rootItem());
340 bottomItem->setObjectName("Bottom Item");
341 bottomItem->setSize(QSizeF(150, 150));
343 TestTouchItem *middleItem = new TestTouchItem(bottomItem);
344 middleItem->setObjectName("Middle Item");
345 middleItem->setPos(QPointF(50, 50));
346 middleItem->setSize(QSizeF(150, 150));
348 TestTouchItem *topItem = new TestTouchItem(middleItem);
349 topItem->setObjectName("Top Item");
350 topItem->setPos(QPointF(50, 50));
351 topItem->setSize(QSizeF(150, 150));
355 // press single point
356 QTest::touchEvent(canvas, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),canvas);
359 QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
361 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
362 QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
363 // At one point this was failing with kwin (KDE window manager) because canvas->setPos(100, 100)
364 // would put the decorated window at that position rather than the canvas itself.
365 COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(topItem, pos)));
368 // press multiple points
369 QTest::touchEvent(canvas, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),canvas)
370 .press(1, bottomItem->mapToScene(pos).toPoint(), canvas);
372 QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
373 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
374 QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
375 COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(topItem, pos)));
376 COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos)));
380 // touch point on top item moves to bottom item, but top item should still receive the event
381 QTest::touchEvent(canvas, touchDevice).press(0, topItem->mapToScene(pos).toPoint(), canvas);
383 QTest::touchEvent(canvas, touchDevice).move(0, bottomItem->mapToScene(pos).toPoint(), canvas);
385 QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
386 COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchUpdate, canvas, Qt::TouchPointMoved,
387 makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos), pos)));
390 // touch point on bottom item moves to top item, but bottom item should still receive the event
391 QTest::touchEvent(canvas, touchDevice).press(0, bottomItem->mapToScene(pos).toPoint(), canvas);
393 QTest::touchEvent(canvas, touchDevice).move(0, topItem->mapToScene(pos).toPoint(), canvas);
395 QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
396 COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchUpdate, canvas, Qt::TouchPointMoved,
397 makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos), pos)));
400 // a single stationary press on an item shouldn't cause an event
401 QTest::touchEvent(canvas, touchDevice).press(0, topItem->mapToScene(pos).toPoint(), canvas);
403 QTest::touchEvent(canvas, touchDevice).stationary(0)
404 .press(1, bottomItem->mapToScene(pos).toPoint(), canvas);
406 QCOMPARE(topItem->lastEvent.touchPoints.count(), 1); // received press only, not stationary
407 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
408 QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
409 COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(topItem, pos)));
410 COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos)));
413 // cleanup: what is pressed must be released
414 // Otherwise you will get an assertion failure:
415 // ASSERT: "itemForTouchPointId.isEmpty()" in file items/qquickcanvas.cpp
416 QTest::touchEvent(canvas, touchDevice).release(0, pos.toPoint(), canvas).release(1, pos.toPoint(), canvas);
418 // move touch point from top item to bottom, and release
419 QTest::touchEvent(canvas, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),canvas);
421 QTest::touchEvent(canvas, touchDevice).release(0, bottomItem->mapToScene(pos).toPoint(),canvas);
423 QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
424 COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchEnd, canvas, Qt::TouchPointReleased,
425 makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos), pos)));
428 // release while another point is pressed
429 QTest::touchEvent(canvas, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),canvas)
430 .press(1, bottomItem->mapToScene(pos).toPoint(), canvas);
432 QTest::touchEvent(canvas, touchDevice).move(0, bottomItem->mapToScene(pos).toPoint(), canvas);
434 QTest::touchEvent(canvas, touchDevice).release(0, bottomItem->mapToScene(pos).toPoint(), canvas)
437 QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
438 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
439 QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
440 COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchEnd, canvas, Qt::TouchPointReleased,
441 makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos))));
442 COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos)));
452 void tst_qquickcanvas::touchEvent_propagation()
454 TestTouchItem::clearMousePressCounter();
456 QFETCH(bool, acceptTouchEvents);
457 QFETCH(bool, acceptMouseEvents);
458 QFETCH(bool, enableItem);
459 QFETCH(qreal, itemOpacity);
461 QQuickCanvas *canvas = new QQuickCanvas;
462 canvas->resize(250, 250);
463 canvas->setPos(100, 100);
465 QTest::qWaitForWindowShown(canvas);
467 TestTouchItem *bottomItem = new TestTouchItem(canvas->rootItem());
468 bottomItem->setObjectName("Bottom Item");
469 bottomItem->setSize(QSizeF(150, 150));
471 TestTouchItem *middleItem = new TestTouchItem(bottomItem);
472 middleItem->setObjectName("Middle Item");
473 middleItem->setPos(QPointF(50, 50));
474 middleItem->setSize(QSizeF(150, 150));
476 TestTouchItem *topItem = new TestTouchItem(middleItem);
477 topItem->setObjectName("Top Item");
478 topItem->setPos(QPointF(50, 50));
479 topItem->setSize(QSizeF(150, 150));
482 QPoint pointInBottomItem = bottomItem->mapToScene(pos).toPoint(); // (10, 10)
483 QPoint pointInMiddleItem = middleItem->mapToScene(pos).toPoint(); // (60, 60) overlaps with bottomItem
484 QPoint pointInTopItem = topItem->mapToScene(pos).toPoint(); // (110, 110) overlaps with bottom & top items
487 topItem->acceptTouchEvents = acceptTouchEvents;
488 topItem->acceptMouseEvents = acceptMouseEvents;
489 topItem->setEnabled(enableItem);
490 topItem->setOpacity(itemOpacity);
492 // single touch to top item, should be received by middle item
493 QTest::touchEvent(canvas, touchDevice).press(0, pointInTopItem, canvas);
495 QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
496 QCOMPARE(middleItem->lastEvent.touchPoints.count(), 1);
497 QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
498 COMPARE_TOUCH_DATA(middleItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
499 makeTouchPoint(middleItem, middleItem->mapFromItem(topItem, pos))));
501 // touch top and middle items, middle item should get both events
502 QTest::touchEvent(canvas, touchDevice).press(0, pointInTopItem, canvas)
503 .press(1, pointInMiddleItem, canvas);
505 QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
506 QCOMPARE(middleItem->lastEvent.touchPoints.count(), 2);
507 QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
508 COMPARE_TOUCH_DATA(middleItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
509 (QList<QTouchEvent::TouchPoint>() << makeTouchPoint(middleItem, middleItem->mapFromItem(topItem, pos))
510 << makeTouchPoint(middleItem, pos) )));
513 // disable middleItem as well
514 middleItem->acceptTouchEvents = acceptTouchEvents;
515 middleItem->acceptMouseEvents = acceptMouseEvents;
516 middleItem->setEnabled(enableItem);
517 middleItem->setOpacity(itemOpacity);
519 // touch top and middle items, bottom item should get all events
520 QTest::touchEvent(canvas, touchDevice).press(0, pointInTopItem, canvas)
521 .press(1, pointInMiddleItem, canvas);
523 QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
524 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
525 QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 2);
526 COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
527 (QList<QTouchEvent::TouchPoint>() << makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos))
528 << makeTouchPoint(bottomItem, bottomItem->mapFromItem(middleItem, pos)) )));
531 // disable bottom item as well
532 bottomItem->acceptTouchEvents = acceptTouchEvents;
533 bottomItem->setEnabled(enableItem);
534 bottomItem->setOpacity(itemOpacity);
536 // no events should be received
537 QTest::touchEvent(canvas, touchDevice).press(0, pointInTopItem, canvas)
538 .press(1, pointInMiddleItem, canvas)
539 .press(2, pointInBottomItem, canvas);
541 QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
542 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
543 QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
549 // disable middle item, touch on top item
550 middleItem->acceptTouchEvents = acceptTouchEvents;
551 middleItem->setEnabled(enableItem);
552 middleItem->setOpacity(itemOpacity);
553 QTest::touchEvent(canvas, touchDevice).press(0, pointInTopItem, canvas);
555 if (!enableItem || itemOpacity == 0) {
556 // middle item is disabled or has 0 opacity, bottom item receives the event
557 QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
558 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
559 QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
560 COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
561 makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos))));
563 // middle item ignores event, sends it to the top item (top-most child)
564 QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
565 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
566 QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
567 COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
568 makeTouchPoint(topItem, pos)));
577 void tst_qquickcanvas::touchEvent_propagation_data()
579 QTest::addColumn<bool>("acceptTouchEvents");
580 QTest::addColumn<bool>("acceptMouseEvents");
581 QTest::addColumn<bool>("enableItem");
582 QTest::addColumn<qreal>("itemOpacity");
584 QTest::newRow("disable events") << false << false << true << 1.0;
585 QTest::newRow("disable item") << true << true << false << 1.0;
586 QTest::newRow("opacity of 0") << true << true << true << 0.0;
589 void tst_qquickcanvas::touchEvent_cancel()
591 TestTouchItem::clearMousePressCounter();
593 QQuickCanvas *canvas = new QQuickCanvas;
594 canvas->resize(250, 250);
595 canvas->setPos(100, 100);
597 QTest::qWaitForWindowShown(canvas);
599 TestTouchItem *item = new TestTouchItem(canvas->rootItem());
600 item->setPos(QPointF(50, 50));
601 item->setSize(QSizeF(150, 150));
604 QTest::touchEvent(canvas, touchDevice).press(0, item->mapToScene(pos).toPoint(),canvas);
605 QCoreApplication::processEvents();
607 QTRY_COMPARE(item->lastEvent.touchPoints.count(), 1);
608 TouchEventData d = makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(item,pos));
609 COMPARE_TOUCH_DATA(item->lastEvent, d);
612 QWindowSystemInterface::handleTouchCancelEvent(0, touchDevice);
613 QCoreApplication::processEvents();
614 d = makeTouchData(QEvent::TouchCancel, canvas);
615 COMPARE_TOUCH_DATA(item->lastEvent, d);
621 void tst_qquickcanvas::touchEvent_reentrant()
623 TestTouchItem::clearMousePressCounter();
625 QQuickCanvas *canvas = new QQuickCanvas;
626 canvas->resize(250, 250);
627 canvas->setPos(100, 100);
629 QTest::qWaitForWindowShown(canvas);
631 TestTouchItem *item = new TestTouchItem(canvas->rootItem());
633 item->spinLoopWhenPressed = true; // will call processEvents() from the touch handler
635 item->setPos(QPointF(50, 50));
636 item->setSize(QSizeF(150, 150));
639 // None of these should commit from the dtor.
640 QTest::QTouchEventSequence press = QTest::touchEvent(canvas, touchDevice, false).press(0, pos.toPoint(), canvas);
641 pos += QPointF(2, 2);
642 QTest::QTouchEventSequence move = QTest::touchEvent(canvas, touchDevice, false).move(0, pos.toPoint(), canvas);
643 QTest::QTouchEventSequence release = QTest::touchEvent(canvas, touchDevice, false).release(0, pos.toPoint(), canvas);
645 // Now commit (i.e. call QWindowSystemInterface::handleTouchEvent), but do not process the events yet.
648 release.commit(false);
650 QCoreApplication::processEvents();
652 QTRY_COMPARE(item->touchEventCount, 3);
658 void tst_qquickcanvas::touchEvent_velocity()
660 TestTouchItem::clearMousePressCounter();
662 QQuickCanvas *canvas = new QQuickCanvas;
663 canvas->resize(250, 250);
664 canvas->setPos(100, 100);
666 QTest::qWaitForWindowShown(canvas);
669 TestTouchItem *item = new TestTouchItem(canvas->rootItem());
670 item->setPos(QPointF(50, 50));
671 item->setSize(QSizeF(150, 150));
673 QList<QWindowSystemInterface::TouchPoint> points;
674 QWindowSystemInterface::TouchPoint tp;
676 tp.state = Qt::TouchPointPressed;
677 QPoint pos = canvas->mapToGlobal(item->mapToScene(QPointF(10, 10)).toPoint());
678 tp.area = QRectF(pos, QSizeF(4, 4));
680 QWindowSystemInterface::handleTouchEvent(canvas, touchDeviceWithVelocity, points);
681 points[0].state = Qt::TouchPointMoved;
682 points[0].area.adjust(5, 5, 5, 5);
683 QVector2D velocity(1.5, 2.5);
684 points[0].velocity = velocity;
685 QWindowSystemInterface::handleTouchEvent(canvas, touchDeviceWithVelocity, points);
686 QCoreApplication::processEvents();
687 QCOMPARE(item->touchEventCount, 2);
688 QCOMPARE(item->lastEvent.touchPoints.count(), 1);
689 QCOMPARE(item->lastVelocity, velocity);
691 // Now have a transformation on the item and check if velocity and position are transformed accordingly.
692 item->setRotation(90); // clockwise
693 QMatrix4x4 transformMatrix;
694 transformMatrix.rotate(-90, 0, 0, 1); // counterclockwise
695 QVector2D transformedVelocity = transformMatrix.mapVector(velocity).toVector2D();
696 points[0].area.adjust(5, 5, 5, 5);
697 QWindowSystemInterface::handleTouchEvent(canvas, touchDeviceWithVelocity, points);
698 QCoreApplication::processEvents();
699 QCOMPARE(item->lastVelocity, transformedVelocity);
700 QPoint itemLocalPos = item->mapFromScene(canvas->mapFromGlobal(points[0].area.center().toPoint())).toPoint();
701 QPoint itemLocalPosFromEvent = item->lastEvent.touchPoints[0].pos().toPoint();
702 QCOMPARE(itemLocalPos, itemLocalPosFromEvent);
704 points[0].state = Qt::TouchPointReleased;
705 QWindowSystemInterface::handleTouchEvent(canvas, touchDeviceWithVelocity, points);
706 QCoreApplication::processEvents();
711 void tst_qquickcanvas::mouseFromTouch_basic()
713 // Turn off accepting touch events with acceptTouchEvents. This
714 // should result in sending mouse events generated from the touch
715 // with the new event propagation system.
717 TestTouchItem::clearMousePressCounter();
718 QQuickCanvas *canvas = new QQuickCanvas;
719 canvas->resize(250, 250);
720 canvas->setPos(100, 100);
722 QTest::qWaitForWindowShown(canvas);
725 TestTouchItem *item = new TestTouchItem(canvas->rootItem());
726 item->setPos(QPointF(50, 50));
727 item->setSize(QSizeF(150, 150));
728 item->acceptTouchEvents = false;
730 QList<QWindowSystemInterface::TouchPoint> points;
731 QWindowSystemInterface::TouchPoint tp;
733 tp.state = Qt::TouchPointPressed;
734 QPoint pos = canvas->mapToGlobal(item->mapToScene(QPointF(10, 10)).toPoint());
735 tp.area = QRectF(pos, QSizeF(4, 4));
737 QWindowSystemInterface::handleTouchEvent(canvas, touchDeviceWithVelocity, points);
738 points[0].state = Qt::TouchPointMoved;
739 points[0].area.adjust(5, 5, 5, 5);
740 QVector2D velocity(1.5, 2.5);
741 points[0].velocity = velocity;
742 QWindowSystemInterface::handleTouchEvent(canvas, touchDeviceWithVelocity, points);
743 points[0].state = Qt::TouchPointReleased;
744 QWindowSystemInterface::handleTouchEvent(canvas, touchDeviceWithVelocity, points);
745 QCoreApplication::processEvents();
747 // The item should have received a mouse press, move, and release.
748 QCOMPARE(item->mousePressNum, 1);
749 QCOMPARE(item->mouseMoveNum, 1);
750 QCOMPARE(item->mouseReleaseNum, 1);
751 QCOMPARE(item->lastMousePos.toPoint(), item->mapFromScene(canvas->mapFromGlobal(points[0].area.center().toPoint())).toPoint());
752 QCOMPARE(item->lastVelocityFromMouseMove, velocity);
753 QVERIFY((item->lastMouseCapabilityFlags & QTouchDevice::Velocity) != 0);
755 // Now the same with a transformation.
756 item->setRotation(90); // clockwise
757 QMatrix4x4 transformMatrix;
758 transformMatrix.rotate(-90, 0, 0, 1); // counterclockwise
759 QVector2D transformedVelocity = transformMatrix.mapVector(velocity).toVector2D();
760 points[0].state = Qt::TouchPointPressed;
761 points[0].velocity = velocity;
762 points[0].area = QRectF(pos, QSizeF(4, 4));
763 QWindowSystemInterface::handleTouchEvent(canvas, touchDeviceWithVelocity, points);
764 points[0].state = Qt::TouchPointMoved;
765 points[0].area.adjust(5, 5, 5, 5);
766 QWindowSystemInterface::handleTouchEvent(canvas, touchDeviceWithVelocity, points);
767 QCoreApplication::processEvents();
768 QCOMPARE(item->lastMousePos.toPoint(), item->mapFromScene(canvas->mapFromGlobal(points[0].area.center().toPoint())).toPoint());
769 QCOMPARE(item->lastVelocityFromMouseMove, transformedVelocity);
771 points[0].state = Qt::TouchPointReleased;
772 QWindowSystemInterface::handleTouchEvent(canvas, touchDeviceWithVelocity, points);
773 QCoreApplication::processEvents();
778 void tst_qquickcanvas::clearCanvas()
780 QQuickCanvas *canvas = new QQuickCanvas;
781 QQuickItem *item = new QQuickItem;
782 item->setParentItem(canvas->rootItem());
784 QVERIFY(item->canvas() == canvas);
788 QVERIFY(item->canvas() == 0);
793 void tst_qquickcanvas::mouseFiltering()
795 TestTouchItem::clearMousePressCounter();
797 QQuickCanvas *canvas = new QQuickCanvas;
798 canvas->resize(250, 250);
799 canvas->setPos(100, 100);
801 QTest::qWaitForWindowShown(canvas);
803 TestTouchItem *bottomItem = new TestTouchItem(canvas->rootItem());
804 bottomItem->setObjectName("Bottom Item");
805 bottomItem->setSize(QSizeF(150, 150));
807 TestTouchItem *middleItem = new TestTouchItem(bottomItem);
808 middleItem->setObjectName("Middle Item");
809 middleItem->setPos(QPointF(50, 50));
810 middleItem->setSize(QSizeF(150, 150));
812 TestTouchItem *topItem = new TestTouchItem(middleItem);
813 topItem->setObjectName("Top Item");
814 topItem->setPos(QPointF(50, 50));
815 topItem->setSize(QSizeF(150, 150));
817 QPoint pos(100, 100);
819 QTest::mousePress(canvas, Qt::LeftButton, 0, pos);
821 // Mouse filtering propagates down the stack, so the
823 // 1. middleItem filters event
824 // 2. bottomItem filters event
825 // 3. topItem receives event
826 QTRY_COMPARE(middleItem->mousePressId, 1);
827 QTRY_COMPARE(bottomItem->mousePressId, 2);
828 QTRY_COMPARE(topItem->mousePressId, 3);
833 void tst_qquickcanvas::qmlCreation()
836 QQmlComponent component(&engine);
837 component.loadUrl(testFileUrl("window.qml"));
838 QObject* created = component.create();
841 QQuickCanvas* canvas = qobject_cast<QQuickCanvas*>(created);
843 QCOMPARE(canvas->clearColor(), QColor(Qt::green));
845 QQuickItem* item = canvas->findChild<QQuickItem*>("item");
847 QCOMPARE(item->canvas(), canvas);
852 void tst_qquickcanvas::clearColor()
854 //### Can we examine rendering to make sure it is really blue?
855 QQuickCanvas *canvas = new QQuickCanvas;
856 canvas->resize(250, 250);
857 canvas->setPos(100, 100);
858 canvas->setClearColor(Qt::blue);
860 QTest::qWaitForWindowShown(canvas);
861 QCOMPARE(canvas->clearColor(), QColor(Qt::blue));
865 void tst_qquickcanvas::grab()
868 canvas.setClearColor(Qt::red);
870 canvas.resize(250, 250);
873 QTest::qWaitForWindowShown(&canvas);
875 QImage content = canvas.grabFrameBuffer();
876 QCOMPARE(content.width(), canvas.width());
877 QCOMPARE(content.height(), canvas.height());
878 QCOMPARE((uint) content.convertToFormat(QImage::Format_RGB32).pixel(0, 0), (uint) 0xffff0000);
881 void tst_qquickcanvas::multipleWindows()
883 QList<QQuickCanvas *> windows;
884 for (int i=0; i<6; ++i) {
885 QQuickCanvas *c = new QQuickCanvas();
886 c->setClearColor(Qt::GlobalColor(Qt::red + i));
888 c->setPos(100 + i * 30, 100 + i * 20);
891 QVERIFY(c->isVisible());
895 for (int i=0; i<windows.size(); ++i) {
896 QQuickCanvas *c = windows.at(i);
897 c->setPos(c->x() - 10, c->y() - 10);
901 for (int i=0; i<windows.size(); ++i) {
902 QQuickCanvas *c = windows.at(i);
909 void tst_qquickcanvas::animationsWhileHidden()
912 QQmlComponent component(&engine);
913 component.loadUrl(testFileUrl("AnimationsWhileHidden.qml"));
914 QObject* created = component.create();
916 QQuickCanvas* canvas = qobject_cast<QQuickCanvas*>(created);
918 QVERIFY(canvas->isVisible());
920 // Now hide the window and verify that it went off screen
923 QVERIFY(!canvas->isVisible());
925 // Running animaiton should cause it to become visible again shortly.
926 QTRY_VERIFY(canvas->isVisible());
932 void tst_qquickcanvas::headless()
935 QQmlComponent component(&engine);
936 component.loadUrl(testFileUrl("Headless.qml"));
937 QObject* created = component.create();
939 QQuickCanvas* canvas = qobject_cast<QQuickCanvas*>(created);
942 QTest::qWaitForWindowShown(canvas);
943 QVERIFY(canvas->isVisible());
945 QSignalSpy initialized(canvas, SIGNAL(sceneGraphInitialized()));
946 QSignalSpy invalidated(canvas, SIGNAL(sceneGraphInvalidated()));
948 // Verify that the canvas is alive and kicking
949 QVERIFY(canvas->openglContext() != 0);
951 // Store the visual result
952 QImage originalContent = canvas->grabFrameBuffer();
954 // Hide the canvas and verify signal emittion and GL context deletion
956 canvas->releaseResources();
958 QTRY_COMPARE(invalidated.size(), 1);
959 QVERIFY(canvas->openglContext() == 0);
961 // Destroy the native windowing system buffers
963 QVERIFY(canvas->handle() == 0);
965 // Show and verify that we are back and running
967 QTest::qWaitForWindowShown(canvas);
969 QTRY_COMPARE(initialized.size(), 1);
970 QVERIFY(canvas->openglContext() != 0);
972 // Verify that the visual output is the same
973 QImage newContent = canvas->grabFrameBuffer();
975 QCOMPARE(originalContent, newContent);
980 void tst_qquickcanvas::focusObject()
983 QQmlComponent component(&engine);
984 component.loadUrl(testFileUrl("focus.qml"));
985 QObject *created = component.create();
988 QQuickCanvas *canvas = qobject_cast<QQuickCanvas*>(created);
991 QQuickItem *item1 = canvas->findChild<QQuickItem*>("item1");
993 item1->setFocus(true);
994 QCOMPARE(item1, canvas->focusObject());
996 QQuickItem *item2 = canvas->findChild<QQuickItem*>("item2");
998 item2->setFocus(true);
999 QCOMPARE(item2, canvas->focusObject());
1004 void tst_qquickcanvas::ignoreUnhandledMouseEvents()
1006 QQuickCanvas* canvas = new QQuickCanvas;
1007 canvas->resize(100, 100);
1009 QTest::qWaitForWindowShown(canvas);
1011 QQuickItem* item = new QQuickItem;
1012 item->setSize(QSizeF(100, 100));
1013 item->setParentItem(canvas->rootItem());
1016 QMouseEvent me(QEvent::MouseButtonPress, QPointF(50, 50), Qt::LeftButton, Qt::LeftButton,
1018 me.setAccepted(true);
1019 QVERIFY(QCoreApplication::sendEvent(canvas, &me));
1020 QVERIFY(!me.isAccepted());
1024 QMouseEvent me(QEvent::MouseMove, QPointF(51, 51), Qt::LeftButton, Qt::LeftButton,
1026 me.setAccepted(true);
1027 QVERIFY(QCoreApplication::sendEvent(canvas, &me));
1028 QVERIFY(!me.isAccepted());
1032 QMouseEvent me(QEvent::MouseButtonRelease, QPointF(51, 51), Qt::LeftButton, Qt::LeftButton,
1034 me.setAccepted(true);
1035 QVERIFY(QCoreApplication::sendEvent(canvas, &me));
1036 QVERIFY(!me.isAccepted());
1043 void tst_qquickcanvas::ownershipRootItem()
1045 qmlRegisterType<RootItemAccessor>("QtQuick", 2, 0, "RootItemAccessor");
1048 QQmlComponent component(&engine);
1049 component.loadUrl(testFileUrl("ownershipRootItem.qml"));
1050 QObject* created = component.create();
1052 QQuickCanvas* canvas = qobject_cast<QQuickCanvas*>(created);
1054 QTest::qWaitForWindowShown(canvas);
1056 RootItemAccessor* accessor = canvas->findChild<RootItemAccessor*>("accessor");
1058 engine.collectGarbage();
1060 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1061 QCoreApplication::processEvents();
1062 QVERIFY(!accessor->isRootItemDestroyed());
1064 QTEST_MAIN(tst_qquickcanvas)
1066 #include "tst_qquickcanvas.moc"