1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the test suite of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
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>
51 struct TouchEventData {
55 Qt::TouchPointStates states;
56 QList<QTouchEvent::TouchPoint> touchPoints;
59 static QTouchEvent::TouchPoint makeTouchPoint(QSGItem *item, const QPointF &p, const QPointF &lastPoint = QPointF())
61 QPointF last = lastPoint.isNull() ? p : lastPoint;
63 QTouchEvent::TouchPoint tp;
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()));
74 static TouchEventData makeTouchData(QEvent::Type type, QWidget *w, Qt::TouchPointStates states, const QList<QTouchEvent::TouchPoint> &touchPoints)
76 TouchEventData d = { type, w, 0, states, touchPoints };
80 static TouchEventData makeTouchData(QEvent::Type type, QWidget *w, Qt::TouchPointStates states, const QTouchEvent::TouchPoint &touchPoint)
82 QList<QTouchEvent::TouchPoint> points;
84 return makeTouchData(type, w, states, points);
86 static TouchEventData makeTouchData(QEvent::Type type, QWindow *w, Qt::TouchPointStates states, const QList<QTouchEvent::TouchPoint>& touchPoints)
88 TouchEventData d = { type, 0, w, states, touchPoints };
91 static TouchEventData makeTouchData(QEvent::Type type, QWindow *w, Qt::TouchPointStates states, const QTouchEvent::TouchPoint &touchPoint)
93 QList<QTouchEvent::TouchPoint> points;
95 return makeTouchData(type, w, states, points);
98 #define COMPARE_TOUCH_POINTS(tp1, tp2) \
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()); \
108 #define COMPARE_TOUCH_DATA(d1, d2) \
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]); \
119 class TestTouchItem : public QSGRectangle
123 TestTouchItem(QSGItem *parent = 0)
124 : QSGRectangle(parent), acceptEvents(true)
126 border()->setWidth(1);
134 lastEvent = makeTouchData(QEvent::None, canvas(), 0, QList<QTouchEvent::TouchPoint>());//CHECK_VALID
138 TouchEventData lastEvent;
141 virtual void touchEvent(QTouchEvent *event) {
146 lastEvent = makeTouchData(event->type(), event->widget(), event->touchPointStates(), event->touchPoints());
152 class ConstantUpdateItem : public QSGItem
156 ConstantUpdateItem(QSGItem *parent = 0) : QSGItem(parent), iterations(0) {setFlag(ItemHasContents);}
160 QSGNode* updatePaintNode(QSGNode *, UpdatePaintNodeData *){
167 class tst_qsgcanvas : public QObject
175 void cleanupTestCase();
177 void constantUpdates();
179 void touchEvent_basic();
180 void touchEvent_propagation();
181 void touchEvent_propagation_data();
186 tst_qsgcanvas::tst_qsgcanvas()
190 void tst_qsgcanvas::initTestCase()
194 void tst_qsgcanvas::cleanupTestCase()
198 //If the item calls update inside updatePaintNode, it should schedule another update
199 void tst_qsgcanvas::constantUpdates()
202 ConstantUpdateItem item(canvas.rootItem());
204 QTRY_VERIFY(item.iterations > 60);
207 void tst_qsgcanvas::touchEvent_basic()
209 QSGCanvas *canvas = new QSGCanvas;
210 canvas->resize(250, 250);
211 canvas->move(100, 100);
214 TestTouchItem *bottomItem = new TestTouchItem(canvas->rootItem());
215 bottomItem->setObjectName("Bottom Item");
216 bottomItem->setSize(QSizeF(150, 150));
218 TestTouchItem *middleItem = new TestTouchItem(bottomItem);
219 middleItem->setObjectName("Middle Item");
220 middleItem->setPos(QPointF(50, 50));
221 middleItem->setSize(QSizeF(150, 150));
223 TestTouchItem *topItem = new TestTouchItem(middleItem);
224 topItem->setObjectName("Top Item");
225 topItem->setPos(QPointF(50, 50));
226 topItem->setSize(QSizeF(150, 150));
230 // press single point
231 QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint(),canvas);
234 QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
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)));
242 // press multiple points
243 QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint(),canvas)
244 .press(1, bottomItem->mapToScene(pos).toPoint(), canvas);
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)));
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);
257 QTest::touchEvent(canvas).move(0, bottomItem->mapToScene(pos).toPoint(), canvas);
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)));
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);
267 QTest::touchEvent(canvas).move(0, topItem->mapToScene(pos).toPoint(), canvas);
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)));
274 // a single stationary press on an item shouldn't cause an event
275 QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint(), canvas);
277 QTest::touchEvent(canvas).stationary(0)
278 .press(1, bottomItem->mapToScene(pos).toPoint(), canvas);
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)));
288 // move touch point from top item to bottom, and release
289 QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint(),canvas);
291 QTest::touchEvent(canvas).release(0, bottomItem->mapToScene(pos).toPoint(),canvas);
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)));
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);
302 QTest::touchEvent(canvas).move(0, bottomItem->mapToScene(pos).toPoint(), canvas);
304 QTest::touchEvent(canvas).release(0, bottomItem->mapToScene(pos).toPoint(), canvas)
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)));
322 void tst_qsgcanvas::touchEvent_propagation()
324 QFETCH(bool, acceptEvents);
325 QFETCH(bool, enableItem);
326 QFETCH(qreal, itemOpacity);
328 QSGCanvas *canvas = new QSGCanvas;
329 canvas->resize(250, 250);
330 canvas->move(100, 100);
333 TestTouchItem *bottomItem = new TestTouchItem(canvas->rootItem());
334 bottomItem->setObjectName("Bottom Item");
335 bottomItem->setSize(QSizeF(150, 150));
337 TestTouchItem *middleItem = new TestTouchItem(bottomItem);
338 middleItem->setObjectName("Middle Item");
339 middleItem->setPos(QPointF(50, 50));
340 middleItem->setSize(QSizeF(150, 150));
342 TestTouchItem *topItem = new TestTouchItem(middleItem);
343 topItem->setObjectName("Top Item");
344 topItem->setPos(QPointF(50, 50));
345 topItem->setSize(QSizeF(150, 150));
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
353 topItem->acceptEvents = acceptEvents;
354 topItem->setEnabled(enableItem);
355 topItem->setOpacity(itemOpacity);
357 // single touch to top item, should be received by middle item
358 QTest::touchEvent(canvas).press(0, pointInTopItem, canvas);
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))));
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);
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) )));
378 // disable middleItem as well
379 middleItem->acceptEvents = acceptEvents;
380 middleItem->setEnabled(enableItem);
381 middleItem->setOpacity(itemOpacity);
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);
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)) )));
395 // disable bottom item as well
396 bottomItem->acceptEvents = acceptEvents;
397 bottomItem->setEnabled(enableItem);
398 bottomItem->setOpacity(itemOpacity);
400 // no events should be received
401 QTest::touchEvent(canvas).press(0, pointInTopItem, canvas)
402 .press(1, pointInMiddleItem, canvas)
403 .press(2, pointInBottomItem, canvas);
405 QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
406 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
407 QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
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);
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))));
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)));
441 void tst_qsgcanvas::touchEvent_propagation_data()
443 QTest::addColumn<bool>("acceptEvents");
444 QTest::addColumn<bool>("enableItem");
445 QTest::addColumn<qreal>("itemOpacity");
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;
452 void tst_qsgcanvas::clearCanvas()
454 QSGCanvas *canvas = new QSGCanvas;
455 QSGItem *item = new QSGItem;
456 item->setParentItem(canvas->rootItem());
458 QVERIFY(item->canvas() == canvas);
462 QVERIFY(item->canvas() == 0);
469 QTEST_MAIN(tst_qsgcanvas)
471 #include "tst_qsgcanvas.moc"