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 <QtGui/QWindowSystemInterface>
50 struct TouchEventData {
54 Qt::TouchPointStates states;
55 QList<QTouchEvent::TouchPoint> touchPoints;
58 static QTouchEvent::TouchPoint makeTouchPoint(QSGItem *item, const QPointF &p, const QPointF &lastPoint = QPointF())
60 QPointF last = lastPoint.isNull() ? p : lastPoint;
62 QTouchEvent::TouchPoint tp;
66 tp.setScenePos(item->mapToScene(p));
67 tp.setLastScenePos(item->mapToScene(last));
68 tp.setScreenPos(item->canvas()->mapToGlobal(tp.scenePos().toPoint()));
69 tp.setLastScreenPos(item->canvas()->mapToGlobal(tp.lastScenePos().toPoint()));
73 static TouchEventData makeTouchData(QEvent::Type type, QWidget *w, Qt::TouchPointStates states, const QList<QTouchEvent::TouchPoint> &touchPoints)
75 TouchEventData d = { type, w, 0, states, touchPoints };
79 static TouchEventData makeTouchData(QEvent::Type type, QWidget *w, Qt::TouchPointStates states, const QTouchEvent::TouchPoint &touchPoint)
81 QList<QTouchEvent::TouchPoint> points;
83 return makeTouchData(type, w, states, points);
85 static TouchEventData makeTouchData(QEvent::Type type, QWindow *w, Qt::TouchPointStates states, const QList<QTouchEvent::TouchPoint>& touchPoints)
87 TouchEventData d = { type, 0, w, states, touchPoints };
90 static TouchEventData makeTouchData(QEvent::Type type, QWindow *w, Qt::TouchPointStates states, const QTouchEvent::TouchPoint &touchPoint)
92 QList<QTouchEvent::TouchPoint> points;
94 return makeTouchData(type, w, states, points);
97 #define COMPARE_TOUCH_POINTS(tp1, tp2) \
99 QCOMPARE(tp1.pos(), tp2.pos()); \
100 QCOMPARE(tp1.lastPos(), tp2.lastPos()); \
101 QCOMPARE(tp1.scenePos(), tp2.scenePos()); \
102 QCOMPARE(tp1.lastScenePos(), tp2.lastScenePos()); \
103 QCOMPARE(tp1.screenPos(), tp2.screenPos()); \
104 QCOMPARE(tp1.lastScreenPos(), tp2.lastScreenPos()); \
107 #define COMPARE_TOUCH_DATA(d1, d2) \
109 QCOMPARE((int)d1.type, (int)d2.type); \
110 QCOMPARE(d1.widget, d2.widget); \
111 QCOMPARE((int)d1.states, (int)d2.states); \
112 QCOMPARE(d1.touchPoints.count(), d2.touchPoints.count()); \
113 for (int i=0; i<d1.touchPoints.count(); i++) { \
114 COMPARE_TOUCH_POINTS(d1.touchPoints[i], d2.touchPoints[i]); \
118 class TestTouchItem : public QSGRectangle
122 TestTouchItem(QSGItem *parent = 0)
123 : QSGRectangle(parent), acceptEvents(true), mousePressId(0)
125 border()->setWidth(1);
126 setAcceptedMouseButtons(Qt::LeftButton);
127 setFiltersChildMouseEvents(true);
135 lastEvent = makeTouchData(QEvent::None, canvas(), 0, QList<QTouchEvent::TouchPoint>());//CHECK_VALID
139 TouchEventData lastEvent;
143 virtual void touchEvent(QTouchEvent *event) {
148 lastEvent = makeTouchData(event->type(), event->widget(), event->touchPointStates(), event->touchPoints());
152 virtual void mousePressEvent(QMouseEvent *event) {
153 mousePressId = ++mousePressNum;
156 bool childMouseEventFilter(QSGItem *, QEvent *) {
157 mousePressId = ++mousePressNum;
161 static int mousePressNum;
164 int TestTouchItem::mousePressNum = 0;
166 class ConstantUpdateItem : public QSGItem
170 ConstantUpdateItem(QSGItem *parent = 0) : QSGItem(parent), iterations(0) {setFlag(ItemHasContents);}
174 QSGNode* updatePaintNode(QSGNode *, UpdatePaintNodeData *){
181 class tst_qsgcanvas : public QObject
189 void cleanupTestCase();
191 void constantUpdates();
193 void touchEvent_basic();
194 void touchEvent_propagation();
195 void touchEvent_propagation_data();
198 void mouseFiltering();
201 tst_qsgcanvas::tst_qsgcanvas()
205 void tst_qsgcanvas::initTestCase()
209 void tst_qsgcanvas::cleanupTestCase()
213 //If the item calls update inside updatePaintNode, it should schedule another update
214 void tst_qsgcanvas::constantUpdates()
217 ConstantUpdateItem item(canvas.rootItem());
219 QTRY_VERIFY(item.iterations > 60);
222 void tst_qsgcanvas::touchEvent_basic()
224 QSGCanvas *canvas = new QSGCanvas;
225 canvas->resize(250, 250);
226 canvas->move(100, 100);
229 TestTouchItem *bottomItem = new TestTouchItem(canvas->rootItem());
230 bottomItem->setObjectName("Bottom Item");
231 bottomItem->setSize(QSizeF(150, 150));
233 TestTouchItem *middleItem = new TestTouchItem(bottomItem);
234 middleItem->setObjectName("Middle Item");
235 middleItem->setPos(QPointF(50, 50));
236 middleItem->setSize(QSizeF(150, 150));
238 TestTouchItem *topItem = new TestTouchItem(middleItem);
239 topItem->setObjectName("Top Item");
240 topItem->setPos(QPointF(50, 50));
241 topItem->setSize(QSizeF(150, 150));
245 // press single point
246 QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint(),canvas);
249 QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
251 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
252 QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
253 TouchEventData d = makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(topItem,pos));
254 COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(topItem, pos)));
257 // press multiple points
258 QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint(),canvas)
259 .press(1, bottomItem->mapToScene(pos).toPoint(), canvas);
261 QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
262 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
263 QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
264 COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(topItem, pos)));
265 COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos)));
269 // touch point on top item moves to bottom item, but top item should still receive the event
270 QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint(), canvas);
272 QTest::touchEvent(canvas).move(0, bottomItem->mapToScene(pos).toPoint(), canvas);
274 QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
275 COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchUpdate, canvas, Qt::TouchPointMoved,
276 makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos), pos)));
279 // touch point on bottom item moves to top item, but bottom item should still receive the event
280 QTest::touchEvent(canvas).press(0, bottomItem->mapToScene(pos).toPoint(), canvas);
282 QTest::touchEvent(canvas).move(0, topItem->mapToScene(pos).toPoint(), canvas);
284 QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
285 COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchUpdate, canvas, Qt::TouchPointMoved,
286 makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos), pos)));
289 // a single stationary press on an item shouldn't cause an event
290 QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint(), canvas);
292 QTest::touchEvent(canvas).stationary(0)
293 .press(1, bottomItem->mapToScene(pos).toPoint(), canvas);
295 QCOMPARE(topItem->lastEvent.touchPoints.count(), 1); // received press only, not stationary
296 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
297 QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
298 COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(topItem, pos)));
299 COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos)));
303 // move touch point from top item to bottom, and release
304 QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint(),canvas);
306 QTest::touchEvent(canvas).release(0, bottomItem->mapToScene(pos).toPoint(),canvas);
308 QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
309 COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchEnd, canvas, Qt::TouchPointReleased,
310 makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos), pos)));
313 // release while another point is pressed
314 QTest::touchEvent(canvas).press(0, topItem->mapToScene(pos).toPoint(),canvas)
315 .press(1, bottomItem->mapToScene(pos).toPoint(), canvas);
317 QTest::touchEvent(canvas).move(0, bottomItem->mapToScene(pos).toPoint(), canvas);
319 QTest::touchEvent(canvas).release(0, bottomItem->mapToScene(pos).toPoint(), canvas)
322 QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
323 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
324 QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
325 COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchEnd, canvas, Qt::TouchPointReleased,
326 makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos))));
327 COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos)));
337 void tst_qsgcanvas::touchEvent_propagation()
339 QFETCH(bool, acceptEvents);
340 QFETCH(bool, enableItem);
341 QFETCH(qreal, itemOpacity);
343 QSGCanvas *canvas = new QSGCanvas;
344 canvas->resize(250, 250);
345 canvas->move(100, 100);
348 TestTouchItem *bottomItem = new TestTouchItem(canvas->rootItem());
349 bottomItem->setObjectName("Bottom Item");
350 bottomItem->setSize(QSizeF(150, 150));
352 TestTouchItem *middleItem = new TestTouchItem(bottomItem);
353 middleItem->setObjectName("Middle Item");
354 middleItem->setPos(QPointF(50, 50));
355 middleItem->setSize(QSizeF(150, 150));
357 TestTouchItem *topItem = new TestTouchItem(middleItem);
358 topItem->setObjectName("Top Item");
359 topItem->setPos(QPointF(50, 50));
360 topItem->setSize(QSizeF(150, 150));
363 QPoint pointInBottomItem = bottomItem->mapToScene(pos).toPoint(); // (10, 10)
364 QPoint pointInMiddleItem = middleItem->mapToScene(pos).toPoint(); // (60, 60) overlaps with bottomItem
365 QPoint pointInTopItem = topItem->mapToScene(pos).toPoint(); // (110, 110) overlaps with bottom & top items
368 topItem->acceptEvents = acceptEvents;
369 topItem->setEnabled(enableItem);
370 topItem->setOpacity(itemOpacity);
372 // single touch to top item, should be received by middle item
373 QTest::touchEvent(canvas).press(0, pointInTopItem, canvas);
375 QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
376 QCOMPARE(middleItem->lastEvent.touchPoints.count(), 1);
377 QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
378 COMPARE_TOUCH_DATA(middleItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
379 makeTouchPoint(middleItem, middleItem->mapFromItem(topItem, pos))));
381 // touch top and middle items, middle item should get both events
382 QTest::touchEvent(canvas).press(0, pointInTopItem, canvas)
383 .press(1, pointInMiddleItem, canvas);
385 QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
386 QCOMPARE(middleItem->lastEvent.touchPoints.count(), 2);
387 QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
388 COMPARE_TOUCH_DATA(middleItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
389 (QList<QTouchEvent::TouchPoint>() << makeTouchPoint(middleItem, middleItem->mapFromItem(topItem, pos))
390 << makeTouchPoint(middleItem, pos) )));
393 // disable middleItem as well
394 middleItem->acceptEvents = acceptEvents;
395 middleItem->setEnabled(enableItem);
396 middleItem->setOpacity(itemOpacity);
398 // touch top and middle items, bottom item should get all events
399 QTest::touchEvent(canvas).press(0, pointInTopItem, canvas)
400 .press(1, pointInMiddleItem, canvas);
402 QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
403 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
404 QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 2);
405 COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
406 (QList<QTouchEvent::TouchPoint>() << makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos))
407 << makeTouchPoint(bottomItem, bottomItem->mapFromItem(middleItem, pos)) )));
410 // disable bottom item as well
411 bottomItem->acceptEvents = acceptEvents;
412 bottomItem->setEnabled(enableItem);
413 bottomItem->setOpacity(itemOpacity);
415 // no events should be received
416 QTest::touchEvent(canvas).press(0, pointInTopItem, canvas)
417 .press(1, pointInMiddleItem, canvas)
418 .press(2, pointInBottomItem, canvas);
420 QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
421 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
422 QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
428 // disable middle item, touch on top item
429 middleItem->acceptEvents = acceptEvents;
430 middleItem->setEnabled(enableItem);
431 middleItem->setOpacity(itemOpacity);
432 QTest::touchEvent(canvas).press(0, pointInTopItem, canvas);
434 if (!enableItem || itemOpacity == 0) {
435 // middle item is disabled or has 0 opacity, bottom item receives the event
436 QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
437 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
438 QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1);
439 COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
440 makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos))));
442 // middle item ignores event, sends it to the top item (top-most child)
443 QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
444 QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
445 QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
446 COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed,
447 makeTouchPoint(topItem, pos)));
456 void tst_qsgcanvas::touchEvent_propagation_data()
458 QTest::addColumn<bool>("acceptEvents");
459 QTest::addColumn<bool>("enableItem");
460 QTest::addColumn<qreal>("itemOpacity");
462 QTest::newRow("disable events") << false << true << 1.0;
463 QTest::newRow("disable item") << true << false << 1.0;
464 QTest::newRow("opacity of 0") << true << true << 0.0;
467 void tst_qsgcanvas::clearCanvas()
469 QSGCanvas *canvas = new QSGCanvas;
470 QSGItem *item = new QSGItem;
471 item->setParentItem(canvas->rootItem());
473 QVERIFY(item->canvas() == canvas);
477 QVERIFY(item->canvas() == 0);
482 void tst_qsgcanvas::mouseFiltering()
484 QSGCanvas *canvas = new QSGCanvas;
485 canvas->resize(250, 250);
486 canvas->move(100, 100);
489 TestTouchItem *bottomItem = new TestTouchItem(canvas->rootItem());
490 bottomItem->setObjectName("Bottom Item");
491 bottomItem->setSize(QSizeF(150, 150));
493 TestTouchItem *middleItem = new TestTouchItem(bottomItem);
494 middleItem->setObjectName("Middle Item");
495 middleItem->setPos(QPointF(50, 50));
496 middleItem->setSize(QSizeF(150, 150));
498 TestTouchItem *topItem = new TestTouchItem(middleItem);
499 topItem->setObjectName("Top Item");
500 topItem->setPos(QPointF(50, 50));
501 topItem->setSize(QSizeF(150, 150));
503 QPoint pos(100, 100);
505 QTest::mousePress(canvas, Qt::LeftButton, 0, pos);
508 // Mouse filtering propagates down the stack, so the
510 // 1. middleItem filters event
511 // 2. bottomItem filters event
512 // 3. topItem receives event
513 QCOMPARE(middleItem->mousePressId, 1);
514 QCOMPARE(bottomItem->mousePressId, 2);
515 QCOMPARE(topItem->mousePressId, 3);
519 QTEST_MAIN(tst_qsgcanvas)
521 #include "tst_qsgcanvas.moc"