Merge branch 'refactor'
[profile/ivi/qtdeclarative.git] / tests / auto / declarative / qsgcanvas / tst_qsgcanvas.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the test suite of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <qtest.h>
43 #include <QDebug>
44 #include <QTouchEvent>
45 #include <QtDeclarative/QSGItem>
46 #include <QtDeclarative/QSGCanvas>
47 #include <QtDeclarative/private/qsgrectangle_p.h>
48 #include "../../../shared/util.h"
49 #include <QtGui/QWindowSystemInterface>
50
51 struct TouchEventData {
52     QEvent::Type type;
53     QWidget *widget;
54     QWindow *window;
55     Qt::TouchPointStates states;
56     QList<QTouchEvent::TouchPoint> touchPoints;
57 };
58
59 static QTouchEvent::TouchPoint makeTouchPoint(QSGItem *item, const QPointF &p, const QPointF &lastPoint = QPointF())
60 {
61     QPointF last = lastPoint.isNull() ? p : lastPoint;
62
63     QTouchEvent::TouchPoint tp;
64
65     tp.setPos(p);
66     tp.setLastPos(last);
67     tp.setScenePos(item->mapToScene(p));
68     tp.setLastScenePos(item->mapToScene(last));
69     tp.setScreenPos(item->canvas()->mapToGlobal(tp.scenePos().toPoint()));
70     tp.setLastScreenPos(item->canvas()->mapToGlobal(tp.lastScenePos().toPoint()));
71     return tp;
72 }
73
74 static TouchEventData makeTouchData(QEvent::Type type, QWidget *w, Qt::TouchPointStates states, const QList<QTouchEvent::TouchPoint> &touchPoints)
75 {
76     TouchEventData d = { type, w, 0, states, touchPoints };
77     return d;
78 }
79
80 static TouchEventData makeTouchData(QEvent::Type type, QWidget *w, Qt::TouchPointStates states, const QTouchEvent::TouchPoint &touchPoint)
81 {
82     QList<QTouchEvent::TouchPoint> points;
83     points << touchPoint;
84     return makeTouchData(type, w, states, points);
85 }
86 static TouchEventData makeTouchData(QEvent::Type type, QWindow *w, Qt::TouchPointStates states, const QList<QTouchEvent::TouchPoint>& touchPoints)
87 {
88     TouchEventData d = { type, 0, w, states, touchPoints };
89     return d;
90 }
91 static TouchEventData makeTouchData(QEvent::Type type, QWindow *w, Qt::TouchPointStates states, const QTouchEvent::TouchPoint &touchPoint)
92 {
93     QList<QTouchEvent::TouchPoint> points;
94     points << touchPoint;
95     return makeTouchData(type, w, states, points);
96 }
97
98 #define COMPARE_TOUCH_POINTS(tp1, tp2) \
99 { \
100     QCOMPARE(tp1.pos(), tp2.pos()); \
101     QCOMPARE(tp1.lastPos(), tp2.lastPos()); \
102     QCOMPARE(tp1.scenePos(), tp2.scenePos()); \
103     QCOMPARE(tp1.lastScenePos(), tp2.lastScenePos()); \
104     QCOMPARE(tp1.screenPos(), tp2.screenPos()); \
105     QCOMPARE(tp1.lastScreenPos(), tp2.lastScreenPos()); \
106 }
107
108 #define COMPARE_TOUCH_DATA(d1, d2) \
109 { \
110     QCOMPARE((int)d1.type, (int)d2.type); \
111     QCOMPARE(d1.widget, d2.widget); \
112     QCOMPARE((int)d1.states, (int)d2.states); \
113     QCOMPARE(d1.touchPoints.count(), d2.touchPoints.count()); \
114     for (int i=0; i<d1.touchPoints.count(); i++) { \
115         COMPARE_TOUCH_POINTS(d1.touchPoints[i], d2.touchPoints[i]); \
116     } \
117 }
118
119 class TestTouchItem : public QSGRectangle
120 {
121     Q_OBJECT
122 public:
123     TestTouchItem(QSGItem *parent = 0)
124         : QSGRectangle(parent), acceptEvents(true)
125     {
126         border()->setWidth(1);
127     }
128
129     void reset() {
130         acceptEvents = true;
131         setEnabled(true);
132         setOpacity(1.0);
133
134         lastEvent = makeTouchData(QEvent::None, canvas(), 0, QList<QTouchEvent::TouchPoint>());//CHECK_VALID
135     }
136
137     bool acceptEvents;
138     TouchEventData lastEvent;
139
140 protected:
141     virtual void touchEvent(QTouchEvent *event) {
142         if (!acceptEvents) {
143             event->ignore();
144             return;
145         }
146         lastEvent = makeTouchData(event->type(), event->widget(), event->touchPointStates(), event->touchPoints());
147         event->accept();
148     }
149 };
150
151
152 class ConstantUpdateItem : public QSGItem
153 {
154 Q_OBJECT
155 public:
156     ConstantUpdateItem(QSGItem *parent = 0) : QSGItem(parent), iterations(0) {setFlag(ItemHasContents);}
157
158     int iterations;
159 protected:
160     QSGNode* updatePaintNode(QSGNode *, UpdatePaintNodeData *){
161         iterations++;
162         update();
163         return 0;
164     }
165 };
166
167 class tst_qsgcanvas : public QObject
168 {
169     Q_OBJECT
170 public:
171     tst_qsgcanvas();
172
173 private slots:
174     void initTestCase();
175     void cleanupTestCase();
176
177     void constantUpdates();
178
179     void touchEvent_basic();
180     void touchEvent_propagation();
181     void touchEvent_propagation_data();
182
183     void clearCanvas();
184 };
185
186 tst_qsgcanvas::tst_qsgcanvas()
187 {
188 }
189
190 void tst_qsgcanvas::initTestCase()
191 {
192 }
193
194 void tst_qsgcanvas::cleanupTestCase()
195 {
196 }
197
198 //If the item calls update inside updatePaintNode, it should schedule another update
199 void tst_qsgcanvas::constantUpdates()
200 {
201     QSGCanvas canvas;
202     ConstantUpdateItem item(canvas.rootItem());
203     canvas.show();
204     QTRY_VERIFY(item.iterations > 60);
205 }
206
207 void tst_qsgcanvas::touchEvent_basic()
208 {
209     QSGCanvas *canvas = new QSGCanvas;
210     canvas->resize(250, 250);
211     canvas->move(100, 100);
212     canvas->show();
213
214     TestTouchItem *bottomItem = new TestTouchItem(canvas->rootItem());
215     bottomItem->setObjectName("Bottom Item");
216     bottomItem->setSize(QSizeF(150, 150));
217
218     TestTouchItem *middleItem = new TestTouchItem(bottomItem);
219     middleItem->setObjectName("Middle Item");
220     middleItem->setPos(QPointF(50, 50));
221     middleItem->setSize(QSizeF(150, 150));
222
223     TestTouchItem *topItem = new TestTouchItem(middleItem);
224     topItem->setObjectName("Top Item");
225     topItem->setPos(QPointF(50, 50));
226     topItem->setSize(QSizeF(150, 150));
227
228     QPointF pos(10, 10);
229
230     // press single point
231     QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint(),canvas);
232     QTest::qWait(50);
233
234     QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
235
236     QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
237     QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
238     TouchEventData d = makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(topItem,pos));
239     COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(topItem, pos)));
240     topItem->reset();
241
242     // press multiple points
243     QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint(),canvas)
244             .press(1, bottomItem->mapToScene(pos).toPoint(), canvas);
245     QTest::qWait(50);
246     QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
247     QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
248     QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
249     COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(topItem, pos)));
250     COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos)));
251     topItem->reset();
252     bottomItem->reset();
253
254     // touch point on top item moves to bottom item, but top item should still receive the event
255     QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint(), canvas);
256     QTest::qWait(50);
257     QTest::touchEvent(canvas).move(0, bottomItem->mapToScene(pos).toPoint(), canvas);
258     QTest::qWait(50);
259     QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
260     COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchUpdate, canvas, Qt::TouchPointMoved,
261             makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos), pos)));
262     topItem->reset();
263
264     // touch point on bottom item moves to top item, but bottom item should still receive the event
265     QTest::touchEvent(canvas).press(0, bottomItem->mapToScene(pos).toPoint(), canvas);
266     QTest::qWait(50);
267     QTest::touchEvent(canvas).move(0, topItem->mapToScene(pos).toPoint(), canvas);
268     QTest::qWait(50);
269     QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
270     COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchUpdate, canvas, Qt::TouchPointMoved,
271             makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos), pos)));
272     bottomItem->reset();
273
274     // a single stationary press on an item shouldn't cause an event
275     QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint(), canvas);
276     QTest::qWait(50);
277     QTest::touchEvent(canvas).stationary(0)
278             .press(1, bottomItem->mapToScene(pos).toPoint(), canvas);
279     QTest::qWait(50);
280     QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);    // received press only, not stationary
281     QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
282     QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
283     COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(topItem, pos)));
284     COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos)));
285     topItem->reset();
286     bottomItem->reset();
287
288     // move touch point from top item to bottom, and release
289     QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint(),canvas);
290     QTest::qWait(50);
291     QTest::touchEvent(canvas).release(0, bottomItem->mapToScene(pos).toPoint(),canvas);
292     QTest::qWait(50);
293     QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
294     COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchEnd, canvas, Qt::TouchPointReleased,
295             makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos), pos)));
296     topItem->reset();
297
298     // release while another point is pressed
299     QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint(),canvas)
300             .press(1, bottomItem->mapToScene(pos).toPoint(), canvas);
301     QTest::qWait(50);
302     QTest::touchEvent(canvas).move(0, bottomItem->mapToScene(pos).toPoint(), canvas);
303     QTest::qWait(50);
304     QTest::touchEvent(canvas).release(0, bottomItem->mapToScene(pos).toPoint(), canvas)
305                              .stationary(1);
306     QTest::qWait(50);
307     QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
308     QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
309     QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
310     COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchEnd, canvas, Qt::TouchPointReleased,
311             makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos))));
312     COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos)));
313     topItem->reset();
314     bottomItem->reset();
315
316     delete topItem;
317     delete middleItem;
318     delete bottomItem;
319     delete canvas;
320 }
321
322 void tst_qsgcanvas::touchEvent_propagation()
323 {
324     QFETCH(bool, acceptEvents);
325     QFETCH(bool, enableItem);
326     QFETCH(qreal, itemOpacity);
327
328     QSGCanvas *canvas = new QSGCanvas;
329     canvas->resize(250, 250);
330     canvas->move(100, 100);
331     canvas->show();
332
333     TestTouchItem *bottomItem = new TestTouchItem(canvas->rootItem());
334     bottomItem->setObjectName("Bottom Item");
335     bottomItem->setSize(QSizeF(150, 150));
336
337     TestTouchItem *middleItem = new TestTouchItem(bottomItem);
338     middleItem->setObjectName("Middle Item");
339     middleItem->setPos(QPointF(50, 50));
340     middleItem->setSize(QSizeF(150, 150));
341
342     TestTouchItem *topItem = new TestTouchItem(middleItem);
343     topItem->setObjectName("Top Item");
344     topItem->setPos(QPointF(50, 50));
345     topItem->setSize(QSizeF(150, 150));
346
347     QPointF pos(10, 10);
348     QPoint pointInBottomItem = bottomItem->mapToScene(pos).toPoint();  // (10, 10)
349     QPoint pointInMiddleItem = middleItem->mapToScene(pos).toPoint();  // (60, 60) overlaps with bottomItem
350     QPoint pointInTopItem = topItem->mapToScene(pos).toPoint();  // (110, 110) overlaps with bottom & top items
351
352     // disable topItem
353     topItem->acceptEvents = acceptEvents;
354     topItem->setEnabled(enableItem);
355     topItem->setOpacity(itemOpacity);
356
357     // single touch to top item, should be received by middle item
358     QTest::touchEvent(canvas).press(0, pointInTopItem, canvas);
359     QTest::qWait(50);
360     QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
361     QCOMPARE(middleItem->lastEvent.touchPoints.count(), 1);
362     QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
363     COMPARE_TOUCH_DATA(middleItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
364             makeTouchPoint(middleItem, middleItem->mapFromItem(topItem, pos))));
365
366     // touch top and middle items, middle item should get both events
367     QTest::touchEvent(canvas).press(0, pointInTopItem, canvas)
368             .press(1, pointInMiddleItem, canvas);
369     QTest::qWait(50);
370     QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
371     QCOMPARE(middleItem->lastEvent.touchPoints.count(), 2);
372     QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
373     COMPARE_TOUCH_DATA(middleItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
374            (QList<QTouchEvent::TouchPoint>() << makeTouchPoint(middleItem, middleItem->mapFromItem(topItem, pos))
375                                               << makeTouchPoint(middleItem, pos) )));
376     middleItem->reset();
377
378     // disable middleItem as well
379     middleItem->acceptEvents = acceptEvents;
380     middleItem->setEnabled(enableItem);
381     middleItem->setOpacity(itemOpacity);
382
383     // touch top and middle items, bottom item should get all events
384     QTest::touchEvent(canvas).press(0, pointInTopItem, canvas)
385             .press(1, pointInMiddleItem, canvas);
386     QTest::qWait(50);
387     QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
388     QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
389     QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 2);
390     COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
391             (QList<QTouchEvent::TouchPoint>() << makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos))
392                                               << makeTouchPoint(bottomItem, bottomItem->mapFromItem(middleItem, pos)) )));
393     bottomItem->reset();
394
395     // disable bottom item as well
396     bottomItem->acceptEvents = acceptEvents;
397     bottomItem->setEnabled(enableItem);
398     bottomItem->setOpacity(itemOpacity);
399
400     // no events should be received
401     QTest::touchEvent(canvas).press(0, pointInTopItem, canvas)
402             .press(1, pointInMiddleItem, canvas)
403             .press(2, pointInBottomItem, canvas);
404     QTest::qWait(50);
405     QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
406     QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
407     QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
408
409     topItem->reset();
410     middleItem->reset();
411     bottomItem->reset();
412
413     // disable middle item, touch on top item
414     middleItem->acceptEvents = acceptEvents;
415     middleItem->setEnabled(enableItem);
416     middleItem->setOpacity(itemOpacity);
417     QTest::touchEvent(canvas).press(0, pointInTopItem, canvas);
418     QTest::qWait(50);
419     if (!enableItem || itemOpacity == 0) {
420         // middle item is disabled or has 0 opacity, bottom item receives the event
421         QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
422         QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
423         QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
424         COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
425                 makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos))));
426     } else {
427         // middle item ignores event, sends it to the top item (top-most child)
428         QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
429         QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
430         QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
431         COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
432                 makeTouchPoint(topItem, pos)));
433     }
434
435     delete topItem;
436     delete middleItem;
437     delete bottomItem;
438     delete canvas;
439 }
440
441 void tst_qsgcanvas::touchEvent_propagation_data()
442 {
443     QTest::addColumn<bool>("acceptEvents");
444     QTest::addColumn<bool>("enableItem");
445     QTest::addColumn<qreal>("itemOpacity");
446
447     QTest::newRow("disable events") << false << true << 1.0;
448     QTest::newRow("disable item") << true << false << 1.0;
449     QTest::newRow("opacity of 0") << true << true << 0.0;
450 }
451
452 void tst_qsgcanvas::clearCanvas()
453 {
454     QSGCanvas *canvas = new QSGCanvas;
455     QSGItem *item = new QSGItem;
456     item->setParentItem(canvas->rootItem());
457
458     QVERIFY(item->canvas() == canvas);
459
460     delete canvas;
461
462     QVERIFY(item->canvas() == 0);
463
464     delete item;
465 }
466
467
468
469 QTEST_MAIN(tst_qsgcanvas)
470
471 #include "tst_qsgcanvas.moc"