From f289141524d9331bdafd9f88d95e0fa9b29ccdee Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Sun, 28 Aug 2016 12:27:27 +0200 Subject: Add a C++ autotest for PinchHandler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adapted from the PinchArea test. cancel() does not work, TDB if we want to support that Done-with: Jan-Arve Sæther Task-number: QTBUG-69134 Change-Id: I63dfba7b327220b9f032f19c588cc19ebdfd95c2 Reviewed-by: Shawn Rutledge --- .../auto/quick/pointerhandlers/pointerhandlers.pro | 1 + .../qquickpinchhandler/data/pinchproperties.qml | 69 ++ .../qquickpinchhandler/data/threeFingers.qml | 59 ++ .../data/transformedPinchHandler.qml | 53 ++ .../qquickpinchhandler/qquickpinchhandler.pro | 16 + .../qquickpinchhandler/tst_qquickpinchhandler.cpp | 692 +++++++++++++++++++++ .../quick/qquickpincharea/tst_qquickpincharea.cpp | 2 +- 7 files changed, 891 insertions(+), 1 deletion(-) create mode 100644 tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchproperties.qml create mode 100644 tests/auto/quick/pointerhandlers/qquickpinchhandler/data/threeFingers.qml create mode 100644 tests/auto/quick/pointerhandlers/qquickpinchhandler/data/transformedPinchHandler.qml create mode 100644 tests/auto/quick/pointerhandlers/qquickpinchhandler/qquickpinchhandler.pro create mode 100644 tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp (limited to 'tests') diff --git a/tests/auto/quick/pointerhandlers/pointerhandlers.pro b/tests/auto/quick/pointerhandlers/pointerhandlers.pro index dc14bc1c27..df9869315f 100644 --- a/tests/auto/quick/pointerhandlers/pointerhandlers.pro +++ b/tests/auto/quick/pointerhandlers/pointerhandlers.pro @@ -7,6 +7,7 @@ qtConfig(private_tests) { qquickpointerhandler \ qquickpointhandler \ qquickdraghandler \ + qquickpinchhandler \ qquicktaphandler \ } diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchproperties.qml b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchproperties.qml new file mode 100644 index 0000000000..15775df1e7 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchproperties.qml @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 + +Rectangle { + id: whiteRect + property real scale: -1.0 + property int activeCount : 0 + property int deactiveCount : 0 + width: 240; height: 320 + color: "white" + Rectangle { + id: blackRect + objectName: "blackrect" + color: "black" + y: 50 + x: 50 + width: 100 + height: 100 + opacity: (whiteRect.width-blackRect.x+whiteRect.height-blackRect.y-199)/200 + Text { color: "white"; text: "opacity: " + blackRect.opacity + "\nscale: " + blackRect.scale} + PinchHandler { + id: pincharea + objectName: "pinchHandler" + minimumScale: 1.0 + maximumScale: 4.0 + minimumRotation: 0.0 + maximumRotation: 90.0 + xAxis.maximum: 140 + yAxis.maximum: 170 + onActiveChanged: { + whiteRect.scale = pincharea.scale + if (active) ++activeCount + else ++deactiveCount; + } + + onUpdated: { + whiteRect.scale = pincharea.scale + //whiteRect.pointCount = pincharea.pointCount + } + } + } + } diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/threeFingers.qml b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/threeFingers.qml new file mode 100644 index 0000000000..4d1a520c01 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/threeFingers.qml @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 + +Rectangle { + id: root + property variant centroid : pinchHandler.centroid + property real scale: pinchHandler.scale + property int pointCount: 0 + property bool pinchActive: pinchHandler.active + width: 240; height: 320 + + Rectangle { + id: blackRect + objectName: "blackrect" + color: "black" + y: 50 + x: 50 + width: 200 + height: 200 + + PinchHandler { + id: pinchHandler + objectName: "pinchHandler" + minimumScale: 0.5 + maximumScale: 2.0 + minimumRotation: 0.0 + maximumRotation: 0.0 + minimumPointCount: 3 + maximumPointCount: 3 + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/transformedPinchHandler.qml b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/transformedPinchHandler.qml new file mode 100644 index 0000000000..46e9ccca87 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/transformedPinchHandler.qml @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 + +Rectangle { + width: 400 + height: 400 + + Rectangle { + x: 100 + y: 100 + width: 200 + height: 200 + rotation: 45 + + Rectangle { + id: rect + scale: 0.5 + color: "black" + anchors.fill: parent + + PinchHandler { + objectName: "pinchHandler" + } + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/qquickpinchhandler.pro b/tests/auto/quick/pointerhandlers/qquickpinchhandler/qquickpinchhandler.pro new file mode 100644 index 0000000000..7e177d9786 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/qquickpinchhandler.pro @@ -0,0 +1,16 @@ +CONFIG += testcase +TARGET = tst_qquickpinchhandler +macos:CONFIG -= app_bundle + +SOURCES += tst_qquickpinchhandler.cpp +OTHER_FILES = \ + data/pinchproperties.qml \ + data/threeFingers.qml \ + data/transformedPinchArea.qml + +include (../../../shared/util.pri) +include (../../shared/util.pri) + +TESTDATA = data/* + +QT += core-private gui-private qml-private quick-private testlib diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp new file mode 100644 index 0000000000..818863bf86 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp @@ -0,0 +1,692 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../../shared/util.h" +#include "../../shared/viewtestutil.h" + +class tst_QQuickPinchHandler: public QQmlDataTest +{ + Q_OBJECT +public: + tst_QQuickPinchHandler() : device(0) { } +private slots: + void initTestCase(); + void cleanupTestCase(); + void pinchProperties(); + void scale(); + void scaleThreeFingers(); + void pan(); + void retouch(); + void cancel(); + void transformedpinchHandler_data(); + void transformedpinchHandler(); + +private: + QQuickView *createView(); + QTouchDevice *device; +}; +void tst_QQuickPinchHandler::initTestCase() +{ + QQmlDataTest::initTestCase(); + if (!device) { + device = new QTouchDevice; + device->setType(QTouchDevice::TouchScreen); + QWindowSystemInterface::registerTouchDevice(device); + } +} + +void tst_QQuickPinchHandler::cleanupTestCase() +{ + +} + +static bool withinBounds(qreal lower, qreal num, qreal upper) +{ + return num >= lower && num <= upper; +} + +void tst_QQuickPinchHandler::pinchProperties() +{ + QScopedPointer window(createView()); + window->setSource(testFileUrl("pinchproperties.qml")); + window->show(); + QVERIFY(window->rootObject() != nullptr); + + QQuickPinchHandler *pinchHandler = window->rootObject()->findChild("pinchHandler"); + QVERIFY(pinchHandler != nullptr); + + // target + QQuickItem *blackRect = window->rootObject()->findChild("blackrect"); + QVERIFY(blackRect != nullptr); + QCOMPARE(blackRect, pinchHandler->target()); + QQuickItem *rootItem = qobject_cast(window->rootObject()); + QVERIFY(rootItem != nullptr); + QSignalSpy targetSpy(pinchHandler, SIGNAL(targetChanged())); + pinchHandler->setTarget(rootItem); + QCOMPARE(targetSpy.count(),1); + pinchHandler->setTarget(rootItem); + QCOMPARE(targetSpy.count(),1); + + // axis + /* + QCOMPARE(pinchHandler->axis(), QQuickPinch::XAndYAxis); + QSignalSpy axisSpy(pinchHandler, SIGNAL(dragAxisChanged())); + pinchHandler->setAxis(QQuickPinch::XAxis); + QCOMPARE(pinchHandler->axis(), QQuickPinch::XAxis); + QCOMPARE(axisSpy.count(),1); + pinchHandler->setAxis(QQuickPinch::XAxis); + QCOMPARE(axisSpy.count(),1); + + // minimum and maximum drag properties + QSignalSpy xminSpy(pinchHandler, SIGNAL(minimumXChanged())); + QSignalSpy xmaxSpy(pinchHandler, SIGNAL(maximumXChanged())); + QSignalSpy yminSpy(pinchHandler, SIGNAL(minimumYChanged())); + QSignalSpy ymaxSpy(pinchHandler, SIGNAL(maximumYChanged())); + + QCOMPARE(pinchHandler->xmin(), 0.0); + QCOMPARE(pinchHandler->xmax(), rootItem->width()-blackRect->width()); + QCOMPARE(pinchHandler->ymin(), 0.0); + QCOMPARE(pinchHandler->ymax(), rootItem->height()-blackRect->height()); + + pinchHandler->setXmin(10); + pinchHandler->setXmax(10); + pinchHandler->setYmin(10); + pinchHandler->setYmax(10); + + QCOMPARE(pinchHandler->xmin(), 10.0); + QCOMPARE(pinchHandler->xmax(), 10.0); + QCOMPARE(pinchHandler->ymin(), 10.0); + QCOMPARE(pinchHandler->ymax(), 10.0); + + QCOMPARE(xminSpy.count(),1); + QCOMPARE(xmaxSpy.count(),1); + QCOMPARE(yminSpy.count(),1); + QCOMPARE(ymaxSpy.count(),1); + + pinchHandler->setXmin(10); + pinchHandler->setXmax(10); + pinchHandler->setYmin(10); + pinchHandler->setYmax(10); + + QCOMPARE(xminSpy.count(),1); + QCOMPARE(xmaxSpy.count(),1); + QCOMPARE(yminSpy.count(),1); + QCOMPARE(ymaxSpy.count(),1); + */ + + // minimum and maximum scale properties + QSignalSpy scaleMinSpy(pinchHandler, SIGNAL(minimumScaleChanged())); + QSignalSpy scaleMaxSpy(pinchHandler, SIGNAL(maximumScaleChanged())); + + QCOMPARE(pinchHandler->minimumScale(), 1.0); + QCOMPARE(pinchHandler->maximumScale(), 4.0); + + pinchHandler->setMinimumScale(0.5); + pinchHandler->setMaximumScale(1.5); + + QCOMPARE(pinchHandler->minimumScale(), 0.5); + QCOMPARE(pinchHandler->maximumScale(), 1.5); + + QCOMPARE(scaleMinSpy.count(),1); + QCOMPARE(scaleMaxSpy.count(),1); + + pinchHandler->setMinimumScale(0.5); + pinchHandler->setMaximumScale(1.5); + + QCOMPARE(scaleMinSpy.count(),1); + QCOMPARE(scaleMaxSpy.count(),1); + + // minimum and maximum rotation properties + QSignalSpy rotMinSpy(pinchHandler, SIGNAL(minimumRotationChanged())); + QSignalSpy rotMaxSpy(pinchHandler, SIGNAL(maximumRotationChanged())); + + QCOMPARE(pinchHandler->minimumRotation(), 0.0); + QCOMPARE(pinchHandler->maximumRotation(), 90.0); + + pinchHandler->setMinimumRotation(-90.0); + pinchHandler->setMaximumRotation(45.0); + + QCOMPARE(pinchHandler->minimumRotation(), -90.0); + QCOMPARE(pinchHandler->maximumRotation(), 45.0); + + QCOMPARE(rotMinSpy.count(),1); + QCOMPARE(rotMaxSpy.count(),1); + + pinchHandler->setMinimumRotation(-90.0); + pinchHandler->setMaximumRotation(45.0); + + QCOMPARE(rotMinSpy.count(),1); + QCOMPARE(rotMaxSpy.count(),1); +} + +QTouchEvent::TouchPoint makeTouchPoint(int id, QPoint p, QQuickView *v, QQuickItem *i) +{ + QTouchEvent::TouchPoint touchPoint(id); + touchPoint.setPos(i->mapFromScene(p)); + touchPoint.setScreenPos(v->mapToGlobal(p)); + touchPoint.setScenePos(p); + return touchPoint; +} + +void tst_QQuickPinchHandler::scale() +{ + QQuickView *window = createView(); + QScopedPointer scope(window); + window->setSource(testFileUrl("pinchproperties.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + QVERIFY(window->rootObject() != nullptr); + qApp->processEvents(); + + QQuickPinchHandler *pinchHandler = window->rootObject()->findChild("pinchHandler"); + QVERIFY(pinchHandler != nullptr); + + QQuickItem *root = qobject_cast(window->rootObject()); + QVERIFY(root != nullptr); + + // target + QQuickItem *blackRect = window->rootObject()->findChild("blackrect"); + QVERIFY(blackRect != nullptr); + + QPoint p0(80, 80); + QPoint p1(100, 100); + { + QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device); + pinchSequence.press(0, p0, window).commit(); + QQuickTouchUtils::flush(window); + // In order for the stationary point to remember its previous position, + // we have to reuse the same pinchSequence object. Otherwise if we let it + // be destroyed and then start a new sequence, point 0 will default to being + // stationary at 0, 0, and pinchHandler will filter out that touchpoint because + // it is outside its bounds. + pinchSequence.stationary(0).press(1, p1, window).commit(); + QQuickTouchUtils::flush(window); + + QPoint pd(10, 10); + // move one point until PinchHandler activates + for (int pi = 0; pi < 10 && !pinchHandler->active(); ++pi) { + p1 += pd; + pinchSequence.stationary(0).move(1, p1, window).commit(); + QQuickTouchUtils::flush(window); + } + QCOMPARE(pinchHandler->active(), true); + QLineF line(p0, p1); + const qreal startLength = line.length(); + + p1+=pd; + pinchSequence.stationary(0).move(1, p1, window).commit(); + QQuickTouchUtils::flush(window); + line.setP2(p1); + qreal scale = line.length() / startLength; + QVERIFY(qFloatDistance(root->property("scale").toReal(), scale) < 10); + QVERIFY(qFloatDistance(blackRect->scale(), scale) < 10); + + p1+=pd; + pinchSequence.stationary(0).move(1, p1, window).commit(); + QQuickTouchUtils::flush(window); + line.setP2(p1); + scale = line.length() / startLength; + + QVERIFY(qFloatDistance(root->property("scale").toReal(), scale) < 10); + QVERIFY(qFloatDistance(blackRect->scale(), scale) < 10); + + QPointF expectedCentroid = p0 + (p1 - p0)/2; + QCOMPARE(pinchHandler->centroid().scenePosition(), expectedCentroid); + } + + // scale beyond bound + p1 += QPoint(20, 20); + { + QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device); + pinchSequence.stationary(0).move(1, p1, window).commit(); + QQuickTouchUtils::flush(window); + QCOMPARE(blackRect->scale(), qreal(4)); // qquickpinchhandler does not manipulate scale property + pinchSequence.release(0, p0, window).release(1, p1, window).commit(); + QQuickTouchUtils::flush(window); + } + QCOMPARE(pinchHandler->active(), false); +} + +void tst_QQuickPinchHandler::scaleThreeFingers() +{ + QQuickView *window = createView(); + QScopedPointer scope(window); + window->setSource(testFileUrl("threeFingers.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + QVERIFY(window->rootObject() != nullptr); + qApp->processEvents(); + + QQuickPinchHandler *pinchHandler = window->rootObject()->findChild("pinchHandler"); + QVERIFY(pinchHandler != nullptr); + + QQuickItem *root = qobject_cast(window->rootObject()); + QVERIFY(root != nullptr); + + // target + QQuickItem *blackRect = window->rootObject()->findChild("blackrect"); + QVERIFY(blackRect != nullptr); + + // center of blackrect is at 150,150 + QPoint p0(80, 80); + QPoint p1(220, 80); + QPoint p2(150, 220); + { + QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device); + pinchSequence.press(0, p0, window).commit(); + QQuickTouchUtils::flush(window); + // In order for the stationary point to remember its previous position, + // we have to reuse the same pinchSequence object. Otherwise if we let it + // be destroyed and then start a new sequence, point 0 will default to being + // stationary at 0, 0, and pinchHandler will filter out that touchpoint because + // it is outside its bounds. + pinchSequence.stationary(0).press(1, p1, window).commit(); + QQuickTouchUtils::flush(window); + pinchSequence.stationary(0).stationary(1).press(2, p2, window).commit(); + QQuickTouchUtils::flush(window); + for (int i = 0; i < 5;++i) { + p0 += QPoint(-4, -4); + p1 += QPoint(+4, -4); + p2 += QPoint( 0, +6); + pinchSequence.move(0, p0,window).move(1, p1,window).move(2, p2,window).commit(); + QQuickTouchUtils::flush(window); + } + + QCOMPARE(pinchHandler->active(), true); + // scale we got was 1.1729088738267854364, but keep some slack + QVERIFY(withinBounds(1.163, root->property("scale").toReal(), 1.183)); + // should not rotate + QCOMPARE(root->property("rotation").toReal(), 0.); + + for (int i = 0; i < 5;++i) { + p0 += QPoint(-4, -4); + p1 += QPoint(+4, -4); + p2 += QPoint( 0, +6); + pinchSequence.move(0, p0,window).move(1, p1,window).move(2, p2,window).commit(); + QQuickTouchUtils::flush(window); + } + // scale we got was 1.4613, but keep some slack + QVERIFY(withinBounds(1.361, root->property("scale").toReal(), 1.561)); + + // since points were moved symetrically around the y axis, centroid should remain at x:150 + QCOMPARE(root->property("centroid").value().scenePosition().x(), 150); // blackrect is at 50,50 + + // scale beyond bound, we should reach the maximumScale + p0 += QPoint(-40, -40); + p1 += QPoint(+40, -40); + p2 += QPoint( 0, +60); + pinchSequence.move(0, p0,window).move(1, p1,window).move(2, p2,window).commit(); + QQuickTouchUtils::flush(window); + + QCOMPARE(root->property("scale").toReal(), 2.); + pinchSequence.release(0, p0, window).release(1, p1, window).release(2, p2, window).commit(); + QQuickTouchUtils::flush(window); + } + QCOMPARE(pinchHandler->active(), false); +} + +void tst_QQuickPinchHandler::pan() +{ + QQuickView *window = createView(); + QScopedPointer scope(window); + window->setSource(testFileUrl("pinchproperties.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + QVERIFY(window->rootObject() != nullptr); + qApp->processEvents(); + + QQuickPinchHandler *pinchHandler = window->rootObject()->findChild("pinchHandler"); + QVERIFY(pinchHandler != nullptr); + + QQuickItem *root = qobject_cast(window->rootObject()); + QVERIFY(root != nullptr); + + // target + QQuickItem *blackRect = window->rootObject()->findChild("blackrect"); + QVERIFY(blackRect != nullptr); + + QPoint p0(80, 80); + QPoint p1(100, 100); + { + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device); + pinchSequence.press(0, p0, window).commit(); + QQuickTouchUtils::flush(window); + // In order for the stationary point to remember its previous position, + // we have to reuse the same pinchSequence object. + pinchSequence.stationary(0).press(1, p1, window).commit(); + QQuickTouchUtils::flush(window); + QVERIFY(!root->property("pinchActive").toBool()); + QCOMPARE(root->property("scale").toReal(), -1.0); + + p0 += QPoint(dragThreshold, 0); + p1 += QPoint(dragThreshold, 0); + pinchSequence.move(0, p0, window).move(1, p1, window).commit(); + QQuickTouchUtils::flush(window); + // movement < dragThreshold: pinchHandler not yet active + QVERIFY(!root->property("pinchActive").toBool()); + QCOMPARE(root->property("scale").toReal(), -1.0); + + // just above the dragThreshold: pinchHandler starts + p0 += QPoint(1, 0); + p1 += QPoint(1, 0); + pinchSequence.move(0, p0, window).move(1, p1, window).commit(); + QQuickTouchUtils::flush(window); + QCOMPARE(pinchHandler->active(), true); + QCOMPARE(root->property("scale").toReal(), 1.0); + + // Calculation of the center point is tricky at first: + // center point of the two touch points in item coordinates: + // scene coordinates: (80, 80) + (dragThreshold, 0), (100, 100) + (dragThreshold, 0) + // = ((180+dT)/2, 180/2) = (90+dT, 90) + // item coordinates: (scene) - (50, 50) = (40+dT, 40) + QCOMPARE(pinchHandler->centroid().scenePosition(), QPointF(90 + dragThreshold + 1, 90)); + // pan started, but no actual movement registered yet: + // blackrect starts at 50,50 + QCOMPARE(blackRect->x(), 50.0); + QCOMPARE(blackRect->y(), 50.0); + + p0 += QPoint(10, 0); + p1 += QPoint(10, 0); + pinchSequence.move(0, p0, window).move(1, p1, window).commit(); + QQuickTouchUtils::flush(window); + QCOMPARE(pinchHandler->centroid().scenePosition(), QPointF(90 + dragThreshold + 11, 90)); + QCOMPARE(blackRect->x(), 60.0); + QCOMPARE(blackRect->y(), 50.0); + + p0 += QPoint(0, 10); + p1 += QPoint(0, 10); + pinchSequence.move(0, p0, window).move(1, p1, window).commit(); + QQuickTouchUtils::flush(window); + QCOMPARE(pinchHandler->centroid().scenePosition(), QPointF(90 + dragThreshold + 11, 90 + 10)); + QCOMPARE(blackRect->x(), 60.0); + QCOMPARE(blackRect->y(), 60.0); + + p0 += QPoint(10, 10); + p1 += QPoint(10, 10); + pinchSequence.move(0, p0, window).move(1, p1, window).commit(); + QQuickTouchUtils::flush(window); + // now the item moved again, thus the center point of the touch is moved in total by (10, 10) + QCOMPARE(pinchHandler->centroid().scenePosition(), QPointF(90 + dragThreshold + 21, 90 + 20)); + QCOMPARE(blackRect->x(), 70.0); + QCOMPARE(blackRect->y(), 70.0); + } + + // pan x beyond bound + p0 += QPoint(100,100); + p1 += QPoint(100,100); + QTest::touchEvent(window, device).move(0, p0, window).move(1, p1, window); + QQuickTouchUtils::flush(window); + + QCOMPARE(blackRect->x(), 140.0); + QCOMPARE(blackRect->y(), 170.0); + + QTest::touchEvent(window, device).release(0, p0, window).release(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(!root->property("pinchActive").toBool()); +} + +// test pinchHandler, release one point, touch again to continue pinchHandler +void tst_QQuickPinchHandler::retouch() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QQuickView *window = createView(); + QScopedPointer scope(window); + window->setSource(testFileUrl("pinchproperties.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + QVERIFY(window->rootObject() != nullptr); + qApp->processEvents(); + + QQuickPinchHandler *pinchHandler = window->rootObject()->findChild("pinchHandler"); + QVERIFY(pinchHandler != nullptr); + + QQuickItem *root = qobject_cast(window->rootObject()); + QVERIFY(root != nullptr); + + // target + QQuickItem *blackRect = window->rootObject()->findChild("blackrect"); + QVERIFY(blackRect != nullptr); + + QPoint p0(80, 80); + QPoint p1(100, 100); + { + QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device); + pinchSequence.press(0, p0, window).commit(); + QQuickTouchUtils::flush(window); + // In order for the stationary point to remember its previous position, + // we have to reuse the same pinchSequence object. + pinchSequence.stationary(0).press(1, p1, window).commit(); + QQuickTouchUtils::flush(window); + const QPoint delta(dragThreshold + 1, dragThreshold + 1); + p0 -= delta; + p1 += delta; + pinchSequence.move(0, p0,window).move(1, p1,window).commit(); + QQuickTouchUtils::flush(window); + + QCOMPARE(root->property("scale").toReal(), 1.0); + QCOMPARE(pinchHandler->active(), true); + + p0 -= delta; + p1 += delta; + pinchSequence.move(0, p0,window).move(1, p1,window).commit(); + QQuickTouchUtils::flush(window); + + QCOMPARE(pinchHandler->active(), true); + + // accept some slack + QVERIFY(withinBounds(1.4, root->property("scale").toReal(), 1.6)); + QCOMPARE(pinchHandler->centroid().position(), QPointF(40, 40)); // blackrect is at 50,50 + QVERIFY(withinBounds(1.4, blackRect->scale(), 1.6)); + + QCOMPARE(root->property("activeCount").toInt(), 1); + QCOMPARE(root->property("deactiveCount").toInt(), 0); + + // Hold down the first finger but release the second one + pinchSequence.stationary(0).release(1, p1, window).commit(); + QQuickTouchUtils::flush(window); + + QCOMPARE(root->property("activeCount").toInt(), 1); + QCOMPARE(root->property("deactiveCount").toInt(), 1); + + // Keep holding down the first finger and re-touch the second one, then move them both + pinchSequence.stationary(0).press(1, p1, window).commit(); + QQuickTouchUtils::flush(window); + p0 -= QPoint(10,10); + p1 += QPoint(10,10); + pinchSequence.move(0, p0, window).move(1, p1, window).commit(); + QQuickTouchUtils::flush(window); + + // Lifting and retouching results in onPinchStarted being called again + QCOMPARE(root->property("activeCount").toInt(), 2); + QCOMPARE(root->property("deactiveCount").toInt(), 1); + + pinchSequence.release(0, p0, window).release(1, p1, window).commit(); + QQuickTouchUtils::flush(window); + + QCOMPARE(pinchHandler->active(), false); + QCOMPARE(root->property("activeCount").toInt(), 2); + QCOMPARE(root->property("deactiveCount").toInt(), 2); + } +} + +void tst_QQuickPinchHandler::cancel() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QQuickView *window = createView(); + QScopedPointer scope(window); + window->setSource(testFileUrl("pinchproperties.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + QVERIFY(window->rootObject() != nullptr); + qApp->processEvents(); + + QQuickPinchHandler *pinchHandler = window->rootObject()->findChild("pinchHandler"); + QVERIFY(pinchHandler != nullptr); + + QQuickItem *root = qobject_cast(window->rootObject()); + QVERIFY(root != nullptr); + + // target + QQuickItem *blackRect = window->rootObject()->findChild("blackrect"); + QVERIFY(blackRect != nullptr); + + QPoint p0(80, 80); + QPoint p1(100, 100); + { + QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device); + pinchSequence.press(0, p0, window).commit(); + QQuickTouchUtils::flush(window); + // In order for the stationary point to remember its previous position, + // we have to reuse the same pinchSequence object. Otherwise if we let it + // be destroyed and then start a new sequence, point 0 will default to being + // stationary at 0, 0, and pinchHandler will filter out that touchpoint because + // it is outside its bounds. + pinchSequence.stationary(0).press(1, p1, window).commit(); + QQuickTouchUtils::flush(window); + const QPoint delta(dragThreshold + 1, dragThreshold + 1); + p0 -= delta; + p1 += delta; + pinchSequence.move(0, p0,window).move(1, p1,window).commit(); + QQuickTouchUtils::flush(window); + + QCOMPARE(root->property("scale").toReal(), 1.0); + QCOMPARE(pinchHandler->active(), true); + + p0 -= delta; + p1 += delta; + pinchSequence.move(0, p0,window).move(1, p1,window).commit(); + QQuickTouchUtils::flush(window); + + QVERIFY(withinBounds(1.4, root->property("scale").toReal(), 1.6)); + QCOMPARE(pinchHandler->centroid().position(), QPointF(40, 40)); // blackrect is at 50,50 + QVERIFY(withinBounds(1.4, blackRect->scale(), 1.6)); + + QSKIP("cancel is not supported atm"); + + QTouchEvent cancelEvent(QEvent::TouchCancel); + cancelEvent.setDevice(device); + QCoreApplication::sendEvent(window, &cancelEvent); + QQuickTouchUtils::flush(window); + + QCOMPARE(root->property("scale").toReal(), 1.0); + QCOMPARE(root->property("center").toPointF(), QPointF(40, 40)); // blackrect is at 50,50 + QCOMPARE(blackRect->scale(), 1.0); + QVERIFY(!root->property("pinchActive").toBool()); + } +} + +void tst_QQuickPinchHandler::transformedpinchHandler_data() +{ + QTest::addColumn("p0"); + QTest::addColumn("p1"); + QTest::addColumn("shouldPinch"); + + QTest::newRow("checking inner pinchHandler 1") + << QPoint(200, 140) << QPoint(200, 260) << true; + + QTest::newRow("checking inner pinchHandler 2") + << QPoint(140, 200) << QPoint(200, 140) << true; + + QTest::newRow("checking inner pinchHandler 3") + << QPoint(140, 200) << QPoint(260, 200) << true; + + QTest::newRow("checking outer pinchHandler 1") + << QPoint(140, 140) << QPoint(260, 260) << false; + + QTest::newRow("checking outer pinchHandler 2") + << QPoint(140, 140) << QPoint(200, 200) << false; + + QTest::newRow("checking outer pinchHandler 3") + << QPoint(140, 260) << QPoint(260, 260) << false; +} + +void tst_QQuickPinchHandler::transformedpinchHandler() +{ + QFETCH(QPoint, p0); + QFETCH(QPoint, p1); + QFETCH(bool, shouldPinch); + + QQuickView *view = createView(); + QScopedPointer scope(view); + view->setSource(testFileUrl("transformedPinchHandler.qml")); + view->show(); + QVERIFY(QTest::qWaitForWindowExposed(view)); + QVERIFY(view->rootObject() != nullptr); + qApp->processEvents(); + + QQuickPinchHandler *pinchHandler = view->rootObject()->findChild("pinchHandler"); + QVERIFY(pinchHandler != nullptr); + + const int threshold = qApp->styleHints()->startDragDistance(); + + { + QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(view, device); + // start pinchHandler + pinchSequence.press(0, p0, view).commit(); + QQuickTouchUtils::flush(view); + // In order for the stationary point to remember its previous position, + // we have to reuse the same pinchSequence object. + pinchSequence.stationary(0).press(1, p1, view).commit(); + QQuickTouchUtils::flush(view); + + // we move along the line that the two points form. + // The distance we move should be above the threshold (threshold * 2 to be safe) + QVector2D delta(p1 - p0); + delta.normalize(); + QVector2D movement = delta * (threshold * 2); + pinchSequence.stationary(0).move(1, p1 + movement.toPoint(), view).commit(); + QQuickTouchUtils::flush(view); + QCOMPARE(pinchHandler->active(), shouldPinch); + + // release pinchHandler + pinchSequence.release(0, p0, view).release(1, p1, view).commit(); + QQuickTouchUtils::flush(view); + QCOMPARE(pinchHandler->active(), false); + } +} + +QQuickView *tst_QQuickPinchHandler::createView() +{ + QQuickView *window = new QQuickView(0); + window->setGeometry(0,0,240,320); + + return window; +} + +QTEST_MAIN(tst_QQuickPinchHandler) + +#include "tst_qquickpinchhandler.moc" diff --git a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp index 2571e4f288..5b7108d96b 100644 --- a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp +++ b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. -- cgit v1.2.3