Update licenseheader text in source files for qtdeclarative Qt module
[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
46 #include "qsgitem.h"
47 #include "qsgcanvas.h"
48 #include "private/qsgrectangle_p.h"
49 #include "../../../shared/util.h"
50
51 struct TouchEventData {
52     QEvent::Type type;
53     QWidget *widget;
54     Qt::TouchPointStates states;
55     QList<QTouchEvent::TouchPoint> touchPoints;
56 };
57
58 static QTouchEvent::TouchPoint makeTouchPoint(QSGItem *item, const QPointF &p, const QPointF &lastPoint = QPointF())
59 {
60     QPointF last = lastPoint.isNull() ? p : lastPoint;
61
62     QTouchEvent::TouchPoint tp;
63     tp.setPos(p);
64     tp.setLastPos(last);
65     tp.setScenePos(item->mapToScene(p));
66     tp.setLastScenePos(item->mapToScene(last));
67     tp.setScreenPos(item->canvas()->mapToGlobal(tp.scenePos().toPoint()));
68     tp.setLastScreenPos(item->canvas()->mapToGlobal(tp.lastScenePos().toPoint()));
69     return tp;
70 }
71
72 static TouchEventData makeTouchData(QEvent::Type type, QWidget *w, Qt::TouchPointStates states, const QList<QTouchEvent::TouchPoint> &touchPoints)
73 {
74     TouchEventData d = { type, w, states, touchPoints };
75     return d;
76 }
77
78 static TouchEventData makeTouchData(QEvent::Type type, QWidget *w, Qt::TouchPointStates states, const QTouchEvent::TouchPoint &touchPoint)
79 {
80     QList<QTouchEvent::TouchPoint> points;
81     points << touchPoint;
82     return makeTouchData(type, w, states, points);
83 }
84
85 #define COMPARE_TOUCH_POINTS(tp1, tp2) \
86 { \
87     QCOMPARE(tp1.pos(), tp2.pos()); \
88     QCOMPARE(tp1.lastPos(), tp2.lastPos()); \
89     QCOMPARE(tp1.scenePos(), tp2.scenePos()); \
90     QCOMPARE(tp1.lastScenePos(), tp2.lastScenePos()); \
91     QCOMPARE(tp1.screenPos(), tp2.screenPos()); \
92     QCOMPARE(tp1.lastScreenPos(), tp2.lastScreenPos()); \
93 }
94
95 #define COMPARE_TOUCH_DATA(d1, d2) \
96 { \
97     QCOMPARE((int)d1.type, (int)d2.type); \
98     QCOMPARE(d1.widget, d2.widget); \
99     QCOMPARE((int)d1.states, (int)d2.states); \
100     QCOMPARE(d1.touchPoints.count(), d2.touchPoints.count()); \
101     for (int i=0; i<d1.touchPoints.count(); i++) { \
102         COMPARE_TOUCH_POINTS(d1.touchPoints[i], d2.touchPoints[i]); \
103     } \
104 }
105
106 class TestTouchItem : public QSGRectangle
107 {
108     Q_OBJECT
109 public:
110     TestTouchItem(QSGItem *parent = 0)
111         : QSGRectangle(parent), acceptEvents(true)
112     {
113         border()->setWidth(1);
114     }
115
116     void reset() {
117         acceptEvents = true;
118         setEnabled(true);
119         setOpacity(1.0);
120
121         lastEvent = makeTouchData(QEvent::None, 0, 0, QList<QTouchEvent::TouchPoint>());
122     }
123
124     bool acceptEvents;
125     TouchEventData lastEvent;
126
127 protected:
128     virtual void touchEvent(QTouchEvent *event) {
129         if (!acceptEvents) {
130             event->ignore();
131             return;
132         }
133         lastEvent = makeTouchData(event->type(), event->widget(), event->touchPointStates(), event->touchPoints());
134         event->accept();
135     }
136 };
137
138
139 class ConstantUpdateItem : public QSGItem
140 {
141 Q_OBJECT
142 public:
143     ConstantUpdateItem(QSGItem *parent = 0) : QSGItem(parent), iterations(0) {setFlag(ItemHasContents);}
144
145     int iterations;
146 protected:
147     QSGNode* updatePaintNode(QSGNode *, UpdatePaintNodeData *){
148         iterations++;
149         update();
150         return 0;
151     }
152 };
153
154 class tst_qsgcanvas : public QObject
155 {
156     Q_OBJECT
157 public:
158     tst_qsgcanvas();
159
160 private slots:
161     void initTestCase();
162     void cleanupTestCase();
163
164     void constantUpdates();
165
166     void touchEvent_basic();
167     void touchEvent_propagation();
168     void touchEvent_propagation_data();
169
170     void clearCanvas();
171 };
172
173 tst_qsgcanvas::tst_qsgcanvas()
174 {
175 }
176
177 void tst_qsgcanvas::initTestCase()
178 {
179 }
180
181 void tst_qsgcanvas::cleanupTestCase()
182 {
183 }
184
185 //If the item calls update inside updatePaintNode, it should schedule another update
186 void tst_qsgcanvas::constantUpdates()
187 {
188     QSGCanvas canvas;
189     ConstantUpdateItem item(canvas.rootItem());
190     canvas.show();
191     QTRY_VERIFY(item.iterations > 60);
192 }
193
194 void tst_qsgcanvas::touchEvent_basic()
195 {
196     QSGCanvas *canvas = new QSGCanvas;
197     canvas->resize(250, 250);
198     canvas->window()->move(100, 100);
199     canvas->show();
200
201     TestTouchItem *bottomItem = new TestTouchItem(canvas->rootItem());
202     bottomItem->setObjectName("Bottom Item");
203     bottomItem->setSize(QSizeF(150, 150));
204
205     TestTouchItem *middleItem = new TestTouchItem(bottomItem);
206     middleItem->setObjectName("Middle Item");
207     middleItem->setPos(QPointF(50, 50));
208     middleItem->setSize(QSizeF(150, 150));
209
210     TestTouchItem *topItem = new TestTouchItem(middleItem);
211     topItem->setObjectName("Top Item");
212     topItem->setPos(QPointF(50, 50));
213     topItem->setSize(QSizeF(150, 150));
214
215     QPointF pos(10, 10);
216
217     // press single point
218     QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint());
219     QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
220     QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
221     QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
222     COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(topItem, pos)));
223     topItem->reset();
224
225     // press multiple points
226     QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint())
227                              .press(1, bottomItem->mapToScene(pos).toPoint());
228     QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
229     QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
230     QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
231     COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(topItem, pos)));
232     COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos)));
233     topItem->reset();
234     bottomItem->reset();
235
236     // touch point on top item moves to bottom item, but top item should still receive the event
237     QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint());
238     QTest::touchEvent(canvas).move(0, bottomItem->mapToScene(pos).toPoint());
239     QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
240     COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchUpdate, canvas, Qt::TouchPointMoved,
241             makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos), pos)));
242     topItem->reset();
243
244     // touch point on bottom item moves to top item, but bottom item should still receive the event
245     QTest::touchEvent(canvas).press(0, bottomItem->mapToScene(pos).toPoint());
246     QTest::touchEvent(canvas).move(0, topItem->mapToScene(pos).toPoint());
247     QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
248     COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchUpdate, canvas, Qt::TouchPointMoved,
249             makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos), pos)));
250     bottomItem->reset();
251
252     // a single stationary press on an item shouldn't cause an event
253     QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint());
254     QTest::touchEvent(canvas).stationary(0)
255                              .press(1, bottomItem->mapToScene(pos).toPoint());
256     QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);    // received press only, not stationary
257     QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
258     QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
259     COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(topItem, pos)));
260     COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos)));
261     topItem->reset();
262     bottomItem->reset();
263
264     // move touch point from top item to bottom, and release
265     QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint());
266     QTest::touchEvent(canvas).release(0, bottomItem->mapToScene(pos).toPoint());
267     QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
268     COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchEnd, canvas, Qt::TouchPointReleased,
269             makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos), pos)));
270     topItem->reset();
271
272     // release while another point is pressed
273     QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint())
274                              .press(1, bottomItem->mapToScene(pos).toPoint());
275     QTest::touchEvent(canvas).move(0, bottomItem->mapToScene(pos).toPoint());
276     QTest::touchEvent(canvas).release(0, bottomItem->mapToScene(pos).toPoint())
277                              .stationary(1);
278     QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
279     QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
280     QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
281     COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchEnd, canvas, Qt::TouchPointReleased,
282             makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos))));
283     COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos)));
284     topItem->reset();
285     bottomItem->reset();
286
287     delete topItem;
288     delete middleItem;
289     delete bottomItem;
290     delete canvas;
291 }
292
293 void tst_qsgcanvas::touchEvent_propagation()
294 {
295     QFETCH(bool, acceptEvents);
296     QFETCH(bool, enableItem);
297     QFETCH(qreal, itemOpacity);
298
299     QSGCanvas *canvas = new QSGCanvas;
300     canvas->resize(250, 250);
301     canvas->window()->move(100, 100);
302     canvas->show();
303
304     TestTouchItem *bottomItem = new TestTouchItem(canvas->rootItem());
305     bottomItem->setObjectName("Bottom Item");
306     bottomItem->setSize(QSizeF(150, 150));
307
308     TestTouchItem *middleItem = new TestTouchItem(bottomItem);
309     middleItem->setObjectName("Middle Item");
310     middleItem->setPos(QPointF(50, 50));
311     middleItem->setSize(QSizeF(150, 150));
312
313     TestTouchItem *topItem = new TestTouchItem(middleItem);
314     topItem->setObjectName("Top Item");
315     topItem->setPos(QPointF(50, 50));
316     topItem->setSize(QSizeF(150, 150));
317
318     QPointF pos(10, 10);
319     QPoint pointInBottomItem = bottomItem->mapToScene(pos).toPoint();  // (10, 10)
320     QPoint pointInMiddleItem = middleItem->mapToScene(pos).toPoint();  // (60, 60) overlaps with bottomItem
321     QPoint pointInTopItem = topItem->mapToScene(pos).toPoint();  // (110, 110) overlaps with bottom & top items
322
323     // disable topItem
324     topItem->acceptEvents = acceptEvents;
325     topItem->setEnabled(enableItem);
326     topItem->setOpacity(itemOpacity);
327
328     // single touch to top item, should be received by middle item
329     QTest::touchEvent(canvas).press(0, pointInTopItem);
330     QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
331     QCOMPARE(middleItem->lastEvent.touchPoints.count(), 1);
332     QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
333     COMPARE_TOUCH_DATA(middleItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
334             makeTouchPoint(middleItem, middleItem->mapFromItem(topItem, pos))));
335
336     // touch top and middle items, middle item should get both events
337     QTest::touchEvent(canvas).press(0, pointInTopItem)
338                              .press(1, pointInMiddleItem);
339     QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
340     QCOMPARE(middleItem->lastEvent.touchPoints.count(), 2);
341     QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
342     COMPARE_TOUCH_DATA(middleItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
343             (QList<QTouchEvent::TouchPoint>() << makeTouchPoint(middleItem, middleItem->mapFromItem(topItem, pos))
344                                               << makeTouchPoint(middleItem, pos) )));
345     middleItem->reset();
346
347     // disable middleItem as well
348     middleItem->acceptEvents = acceptEvents;
349     middleItem->setEnabled(enableItem);
350     middleItem->setOpacity(itemOpacity);
351
352     // touch top and middle items, bottom item should get all events
353     QTest::touchEvent(canvas).press(0, pointInTopItem)
354                              .press(1, pointInMiddleItem);
355     QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
356     QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
357     QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 2);
358     COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
359             (QList<QTouchEvent::TouchPoint>() << makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos))
360                                               << makeTouchPoint(bottomItem, bottomItem->mapFromItem(middleItem, pos)) )));
361     bottomItem->reset();
362
363     // disable bottom item as well
364     bottomItem->acceptEvents = acceptEvents;
365     bottomItem->setEnabled(enableItem);
366     bottomItem->setOpacity(itemOpacity);
367
368     // no events should be received
369     QTest::touchEvent(canvas).press(0, pointInTopItem)
370                              .press(1, pointInMiddleItem)
371                              .press(2, pointInBottomItem);
372     QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
373     QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
374     QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
375
376     topItem->reset();
377     middleItem->reset();
378     bottomItem->reset();
379
380     // disable middle item, touch on top item
381     middleItem->acceptEvents = acceptEvents;
382     middleItem->setEnabled(enableItem);
383     middleItem->setOpacity(itemOpacity);
384     QTest::touchEvent(canvas).press(0, pointInTopItem);
385     if (!enableItem || itemOpacity == 0) {
386         // middle item is disabled or has 0 opacity, bottom item receives the event
387         QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
388         QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
389         QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
390         COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
391                 makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos))));
392     } else {
393         // middle item ignores event, sends it to the top item (top-most child)
394         QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
395         QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
396         QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
397         COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
398                 makeTouchPoint(topItem, pos)));
399     }
400
401     delete topItem;
402     delete middleItem;
403     delete bottomItem;
404     delete canvas;
405 }
406
407 void tst_qsgcanvas::touchEvent_propagation_data()
408 {
409     QTest::addColumn<bool>("acceptEvents");
410     QTest::addColumn<bool>("enableItem");
411     QTest::addColumn<qreal>("itemOpacity");
412
413     QTest::newRow("disable events") << false << true << 1.0;
414     QTest::newRow("disable item") << true << false << 1.0;
415     QTest::newRow("opacity of 0") << true << true << 0.0;
416 }
417
418 void tst_qsgcanvas::clearCanvas()
419 {
420     QSGCanvas *canvas = new QSGCanvas;
421     QSGItem *item = new QSGItem;
422     item->setParentItem(canvas->rootItem());
423
424     QVERIFY(item->canvas() == canvas);
425
426     delete canvas;
427
428     QVERIFY(item->canvas() == 0);
429
430     delete item;
431 }
432
433
434
435 QTEST_MAIN(tst_qsgcanvas)
436
437 #include "tst_qsgcanvas.moc"