Fix 'Unknown commands' in qtdeclarative documentation.
[profile/ivi/qtdeclarative.git] / tests / auto / quick / qquickwindow / tst_qquickwindow.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <qtest.h>
43 #include <QDebug>
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"
51 #include <QSignalSpy>
52 #include <private/qquickwindow_p.h>
53 #include <private/qguiapplication_p.h>
54
55 struct TouchEventData {
56     QEvent::Type type;
57     QWidget *widget;
58     QWindow *window;
59     Qt::TouchPointStates states;
60     QList<QTouchEvent::TouchPoint> touchPoints;
61 };
62
63 static QTouchEvent::TouchPoint makeTouchPoint(QQuickItem *item, const QPointF &p, const QPointF &lastPoint = QPointF())
64 {
65     QPointF last = lastPoint.isNull() ? p : lastPoint;
66
67     QTouchEvent::TouchPoint tp;
68
69     tp.setPos(p);
70     tp.setLastPos(last);
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()));
75     return tp;
76 }
77
78 static TouchEventData makeTouchData(QEvent::Type type, QWindow *w, Qt::TouchPointStates states = 0,
79                                     const QList<QTouchEvent::TouchPoint>& touchPoints = QList<QTouchEvent::TouchPoint>())
80 {
81     TouchEventData d = { type, 0, w, states, touchPoints };
82     return d;
83 }
84 static TouchEventData makeTouchData(QEvent::Type type, QWindow *w, Qt::TouchPointStates states, const QTouchEvent::TouchPoint &touchPoint)
85 {
86     QList<QTouchEvent::TouchPoint> points;
87     points << touchPoint;
88     return makeTouchData(type, w, states, points);
89 }
90
91 #define COMPARE_TOUCH_POINTS(tp1, tp2) \
92 { \
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()); \
99 }
100
101 #define COMPARE_TOUCH_DATA(d1, d2) \
102 { \
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]); \
109     } \
110 }
111
112
113 class RootItemAccessor : public QQuickItem
114 {
115     Q_OBJECT
116 public:
117     RootItemAccessor()
118         : m_rootItemDestroyed(false)
119         , m_rootItem(0)
120     {
121     }
122     Q_INVOKABLE QQuickItem *rootItem()
123     {
124         if (!m_rootItem) {
125             QQuickWindowPrivate *c = QQuickWindowPrivate::get(window());
126             m_rootItem = c->rootItem;
127             QObject::connect(m_rootItem, SIGNAL(destroyed()), this, SLOT(rootItemDestroyed()));
128         }
129         return m_rootItem;
130     }
131     bool isRootItemDestroyed() {return m_rootItemDestroyed;}
132 public slots:
133     void rootItemDestroyed() {
134         m_rootItemDestroyed = true;
135     }
136
137 private:
138     bool m_rootItemDestroyed;
139     QQuickItem *m_rootItem;
140 };
141
142 class TestTouchItem : public QQuickRectangle
143 {
144     Q_OBJECT
145 public:
146     TestTouchItem(QQuickItem *parent = 0)
147         : QQuickRectangle(parent), acceptTouchEvents(true), acceptMouseEvents(true),
148           mousePressId(0),
149           spinLoopWhenPressed(false), touchEventCount(0)
150     {
151         border()->setWidth(1);
152         setAcceptedMouseButtons(Qt::LeftButton);
153         setFiltersChildMouseEvents(true);
154     }
155
156     void reset() {
157         acceptTouchEvents = acceptMouseEvents = true;
158         setEnabled(true);
159         setVisible(true);
160
161         lastEvent = makeTouchData(QEvent::None, window(), 0, QList<QTouchEvent::TouchPoint>());//CHECK_VALID
162
163         lastVelocity = lastVelocityFromMouseMove = QVector2D();
164         lastMousePos = QPointF();
165         lastMouseCapabilityFlags = 0;
166     }
167
168     static void clearMousePressCounter()
169     {
170         mousePressNum = mouseMoveNum = mouseReleaseNum = 0;
171     }
172
173     void clearTouchEventCounter()
174     {
175         touchEventCount = 0;
176     }
177
178     bool acceptTouchEvents;
179     bool acceptMouseEvents;
180     TouchEventData lastEvent;
181     int mousePressId;
182     bool spinLoopWhenPressed;
183     int touchEventCount;
184     QVector2D lastVelocity;
185     QVector2D lastVelocityFromMouseMove;
186     QPointF lastMousePos;
187     int lastMouseCapabilityFlags;
188
189     void touchEvent(QTouchEvent *event) {
190         if (!acceptTouchEvents) {
191             event->ignore();
192             return;
193         }
194         ++touchEventCount;
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();
198         } else {
199             lastVelocity = QVector2D();
200         }
201         if (spinLoopWhenPressed && event->touchPointStates().testFlag(Qt::TouchPointPressed)) {
202             QCoreApplication::processEvents();
203         }
204     }
205
206     void mousePressEvent(QMouseEvent *e) {
207         if (!acceptMouseEvents) {
208             e->ignore();
209             return;
210         }
211         mousePressId = ++mousePressNum;
212         lastMousePos = e->pos();
213         lastMouseCapabilityFlags = QGuiApplicationPrivate::mouseEventCaps(e);
214     }
215
216     void mouseMoveEvent(QMouseEvent *e) {
217         if (!acceptMouseEvents) {
218             e->ignore();
219             return;
220         }
221         ++mouseMoveNum;
222         lastVelocityFromMouseMove = QGuiApplicationPrivate::mouseEventVelocity(e);
223         lastMouseCapabilityFlags = QGuiApplicationPrivate::mouseEventCaps(e);
224         lastMousePos = e->pos();
225     }
226
227     void mouseReleaseEvent(QMouseEvent *e) {
228         if (!acceptMouseEvents) {
229             e->ignore();
230             return;
231         }
232         ++mouseReleaseNum;
233         lastMousePos = e->pos();
234         lastMouseCapabilityFlags = QGuiApplicationPrivate::mouseEventCaps(e);
235     }
236
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;
241         return false;
242     }
243
244     static int mousePressNum, mouseMoveNum, mouseReleaseNum;
245 };
246
247 int TestTouchItem::mousePressNum = 0;
248 int TestTouchItem::mouseMoveNum = 0;
249 int TestTouchItem::mouseReleaseNum = 0;
250
251 class ConstantUpdateItem : public QQuickItem
252 {
253 Q_OBJECT
254 public:
255     ConstantUpdateItem(QQuickItem *parent = 0) : QQuickItem(parent), iterations(0) {setFlag(ItemHasContents);}
256
257     int iterations;
258 protected:
259     QSGNode* updatePaintNode(QSGNode *, UpdatePaintNodeData *){
260         iterations++;
261         update();
262         return 0;
263     }
264 };
265
266 class tst_qquickwindow : public QQmlDataTest
267 {
268     Q_OBJECT
269 public:
270
271 private slots:
272     void initTestCase()
273     {
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);
282     }
283
284
285     void constantUpdates();
286     void mouseFiltering();
287     void headless();
288
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();
295
296     void mouseFromTouch_basic();
297
298     void clearWindow();
299
300     void qmlCreation();
301     void clearColor();
302
303     void grab();
304     void multipleWindows();
305
306     void animationsWhileHidden();
307
308     void focusObject();
309
310     void ignoreUnhandledMouseEvents();
311
312     void ownershipRootItem();
313 private:
314     QTouchDevice *touchDevice;
315     QTouchDevice *touchDeviceWithVelocity;
316 };
317
318 //If the item calls update inside updatePaintNode, it should schedule another update
319 void tst_qquickwindow::constantUpdates()
320 {
321     QQuickWindow window;
322     window.resize(250, 250);
323     ConstantUpdateItem item(window.rootItem());
324     window.show();
325     QTRY_VERIFY(item.iterations > 60);
326 }
327
328 void tst_qquickwindow::touchEvent_basic()
329 {
330     TestTouchItem::clearMousePressCounter();
331
332     QQuickWindow *window = new QQuickWindow;
333     window->resize(250, 250);
334     window->setPos(100, 100);
335     window->show();
336     QTest::qWaitForWindowShown(window);
337
338     TestTouchItem *bottomItem = new TestTouchItem(window->rootItem());
339     bottomItem->setObjectName("Bottom Item");
340     bottomItem->setSize(QSizeF(150, 150));
341
342     TestTouchItem *middleItem = new TestTouchItem(bottomItem);
343     middleItem->setObjectName("Middle Item");
344     middleItem->setPos(QPointF(50, 50));
345     middleItem->setSize(QSizeF(150, 150));
346
347     TestTouchItem *topItem = new TestTouchItem(middleItem);
348     topItem->setObjectName("Top Item");
349     topItem->setPos(QPointF(50, 50));
350     topItem->setSize(QSizeF(150, 150));
351
352     QPointF pos(10, 10);
353
354     // press single point
355     QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),window);
356     QTest::qWait(50);
357
358     QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
359
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)));
365     topItem->reset();
366
367     // press multiple points
368     QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),window)
369             .press(1, bottomItem->mapToScene(pos).toPoint(), window);
370     QTest::qWait(50);
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)));
376     topItem->reset();
377     bottomItem->reset();
378
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);
381     QTest::qWait(50);
382     QTest::touchEvent(window, touchDevice).move(0, bottomItem->mapToScene(pos).toPoint(), window);
383     QTest::qWait(50);
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)));
387     topItem->reset();
388
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);
391     QTest::qWait(50);
392     QTest::touchEvent(window, touchDevice).move(0, topItem->mapToScene(pos).toPoint(), window);
393     QTest::qWait(50);
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)));
397     bottomItem->reset();
398
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);
401     QTest::qWait(50);
402     QTest::touchEvent(window, touchDevice).stationary(0)
403             .press(1, bottomItem->mapToScene(pos).toPoint(), window);
404     QTest::qWait(50);
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)));
410     topItem->reset();
411     bottomItem->reset();
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);
416
417     // move touch point from top item to bottom, and release
418     QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),window);
419     QTest::qWait(50);
420     QTest::touchEvent(window, touchDevice).release(0, bottomItem->mapToScene(pos).toPoint(),window);
421     QTest::qWait(50);
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)));
425     topItem->reset();
426
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);
430     QTest::qWait(50);
431     QTest::touchEvent(window, touchDevice).move(0, bottomItem->mapToScene(pos).toPoint(), window);
432     QTest::qWait(50);
433     QTest::touchEvent(window, touchDevice).release(0, bottomItem->mapToScene(pos).toPoint(), window)
434                              .stationary(1);
435     QTest::qWait(50);
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)));
442     topItem->reset();
443     bottomItem->reset();
444
445     delete topItem;
446     delete middleItem;
447     delete bottomItem;
448     delete window;
449 }
450
451 void tst_qquickwindow::touchEvent_propagation()
452 {
453     TestTouchItem::clearMousePressCounter();
454
455     QFETCH(bool, acceptTouchEvents);
456     QFETCH(bool, acceptMouseEvents);
457     QFETCH(bool, enableItem);
458     QFETCH(bool, showItem);
459
460     QQuickWindow *window = new QQuickWindow;
461     window->resize(250, 250);
462     window->setPos(100, 100);
463     window->show();
464     QTest::qWaitForWindowShown(window);
465
466     TestTouchItem *bottomItem = new TestTouchItem(window->rootItem());
467     bottomItem->setObjectName("Bottom Item");
468     bottomItem->setSize(QSizeF(150, 150));
469
470     TestTouchItem *middleItem = new TestTouchItem(bottomItem);
471     middleItem->setObjectName("Middle Item");
472     middleItem->setPos(QPointF(50, 50));
473     middleItem->setSize(QSizeF(150, 150));
474
475     TestTouchItem *topItem = new TestTouchItem(middleItem);
476     topItem->setObjectName("Top Item");
477     topItem->setPos(QPointF(50, 50));
478     topItem->setSize(QSizeF(150, 150));
479
480     QPointF pos(10, 10);
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
484
485     // disable topItem
486     topItem->acceptTouchEvents = acceptTouchEvents;
487     topItem->acceptMouseEvents = acceptMouseEvents;
488     topItem->setEnabled(enableItem);
489     topItem->setVisible(showItem);
490
491     // single touch to top item, should be received by middle item
492     QTest::touchEvent(window, touchDevice).press(0, pointInTopItem, window);
493     QTest::qWait(50);
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))));
499
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);
503     QTest::qWait(50);
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) )));
510     middleItem->reset();
511
512     // disable middleItem as well
513     middleItem->acceptTouchEvents = acceptTouchEvents;
514     middleItem->acceptMouseEvents = acceptMouseEvents;
515     middleItem->setEnabled(enableItem);
516     middleItem->setVisible(showItem);
517
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);
521     QTest::qWait(50);
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)) )));
528     bottomItem->reset();
529
530     // disable bottom item as well
531     bottomItem->acceptTouchEvents = acceptTouchEvents;
532     bottomItem->setEnabled(enableItem);
533     bottomItem->setVisible(showItem);
534
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);
539     QTest::qWait(50);
540     QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
541     QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
542     QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
543
544     topItem->reset();
545     middleItem->reset();
546     bottomItem->reset();
547
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);
553     QTest::qWait(50);
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))));
561     } else {
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)));
568     }
569
570     delete topItem;
571     delete middleItem;
572     delete bottomItem;
573     delete window;
574 }
575
576 void tst_qquickwindow::touchEvent_propagation_data()
577 {
578     QTest::addColumn<bool>("acceptTouchEvents");
579     QTest::addColumn<bool>("acceptMouseEvents");
580     QTest::addColumn<bool>("enableItem");
581     QTest::addColumn<bool>("showItem");
582
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;
586 }
587
588 void tst_qquickwindow::touchEvent_cancel()
589 {
590     TestTouchItem::clearMousePressCounter();
591
592     QQuickWindow *window = new QQuickWindow;
593     window->resize(250, 250);
594     window->setPos(100, 100);
595     window->show();
596     QTest::qWaitForWindowShown(window);
597
598     TestTouchItem *item = new TestTouchItem(window->rootItem());
599     item->setPos(QPointF(50, 50));
600     item->setSize(QSizeF(150, 150));
601
602     QPointF pos(10, 10);
603     QTest::touchEvent(window, touchDevice).press(0, item->mapToScene(pos).toPoint(),window);
604     QCoreApplication::processEvents();
605
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);
609     item->reset();
610
611     QWindowSystemInterface::handleTouchCancelEvent(0, touchDevice);
612     QCoreApplication::processEvents();
613     d = makeTouchData(QEvent::TouchCancel, window);
614     COMPARE_TOUCH_DATA(item->lastEvent, d);
615
616     delete item;
617     delete window;
618 }
619
620 void tst_qquickwindow::touchEvent_reentrant()
621 {
622     TestTouchItem::clearMousePressCounter();
623
624     QQuickWindow *window = new QQuickWindow;
625     window->resize(250, 250);
626     window->setPos(100, 100);
627     window->show();
628     QTest::qWaitForWindowShown(window);
629
630     TestTouchItem *item = new TestTouchItem(window->rootItem());
631
632     item->spinLoopWhenPressed = true; // will call processEvents() from the touch handler
633
634     item->setPos(QPointF(50, 50));
635     item->setSize(QSizeF(150, 150));
636     QPointF pos(60, 60);
637
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);
643
644     // Now commit (i.e. call QWindowSystemInterface::handleTouchEvent), but do not process the events yet.
645     press.commit(false);
646     move.commit(false);
647     release.commit(false);
648
649     QCoreApplication::processEvents();
650
651     QTRY_COMPARE(item->touchEventCount, 3);
652
653     delete item;
654     delete window;
655 }
656
657 void tst_qquickwindow::touchEvent_velocity()
658 {
659     TestTouchItem::clearMousePressCounter();
660
661     QQuickWindow *window = new QQuickWindow;
662     window->resize(250, 250);
663     window->setPos(100, 100);
664     window->show();
665     QTest::qWaitForWindowShown(window);
666     QTest::qWait(10);
667
668     TestTouchItem *item = new TestTouchItem(window->rootItem());
669     item->setPos(QPointF(50, 50));
670     item->setSize(QSizeF(150, 150));
671
672     QList<QWindowSystemInterface::TouchPoint> points;
673     QWindowSystemInterface::TouchPoint tp;
674     tp.id = 1;
675     tp.state = Qt::TouchPointPressed;
676     QPoint pos = window->mapToGlobal(item->mapToScene(QPointF(10, 10)).toPoint());
677     tp.area = QRectF(pos, QSizeF(4, 4));
678     points << tp;
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);
689
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);
702
703     points[0].state = Qt::TouchPointReleased;
704     QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points);
705     QCoreApplication::processEvents();
706     delete item;
707     delete window;
708 }
709
710 void tst_qquickwindow::mouseFromTouch_basic()
711 {
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.
715
716     TestTouchItem::clearMousePressCounter();
717     QQuickWindow *window = new QQuickWindow;
718     window->resize(250, 250);
719     window->setPos(100, 100);
720     window->show();
721     QTest::qWaitForWindowShown(window);
722     QTest::qWait(10);
723
724     TestTouchItem *item = new TestTouchItem(window->rootItem());
725     item->setPos(QPointF(50, 50));
726     item->setSize(QSizeF(150, 150));
727     item->acceptTouchEvents = false;
728
729     QList<QWindowSystemInterface::TouchPoint> points;
730     QWindowSystemInterface::TouchPoint tp;
731     tp.id = 1;
732     tp.state = Qt::TouchPointPressed;
733     QPoint pos = window->mapToGlobal(item->mapToScene(QPointF(10, 10)).toPoint());
734     tp.area = QRectF(pos, QSizeF(4, 4));
735     points << tp;
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();
745
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);
753
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);
769
770     points[0].state = Qt::TouchPointReleased;
771     QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points);
772     QCoreApplication::processEvents();
773     delete item;
774     delete window;
775 }
776
777 void tst_qquickwindow::clearWindow()
778 {
779     QQuickWindow *window = new QQuickWindow;
780     QQuickItem *item = new QQuickItem;
781     item->setParentItem(window->rootItem());
782
783     QVERIFY(item->window() == window);
784
785     delete window;
786
787     QVERIFY(item->window() == 0);
788
789     delete item;
790 }
791
792 void tst_qquickwindow::mouseFiltering()
793 {
794     TestTouchItem::clearMousePressCounter();
795
796     QQuickWindow *window = new QQuickWindow;
797     window->resize(250, 250);
798     window->setPos(100, 100);
799     window->show();
800     QTest::qWaitForWindowShown(window);
801
802     TestTouchItem *bottomItem = new TestTouchItem(window->rootItem());
803     bottomItem->setObjectName("Bottom Item");
804     bottomItem->setSize(QSizeF(150, 150));
805
806     TestTouchItem *middleItem = new TestTouchItem(bottomItem);
807     middleItem->setObjectName("Middle Item");
808     middleItem->setPos(QPointF(50, 50));
809     middleItem->setSize(QSizeF(150, 150));
810
811     TestTouchItem *topItem = new TestTouchItem(middleItem);
812     topItem->setObjectName("Top Item");
813     topItem->setPos(QPointF(50, 50));
814     topItem->setSize(QSizeF(150, 150));
815
816     QPoint pos(100, 100);
817
818     QTest::mousePress(window, Qt::LeftButton, 0, pos);
819
820     // Mouse filtering propagates down the stack, so the
821     // correct order is
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);
828
829     delete window;
830 }
831
832 void tst_qquickwindow::qmlCreation()
833 {
834     QQmlEngine engine;
835     QQmlComponent component(&engine);
836     component.loadUrl(testFileUrl("window.qml"));
837     QObject* created = component.create();
838     QVERIFY(created);
839
840     QQuickWindow* window = qobject_cast<QQuickWindow*>(created);
841     QVERIFY(window);
842     QCOMPARE(window->color(), QColor(Qt::green));
843
844     QQuickItem* item = window->findChild<QQuickItem*>("item");
845     QVERIFY(item);
846     QCOMPARE(item->window(), window);
847
848     delete window;
849 }
850
851 void tst_qquickwindow::clearColor()
852 {
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);
858     window->show();
859     QTest::qWaitForWindowShown(window);
860     QCOMPARE(window->color(), QColor(Qt::blue));
861     delete window;
862 }
863
864 void tst_qquickwindow::grab()
865 {
866     QQuickWindow window;
867     window.setColor(Qt::red);
868
869     window.resize(250, 250);
870     window.show();
871
872     QTest::qWaitForWindowShown(&window);
873
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);
878 }
879
880 void tst_qquickwindow::multipleWindows()
881 {
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));
886         c->resize(300, 200);
887         c->setPos(100 + i * 30, 100 + i * 20);
888         c->show();
889         windows << c;
890         QVERIFY(c->isVisible());
891     }
892
893     // move them
894     for (int i=0; i<windows.size(); ++i) {
895         QQuickWindow *c = windows.at(i);
896         c->setPos(c->x() - 10, c->y() - 10);
897     }
898
899     // resize them
900     for (int i=0; i<windows.size(); ++i) {
901         QQuickWindow *c = windows.at(i);
902         c->resize(200, 150);
903     }
904
905     qDeleteAll(windows);
906 }
907
908 void tst_qquickwindow::animationsWhileHidden()
909 {
910     QQmlEngine engine;
911     QQmlComponent component(&engine);
912     component.loadUrl(testFileUrl("AnimationsWhileHidden.qml"));
913     QObject* created = component.create();
914
915     QQuickWindow* window = qobject_cast<QQuickWindow*>(created);
916     QVERIFY(window);
917     QVERIFY(window->isVisible());
918
919     // Now hide the window and verify that it went off screen
920     window->hide();
921     QTest::qWait(10);
922     QVERIFY(!window->isVisible());
923
924     // Running animaiton should cause it to become visible again shortly.
925     QTRY_VERIFY(window->isVisible());
926
927     delete window;
928 }
929
930
931 void tst_qquickwindow::headless()
932 {
933     QQmlEngine engine;
934     QQmlComponent component(&engine);
935     component.loadUrl(testFileUrl("Headless.qml"));
936     QObject* created = component.create();
937
938     QQuickWindow* window = qobject_cast<QQuickWindow*>(created);
939     QVERIFY(window);
940
941     QTest::qWaitForWindowShown(window);
942     QVERIFY(window->isVisible());
943
944     QSignalSpy initialized(window, SIGNAL(sceneGraphInitialized()));
945     QSignalSpy invalidated(window, SIGNAL(sceneGraphInvalidated()));
946
947     // Verify that the window is alive and kicking
948     QVERIFY(window->openglContext() != 0);
949
950     // Store the visual result
951     QImage originalContent = window->grabWindow();
952
953     // Hide the window and verify signal emittion and GL context deletion
954     window->hide();
955     window->releaseResources();
956
957     QTRY_COMPARE(invalidated.size(), 1);
958     QVERIFY(window->openglContext() == 0);
959
960     // Destroy the native windowing system buffers
961     window->destroy();
962     QVERIFY(window->handle() == 0);
963
964     // Show and verify that we are back and running
965     window->show();
966     QTest::qWaitForWindowShown(window);
967
968     QTRY_COMPARE(initialized.size(), 1);
969     QVERIFY(window->openglContext() != 0);
970
971     // Verify that the visual output is the same
972     QImage newContent = window->grabWindow();
973
974     QCOMPARE(originalContent, newContent);
975
976     delete window;
977 }
978
979 void tst_qquickwindow::focusObject()
980 {
981     QQmlEngine engine;
982     QQmlComponent component(&engine);
983     component.loadUrl(testFileUrl("focus.qml"));
984     QObject *created = component.create();
985     QVERIFY(created);
986
987     QQuickWindow *window = qobject_cast<QQuickWindow*>(created);
988     QVERIFY(window);
989
990     QQuickItem *item1 = window->findChild<QQuickItem*>("item1");
991     QVERIFY(item1);
992     item1->setFocus(true);
993     QCOMPARE(item1, window->focusObject());
994
995     QQuickItem *item2 = window->findChild<QQuickItem*>("item2");
996     QVERIFY(item2);
997     item2->setFocus(true);
998     QCOMPARE(item2, window->focusObject());
999
1000     delete window;
1001 }
1002
1003 void tst_qquickwindow::ignoreUnhandledMouseEvents()
1004 {
1005     QQuickWindow* window = new QQuickWindow;
1006     window->resize(100, 100);
1007     window->show();
1008     QTest::qWaitForWindowShown(window);
1009
1010     QQuickItem* item = new QQuickItem;
1011     item->setSize(QSizeF(100, 100));
1012     item->setParentItem(window->rootItem());
1013
1014     {
1015         QMouseEvent me(QEvent::MouseButtonPress, QPointF(50, 50), Qt::LeftButton, Qt::LeftButton,
1016                        Qt::NoModifier);
1017         me.setAccepted(true);
1018         QVERIFY(QCoreApplication::sendEvent(window, &me));
1019         QVERIFY(!me.isAccepted());
1020     }
1021
1022     {
1023         QMouseEvent me(QEvent::MouseMove, QPointF(51, 51), Qt::LeftButton, Qt::LeftButton,
1024                        Qt::NoModifier);
1025         me.setAccepted(true);
1026         QVERIFY(QCoreApplication::sendEvent(window, &me));
1027         QVERIFY(!me.isAccepted());
1028     }
1029
1030     {
1031         QMouseEvent me(QEvent::MouseButtonRelease, QPointF(51, 51), Qt::LeftButton, Qt::LeftButton,
1032                        Qt::NoModifier);
1033         me.setAccepted(true);
1034         QVERIFY(QCoreApplication::sendEvent(window, &me));
1035         QVERIFY(!me.isAccepted());
1036     }
1037
1038     delete window;
1039 }
1040
1041
1042 void tst_qquickwindow::ownershipRootItem()
1043 {
1044     qmlRegisterType<RootItemAccessor>("QtQuick", 2, 0, "RootItemAccessor");
1045
1046     QQmlEngine engine;
1047     QQmlComponent component(&engine);
1048     component.loadUrl(testFileUrl("ownershipRootItem.qml"));
1049     QObject* created = component.create();
1050
1051     QQuickWindow* window = qobject_cast<QQuickWindow*>(created);
1052     QVERIFY(window);
1053     QTest::qWaitForWindowShown(window);
1054
1055     RootItemAccessor* accessor = window->findChild<RootItemAccessor*>("accessor");
1056     QVERIFY(accessor);
1057     engine.collectGarbage();
1058
1059     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1060     QCoreApplication::processEvents();
1061     QVERIFY(!accessor->isRootItemDestroyed());
1062 }
1063 QTEST_MAIN(tst_qquickwindow)
1064
1065 #include "tst_qquickwindow.moc"