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>
47 #include "qsgcanvas.h"
48 #include "private/qsgrectangle_p.h"
49 #include "../../../shared/util.h"
51 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;
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()));
72 static TouchEventData makeTouchData(QEvent::Type type, QWidget *w, Qt::TouchPointStates states, const QList<QTouchEvent::TouchPoint> &touchPoints)
74 TouchEventData d = { type, w, states, touchPoints };
78 static TouchEventData makeTouchData(QEvent::Type type, QWidget *w, Qt::TouchPointStates states, const QTouchEvent::TouchPoint &touchPoint)
80 QList<QTouchEvent::TouchPoint> points;
82 return makeTouchData(type, w, states, points);
85 #define COMPARE_TOUCH_POINTS(tp1, tp2) \
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()); \
95 #define COMPARE_TOUCH_DATA(d1, d2) \
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]); \
106 class TestTouchItem : public QSGRectangle
110 TestTouchItem(QSGItem *parent = 0)
111 : QSGRectangle(parent), acceptEvents(true)
113 border()->setWidth(1);
121 lastEvent = makeTouchData(QEvent::None, 0, 0, QList<QTouchEvent::TouchPoint>());
125 TouchEventData lastEvent;
128 virtual void touchEvent(QTouchEvent *event) {
133 lastEvent = makeTouchData(event->type(), event->widget(), event->touchPointStates(), event->touchPoints());
139 class ConstantUpdateItem : public QSGItem
143 ConstantUpdateItem(QSGItem *parent = 0) : QSGItem(parent), iterations(0) {setFlag(ItemHasContents);}
147 QSGNode* updatePaintNode(QSGNode *, UpdatePaintNodeData *){
154 class tst_qsgcanvas : public QObject
162 void cleanupTestCase();
164 void constantUpdates();
166 void touchEvent_basic();
167 void touchEvent_propagation();
168 void touchEvent_propagation_data();
173 tst_qsgcanvas::tst_qsgcanvas()
177 void tst_qsgcanvas::initTestCase()
181 void tst_qsgcanvas::cleanupTestCase()
185 //If the item calls update inside updatePaintNode, it should schedule another update
186 void tst_qsgcanvas::constantUpdates()
189 ConstantUpdateItem item(canvas.rootItem());
191 QTRY_VERIFY(item.iterations > 60);
194 void tst_qsgcanvas::touchEvent_basic()
196 QSGCanvas *canvas = new QSGCanvas;
197 canvas->resize(250, 250);
198 canvas->window()->move(100, 100);
201 TestTouchItem *bottomItem = new TestTouchItem(canvas->rootItem());
202 bottomItem->setObjectName("Bottom Item");
203 bottomItem->setSize(QSizeF(150, 150));
205 TestTouchItem *middleItem = new TestTouchItem(bottomItem);
206 middleItem->setObjectName("Middle Item");
207 middleItem->setPos(QPointF(50, 50));
208 middleItem->setSize(QSizeF(150, 150));
210 TestTouchItem *topItem = new TestTouchItem(middleItem);
211 topItem->setObjectName("Top Item");
212 topItem->setPos(QPointF(50, 50));
213 topItem->setSize(QSizeF(150, 150));
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)));
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)));
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)));
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)));
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)));
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)));
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())
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)));
293 void tst_qsgcanvas::touchEvent_propagation()
295 QFETCH(bool, acceptEvents);
296 QFETCH(bool, enableItem);
297 QFETCH(qreal, itemOpacity);
299 QSGCanvas *canvas = new QSGCanvas;
300 canvas->resize(250, 250);
301 canvas->window()->move(100, 100);
304 TestTouchItem *bottomItem = new TestTouchItem(canvas->rootItem());
305 bottomItem->setObjectName("Bottom Item");
306 bottomItem->setSize(QSizeF(150, 150));
308 TestTouchItem *middleItem = new TestTouchItem(bottomItem);
309 middleItem->setObjectName("Middle Item");
310 middleItem->setPos(QPointF(50, 50));
311 middleItem->setSize(QSizeF(150, 150));
313 TestTouchItem *topItem = new TestTouchItem(middleItem);
314 topItem->setObjectName("Top Item");
315 topItem->setPos(QPointF(50, 50));
316 topItem->setSize(QSizeF(150, 150));
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
324 topItem->acceptEvents = acceptEvents;
325 topItem->setEnabled(enableItem);
326 topItem->setOpacity(itemOpacity);
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))));
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) )));
347 // disable middleItem as well
348 middleItem->acceptEvents = acceptEvents;
349 middleItem->setEnabled(enableItem);
350 middleItem->setOpacity(itemOpacity);
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)) )));
363 // disable bottom item as well
364 bottomItem->acceptEvents = acceptEvents;
365 bottomItem->setEnabled(enableItem);
366 bottomItem->setOpacity(itemOpacity);
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());
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))));
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)));
407 void tst_qsgcanvas::touchEvent_propagation_data()
409 QTest::addColumn<bool>("acceptEvents");
410 QTest::addColumn<bool>("enableItem");
411 QTest::addColumn<qreal>("itemOpacity");
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;
418 void tst_qsgcanvas::clearCanvas()
420 QSGCanvas *canvas = new QSGCanvas;
421 QSGItem *item = new QSGItem;
422 item->setParentItem(canvas->rootItem());
424 QVERIFY(item->canvas() == canvas);
428 QVERIFY(item->canvas() == 0);
435 QTEST_MAIN(tst_qsgcanvas)
437 #include "tst_qsgcanvas.moc"