1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include <QtTest/QtTest>
43 #include <QtTest/QSignalSpy>
44 #include <QtGui/QStyleHints>
45 #include <qpa/qwindowsysteminterface.h>
46 #include <private/qquickpincharea_p.h>
47 #include <QtQuick/private/qquickrectangle_p.h>
48 #include <QtQuick/qquickview.h>
49 #include <QtQml/qqmlcontext.h>
50 #include "../../shared/util.h"
52 class tst_QQuickPinchArea: public QQmlDataTest
56 tst_QQuickPinchArea() : device(0) { }
59 void cleanupTestCase();
60 void pinchProperties();
64 void transformedPinchArea_data();
65 void transformedPinchArea();
68 QQuickView *createView();
71 void tst_QQuickPinchArea::initTestCase()
73 QQmlDataTest::initTestCase();
75 device = new QTouchDevice;
76 device->setType(QTouchDevice::TouchScreen);
77 QWindowSystemInterface::registerTouchDevice(device);
81 void tst_QQuickPinchArea::cleanupTestCase()
85 void tst_QQuickPinchArea::pinchProperties()
87 QQuickView *window = createView();
88 window->setSource(testFileUrl("pinchproperties.qml"));
90 window->requestActivateWindow();
91 QVERIFY(window->rootObject() != 0);
93 QQuickPinchArea *pinchArea = window->rootObject()->findChild<QQuickPinchArea*>("pincharea");
94 QQuickPinch *pinch = pinchArea->pinch();
95 QVERIFY(pinchArea != 0);
99 QQuickItem *blackRect = window->rootObject()->findChild<QQuickItem*>("blackrect");
100 QVERIFY(blackRect != 0);
101 QVERIFY(blackRect == pinch->target());
102 QQuickItem *rootItem = qobject_cast<QQuickItem*>(window->rootObject());
103 QVERIFY(rootItem != 0);
104 QSignalSpy targetSpy(pinch, SIGNAL(targetChanged()));
105 pinch->setTarget(rootItem);
106 QCOMPARE(targetSpy.count(),1);
107 pinch->setTarget(rootItem);
108 QCOMPARE(targetSpy.count(),1);
111 QCOMPARE(pinch->axis(), QQuickPinch::XandYAxis);
112 QSignalSpy axisSpy(pinch, SIGNAL(dragAxisChanged()));
113 pinch->setAxis(QQuickPinch::XAxis);
114 QCOMPARE(pinch->axis(), QQuickPinch::XAxis);
115 QCOMPARE(axisSpy.count(),1);
116 pinch->setAxis(QQuickPinch::XAxis);
117 QCOMPARE(axisSpy.count(),1);
119 // minimum and maximum drag properties
120 QSignalSpy xminSpy(pinch, SIGNAL(minimumXChanged()));
121 QSignalSpy xmaxSpy(pinch, SIGNAL(maximumXChanged()));
122 QSignalSpy yminSpy(pinch, SIGNAL(minimumYChanged()));
123 QSignalSpy ymaxSpy(pinch, SIGNAL(maximumYChanged()));
125 QCOMPARE(pinch->xmin(), 0.0);
126 QCOMPARE(pinch->xmax(), rootItem->width()-blackRect->width());
127 QCOMPARE(pinch->ymin(), 0.0);
128 QCOMPARE(pinch->ymax(), rootItem->height()-blackRect->height());
135 QCOMPARE(pinch->xmin(), 10.0);
136 QCOMPARE(pinch->xmax(), 10.0);
137 QCOMPARE(pinch->ymin(), 10.0);
138 QCOMPARE(pinch->ymax(), 10.0);
140 QCOMPARE(xminSpy.count(),1);
141 QCOMPARE(xmaxSpy.count(),1);
142 QCOMPARE(yminSpy.count(),1);
143 QCOMPARE(ymaxSpy.count(),1);
150 QCOMPARE(xminSpy.count(),1);
151 QCOMPARE(xmaxSpy.count(),1);
152 QCOMPARE(yminSpy.count(),1);
153 QCOMPARE(ymaxSpy.count(),1);
155 // minimum and maximum scale properties
156 QSignalSpy scaleMinSpy(pinch, SIGNAL(minimumScaleChanged()));
157 QSignalSpy scaleMaxSpy(pinch, SIGNAL(maximumScaleChanged()));
159 QCOMPARE(pinch->minimumScale(), 1.0);
160 QCOMPARE(pinch->maximumScale(), 2.0);
162 pinch->setMinimumScale(0.5);
163 pinch->setMaximumScale(1.5);
165 QCOMPARE(pinch->minimumScale(), 0.5);
166 QCOMPARE(pinch->maximumScale(), 1.5);
168 QCOMPARE(scaleMinSpy.count(),1);
169 QCOMPARE(scaleMaxSpy.count(),1);
171 pinch->setMinimumScale(0.5);
172 pinch->setMaximumScale(1.5);
174 QCOMPARE(scaleMinSpy.count(),1);
175 QCOMPARE(scaleMaxSpy.count(),1);
177 // minimum and maximum rotation properties
178 QSignalSpy rotMinSpy(pinch, SIGNAL(minimumRotationChanged()));
179 QSignalSpy rotMaxSpy(pinch, SIGNAL(maximumRotationChanged()));
181 QCOMPARE(pinch->minimumRotation(), 0.0);
182 QCOMPARE(pinch->maximumRotation(), 90.0);
184 pinch->setMinimumRotation(-90.0);
185 pinch->setMaximumRotation(45.0);
187 QCOMPARE(pinch->minimumRotation(), -90.0);
188 QCOMPARE(pinch->maximumRotation(), 45.0);
190 QCOMPARE(rotMinSpy.count(),1);
191 QCOMPARE(rotMaxSpy.count(),1);
193 pinch->setMinimumRotation(-90.0);
194 pinch->setMaximumRotation(45.0);
196 QCOMPARE(rotMinSpy.count(),1);
197 QCOMPARE(rotMaxSpy.count(),1);
202 QTouchEvent::TouchPoint makeTouchPoint(int id, QPoint p, QQuickView *v, QQuickItem *i)
204 QTouchEvent::TouchPoint touchPoint(id);
205 touchPoint.setPos(i->mapFromScene(p));
206 touchPoint.setScreenPos(v->mapToGlobal(p));
207 touchPoint.setScenePos(p);
211 void tst_QQuickPinchArea::scale()
213 QQuickView *window = createView();
214 window->setSource(testFileUrl("pinchproperties.qml"));
216 window->requestActivateWindow();
217 QVERIFY(QTest::qWaitForWindowActive(window));
218 QVERIFY(window->rootObject() != 0);
219 qApp->processEvents();
221 QQuickPinchArea *pinchArea = window->rootObject()->findChild<QQuickPinchArea*>("pincharea");
222 QQuickPinch *pinch = pinchArea->pinch();
223 QVERIFY(pinchArea != 0);
226 QQuickItem *root = qobject_cast<QQuickItem*>(window->rootObject());
230 QQuickItem *blackRect = window->rootObject()->findChild<QQuickItem*>("blackrect");
231 QVERIFY(blackRect != 0);
236 QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device);
237 pinchSequence.press(0, p1, window).commit();
238 // In order for the stationary point to remember its previous position,
239 // we have to reuse the same pinchSequence object. Otherwise if we let it
240 // be destroyed and then start a new sequence, point 0 will default to being
241 // stationary at 0, 0, and PinchArea will filter out that touchpoint because
242 // it is outside its bounds.
243 pinchSequence.stationary(0).press(1, p2, window).commit();
246 pinchSequence.move(0, p1,window).move(1, p2,window).commit();
248 QCOMPARE(root->property("scale").toReal(), 1.0);
249 QVERIFY(root->property("pinchActive").toBool());
253 pinchSequence.move(0, p1,window).move(1, p2,window).commit();
255 QCOMPARE(root->property("scale").toReal(), 1.5);
256 QCOMPARE(root->property("center").toPointF(), QPointF(40, 40)); // blackrect is at 50,50
257 QCOMPARE(blackRect->scale(), 1.5);
260 // scale beyond bound
264 QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device);
265 pinchSequence.move(0, p1, window).move(1, p2, window).commit();
266 QCOMPARE(blackRect->scale(), 2.0);
267 pinchSequence.release(0, p1, window).release(1, p2, window).commit();
269 QVERIFY(!root->property("pinchActive").toBool());
274 void tst_QQuickPinchArea::pan()
276 QQuickView *window = createView();
277 window->setSource(testFileUrl("pinchproperties.qml"));
279 window->requestActivateWindow();
280 QVERIFY(QTest::qWaitForWindowActive(window));
281 QVERIFY(window->rootObject() != 0);
282 qApp->processEvents();
284 QQuickPinchArea *pinchArea = window->rootObject()->findChild<QQuickPinchArea*>("pincharea");
285 QQuickPinch *pinch = pinchArea->pinch();
286 QVERIFY(pinchArea != 0);
289 QQuickItem *root = qobject_cast<QQuickItem*>(window->rootObject());
293 QQuickItem *blackRect = window->rootObject()->findChild<QQuickItem*>("blackrect");
294 QVERIFY(blackRect != 0);
299 QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device);
300 pinchSequence.press(0, p1, window).commit();
301 // In order for the stationary point to remember its previous position,
302 // we have to reuse the same pinchSequence object.
303 pinchSequence.stationary(0).press(1, p2, window).commit();
306 pinchSequence.move(0, p1,window).move(1, p2,window).commit();
308 QCOMPARE(root->property("scale").toReal(), 1.0);
309 QVERIFY(root->property("pinchActive").toBool());
313 pinchSequence.move(0, p1,window).move(1, p2,window).commit();
316 QCOMPARE(root->property("center").toPointF(), QPointF(60, 60)); // blackrect is at 50,50
317 QCOMPARE(blackRect->x(), 60.0);
318 QCOMPARE(blackRect->y(), 60.0);
320 // pan x beyond bound
321 p1 += QPoint(100,100);
322 p2 += QPoint(100,100);
323 QTest::touchEvent(window, device).move(0, p1, window).move(1, p2, window);
325 QCOMPARE(blackRect->x(), 140.0);
326 QCOMPARE(blackRect->y(), 160.0);
328 QTest::touchEvent(window, device).release(0, p1, window).release(1, p2, window);
329 QVERIFY(!root->property("pinchActive").toBool());
334 // test pinch, release one point, touch again to continue pinch
335 void tst_QQuickPinchArea::retouch()
337 QQuickView *window = createView();
338 window->setSource(testFileUrl("pinchproperties.qml"));
340 window->requestActivateWindow();
341 QVERIFY(QTest::qWaitForWindowActive(window));
342 QVERIFY(window->rootObject() != 0);
343 qApp->processEvents();
345 QQuickPinchArea *pinchArea = window->rootObject()->findChild<QQuickPinchArea*>("pincharea");
346 QQuickPinch *pinch = pinchArea->pinch();
347 QVERIFY(pinchArea != 0);
350 QQuickItem *root = qobject_cast<QQuickItem*>(window->rootObject());
353 QSignalSpy startedSpy(pinchArea, SIGNAL(pinchStarted(QQuickPinchEvent *)));
354 QSignalSpy finishedSpy(pinchArea, SIGNAL(pinchFinished(QQuickPinchEvent *)));
357 QQuickItem *blackRect = window->rootObject()->findChild<QQuickItem*>("blackrect");
358 QVERIFY(blackRect != 0);
363 QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device);
364 pinchSequence.press(0, p1, window).commit();
365 // In order for the stationary point to remember its previous position,
366 // we have to reuse the same pinchSequence object.
367 pinchSequence.stationary(0).press(1, p2, window).commit();
370 pinchSequence.move(0, p1,window).move(1, p2,window).commit();
372 QCOMPARE(root->property("scale").toReal(), 1.0);
373 QVERIFY(root->property("pinchActive").toBool());
377 pinchSequence.move(0, p1,window).move(1, p2,window).commit();
379 QCOMPARE(startedSpy.count(), 1);
381 QCOMPARE(root->property("scale").toReal(), 1.5);
382 QCOMPARE(root->property("center").toPointF(), QPointF(40, 40)); // blackrect is at 50,50
383 QCOMPARE(blackRect->scale(), 1.5);
385 QCOMPARE(window->rootObject()->property("pointCount").toInt(), 2);
387 QCOMPARE(startedSpy.count(), 1);
388 QCOMPARE(finishedSpy.count(), 0);
390 // Hold down the first finger but release the second one
391 pinchSequence.stationary(0).release(1, p2, window).commit();
393 QCOMPARE(startedSpy.count(), 1);
394 QCOMPARE(finishedSpy.count(), 0);
396 QCOMPARE(window->rootObject()->property("pointCount").toInt(), 1);
398 // Keep holding down the first finger and re-touch the second one, then move them both
399 pinchSequence.stationary(0).press(1, p2, window).commit();
402 pinchSequence.move(0, p1, window).move(1, p2, window).commit();
404 // Lifting and retouching results in onPinchStarted being called again
405 QCOMPARE(startedSpy.count(), 2);
406 QCOMPARE(finishedSpy.count(), 0);
408 QCOMPARE(window->rootObject()->property("pointCount").toInt(), 2);
410 pinchSequence.release(0, p1, window).release(1, p2, window).commit();
412 QVERIFY(!root->property("pinchActive").toBool());
413 QCOMPARE(startedSpy.count(), 2);
414 QCOMPARE(finishedSpy.count(), 1);
420 void tst_QQuickPinchArea::transformedPinchArea_data()
422 QTest::addColumn<QPoint>("p1");
423 QTest::addColumn<QPoint>("p2");
424 QTest::addColumn<bool>("shouldPinch");
426 QTest::newRow("checking inner pinch 1")
427 << QPoint(200, 140) << QPoint(200, 260) << true;
429 QTest::newRow("checking inner pinch 2")
430 << QPoint(140, 200) << QPoint(200, 140) << true;
432 QTest::newRow("checking inner pinch 3")
433 << QPoint(140, 200) << QPoint(260, 200) << true;
435 QTest::newRow("checking outer pinch 1")
436 << QPoint(140, 140) << QPoint(260, 260) << false;
438 QTest::newRow("checking outer pinch 2")
439 << QPoint(140, 140) << QPoint(200, 200) << false;
441 QTest::newRow("checking outer pinch 3")
442 << QPoint(140, 260) << QPoint(260, 260) << false;
445 void tst_QQuickPinchArea::transformedPinchArea()
449 QFETCH(bool, shouldPinch);
451 QQuickView *view = createView();
452 view->setSource(testFileUrl("transformedPinchArea.qml"));
454 view->requestActivateWindow();
455 QVERIFY(QTest::qWaitForWindowActive(view));
456 QVERIFY(view->rootObject() != 0);
457 qApp->processEvents();
459 QQuickPinchArea *pinchArea = view->rootObject()->findChild<QQuickPinchArea*>("pinchArea");
460 QVERIFY(pinchArea != 0);
462 const int threshold = qApp->styleHints()->startDragDistance();
465 QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(view, device);
467 pinchSequence.press(0, p1, view).commit();
468 // In order for the stationary point to remember its previous position,
469 // we have to reuse the same pinchSequence object.
470 pinchSequence.stationary(0).press(1, p2, view).commit();
471 pinchSequence.stationary(0).move(1, p2 + QPoint(threshold * 2, 0), view).commit();
472 QCOMPARE(pinchArea->property("pinching").toBool(), shouldPinch);
475 pinchSequence.release(0, p1, view).release(1, p2, view).commit();
476 QCOMPARE(pinchArea->property("pinching").toBool(), false);
482 QQuickView *tst_QQuickPinchArea::createView()
484 QQuickView *window = new QQuickView(0);
485 window->setGeometry(0,0,240,320);
490 QTEST_MAIN(tst_QQuickPinchArea)
492 #include "tst_qquickpincharea.moc"