diff options
author | Shawn Rutledge <shawn.rutledge@qt.io> | 2017-03-22 09:54:57 +0100 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2017-04-21 13:31:13 +0000 |
commit | 1e87f4605bb9456ce642e9ff1797b96647a1ab1c (patch) | |
tree | 5a093dd25a40d0ec042cd9853ab7fa0b2515a2b4 /tests/auto/quick/pointerhandlers/qquicktaphandler | |
parent | df0649fc57de1276ec1d0eb22af8b1b7dfc81a57 (diff) |
add autotest for TapHandler
Change-Id: Idc516220365b3051e072506ede3f3159b0b736b2
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
Diffstat (limited to 'tests/auto/quick/pointerhandlers/qquicktaphandler')
5 files changed, 828 insertions, 0 deletions
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml new file mode 100644 index 0000000000..ead86e142b --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 + +Rectangle { + id: root + property alias label: label.text + property alias pressed: tap.isPressed + property bool checked: false + property alias gesturePolicy: tap.gesturePolicy + signal tapped + + width: label.implicitWidth * 1.5; height: label.implicitHeight * 2.0 + border.color: "#9f9d9a"; border.width: 1; radius: height / 4; antialiasing: true + + gradient: Gradient { + GradientStop { position: 0.0; color: tap.isPressed ? "#b8b5b2" : "#efebe7" } + GradientStop { position: 1.0; color: "#b8b5b2" } + } + + TapHandler { + id: tap + objectName: label.text + longPressThreshold: 100 // CI can be insanely slow, so don't demand a timely release to generate onTapped + onTapped: { + tapFlash.start() + root.tapped() + } + } + + Text { + id: label + font.pointSize: 14 + text: "Button" + anchors.centerIn: parent + } + + Rectangle { + anchors.fill: parent + color: "transparent" + border.width: 2; radius: root.radius; antialiasing: true + opacity: tapFlash.running ? 1 : 0 + FlashAnimation on visible { id: tapFlash } + } + + Rectangle { + objectName: "expandingCircle" + radius: tap.timeHeld * 100 + visible: radius > 0 && tap.isPressed + border.width: 3 + border.color: "blue" + color: "transparent" + width: radius * 2 + height: radius * 2 + x: tap.scenePressPos.x - radius + y: tap.scenePressPos.y - radius + opacity: 0.25 + Component.onCompleted: parent = root.parent + } +} diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/FlashAnimation.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/FlashAnimation.qml new file mode 100644 index 0000000000..2224276819 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/FlashAnimation.qml @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +SequentialAnimation { + id: tapFlash + running: false + PropertyAction { value: false } + PauseAnimation { duration: 100 } + PropertyAction { value: true } + PauseAnimation { duration: 100 } + PropertyAction { value: false } + PauseAnimation { duration: 100 } + PropertyAction { value: true } + PauseAnimation { duration: 100 } + PropertyAction { value: false } + PauseAnimation { duration: 100 } + PropertyAction { value: true } +} diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttons.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttons.qml new file mode 100644 index 0000000000..ca1aba71fb --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttons.qml @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import Qt.labs.handlers 1.0 + +Item { + width: 320 + height: 240 + Button { + objectName: "DragThreshold" + label: "DragThreshold" + x: 10; y: 10; width: parent.width - 20; height: 40 + gesturePolicy: TapHandler.DragThreshold + } + Button { + objectName: "WithinBounds" + label: "WithinBounds" + x: 10; y: 60; width: parent.width - 20; height: 40 + gesturePolicy: TapHandler.WithinBounds + } + Button { + objectName: "ReleaseWithinBounds" + label: "ReleaseWithinBounds" + x: 10; y: 110; width: parent.width - 20; height: 40 + gesturePolicy: TapHandler.ReleaseWithinBounds + } +} diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/qquicktaphandler.pro b/tests/auto/quick/pointerhandlers/qquicktaphandler/qquicktaphandler.pro new file mode 100644 index 0000000000..b41a94b55e --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/qquicktaphandler.pro @@ -0,0 +1,16 @@ +CONFIG += testcase + +TARGET = tst_qquicktaphandler +QT += core-private gui-private qml-private quick-private testlib + +macos:CONFIG -= app_bundle + +SOURCES += tst_qquicktaphandler.cpp + +include (../../../shared/util.pri) +include (../../shared/util.pri) + +TESTDATA = data/* + +# OTHER_FILES += data/foo.qml + diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp new file mode 100644 index 0000000000..2c1926103f --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp @@ -0,0 +1,591 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module 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 <QtTest/QtTest> + +#include <QtGui/qstylehints.h> +#include <QtQuick/qquickview.h> +#include <QtQuick/qquickitem.h> +#include <QtQuick/private/qquickpointerhandler_p.h> +#include <QtQuick/private/qquicktaphandler_p.h> +#include <qpa/qwindowsysteminterface.h> + +#include <private/qquickwindow_p.h> + +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlproperty.h> + +#include "../../../shared/util.h" +#include "../../shared/viewtestutil.h" + +Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") + +class tst_TapHandler : public QQmlDataTest +{ + Q_OBJECT +public: + tst_TapHandler() + :touchDevice(QTest::createTouchDevice()) + {} + +private slots: + void initTestCase(); + + void touchGesturePolicyDragThreshold(); + void mouseGesturePolicyDragThreshold(); + void touchGesturePolicyWithinBounds(); + void mouseGesturePolicyWithinBounds(); + void touchGesturePolicyReleaseWithinBounds(); + void mouseGesturePolicyReleaseWithinBounds(); + void touchMultiTap(); + void mouseMultiTap(); + void touchLongPress(); + void mouseLongPress(); + void buttonsMultiTouch(); + +private: + void createView(QScopedPointer<QQuickView> &window, const char *fileName); + QTouchDevice *touchDevice; +}; + +void tst_TapHandler::createView(QScopedPointer<QQuickView> &window, const char *fileName) +{ + window.reset(new QQuickView); + window->setSource(testFileUrl(fileName)); + QTRY_COMPARE(window->status(), QQuickView::Ready); + QQuickViewTestUtil::centerOnScreen(window.data()); + QQuickViewTestUtil::moveMouseAway(window.data()); + + window->show(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QVERIFY(window->rootObject() != 0); +} + +void tst_TapHandler::initTestCase() +{ + // This test assumes that we don't get synthesized mouse events from QGuiApplication + qApp->setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, false); + + QQmlDataTest::initTestCase(); +} + +void tst_TapHandler::touchGesturePolicyDragThreshold() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "buttons.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *buttonDragThreshold = window->rootObject()->findChild<QQuickItem*>("DragThreshold"); + QVERIFY(buttonDragThreshold); + QSignalSpy dragThresholdTappedSpy(buttonDragThreshold, SIGNAL(tapped())); + + // DragThreshold button stays pressed while touchpoint stays within dragThreshold, emits tapped on release + QPoint p1 = buttonDragThreshold->mapToScene(QPointF(20, 20)).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(buttonDragThreshold->property("pressed").toBool()); + p1 += QPoint(dragThreshold, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(buttonDragThreshold->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!buttonDragThreshold->property("pressed").toBool()); + QCOMPARE(dragThresholdTappedSpy.count(), 1); + + // DragThreshold button is no longer pressed if touchpoint goes beyond dragThreshold + dragThresholdTappedSpy.clear(); + p1 = buttonDragThreshold->mapToScene(QPointF(20, 20)).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(buttonDragThreshold->property("pressed").toBool()); + p1 += QPoint(dragThreshold, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(buttonDragThreshold->property("pressed").toBool()); + p1 += QPoint(1, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!buttonDragThreshold->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(!buttonDragThreshold->property("pressed").toBool()); + QCOMPARE(dragThresholdTappedSpy.count(), 0); +} + +void tst_TapHandler::mouseGesturePolicyDragThreshold() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "buttons.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *buttonDragThreshold = window->rootObject()->findChild<QQuickItem*>("DragThreshold"); + QVERIFY(buttonDragThreshold); + QSignalSpy dragThresholdTappedSpy(buttonDragThreshold, SIGNAL(tapped())); + + // DragThreshold button stays pressed while mouse stays within dragThreshold, emits tapped on release + QPoint p1 = buttonDragThreshold->mapToScene(QPointF(20, 20)).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(buttonDragThreshold->property("pressed").toBool()); + p1 += QPoint(dragThreshold, 0); + QTest::mouseMove(window, p1); + QVERIFY(buttonDragThreshold->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(!buttonDragThreshold->property("pressed").toBool()); + QTRY_COMPARE(dragThresholdTappedSpy.count(), 1); + + // DragThreshold button is no longer pressed if mouse goes beyond dragThreshold + dragThresholdTappedSpy.clear(); + p1 = buttonDragThreshold->mapToScene(QPointF(20, 20)).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(buttonDragThreshold->property("pressed").toBool()); + p1 += QPoint(dragThreshold, 0); + QTest::mouseMove(window, p1); + QVERIFY(buttonDragThreshold->property("pressed").toBool()); + p1 += QPoint(1, 0); + QTest::mouseMove(window, p1); + QTRY_VERIFY(!buttonDragThreshold->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QVERIFY(!buttonDragThreshold->property("pressed").toBool()); + QCOMPARE(dragThresholdTappedSpy.count(), 0); +} + +void tst_TapHandler::touchGesturePolicyWithinBounds() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "buttons.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *buttonWithinBounds = window->rootObject()->findChild<QQuickItem*>("WithinBounds"); + QVERIFY(buttonWithinBounds); + QSignalSpy withinBoundsTappedSpy(buttonWithinBounds, SIGNAL(tapped())); + + // WithinBounds button stays pressed while touchpoint stays within bounds, emits tapped on release + QPoint p1 = buttonWithinBounds->mapToScene(QPointF(20, 20)).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(buttonWithinBounds->property("pressed").toBool()); + p1 += QPoint(50, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(buttonWithinBounds->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!buttonWithinBounds->property("pressed").toBool()); + QCOMPARE(withinBoundsTappedSpy.count(), 1); + + // WithinBounds button is no longer pressed if touchpoint leaves bounds + withinBoundsTappedSpy.clear(); + p1 = buttonWithinBounds->mapToScene(QPointF(20, 20)).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(buttonWithinBounds->property("pressed").toBool()); + p1 += QPoint(0, 100); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!buttonWithinBounds->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(!buttonWithinBounds->property("pressed").toBool()); + QCOMPARE(withinBoundsTappedSpy.count(), 0); +} + +void tst_TapHandler::mouseGesturePolicyWithinBounds() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "buttons.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *buttonWithinBounds = window->rootObject()->findChild<QQuickItem*>("WithinBounds"); + QVERIFY(buttonWithinBounds); + QSignalSpy withinBoundsTappedSpy(buttonWithinBounds, SIGNAL(tapped())); + + // WithinBounds button stays pressed while touchpoint stays within bounds, emits tapped on release + QPoint p1 = buttonWithinBounds->mapToScene(QPointF(20, 20)).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(buttonWithinBounds->property("pressed").toBool()); + p1 += QPoint(50, 0); + QTest::mouseMove(window, p1); + QVERIFY(buttonWithinBounds->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(!buttonWithinBounds->property("pressed").toBool()); + QCOMPARE(withinBoundsTappedSpy.count(), 1); + + // WithinBounds button is no longer pressed if touchpoint leaves bounds + withinBoundsTappedSpy.clear(); + p1 = buttonWithinBounds->mapToScene(QPointF(20, 20)).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(buttonWithinBounds->property("pressed").toBool()); + p1 += QPoint(0, 100); + QTest::mouseMove(window, p1); + QTRY_VERIFY(!buttonWithinBounds->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QVERIFY(!buttonWithinBounds->property("pressed").toBool()); + QCOMPARE(withinBoundsTappedSpy.count(), 0); +} + +void tst_TapHandler::touchGesturePolicyReleaseWithinBounds() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "buttons.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *buttonReleaseWithinBounds = window->rootObject()->findChild<QQuickItem*>("ReleaseWithinBounds"); + QVERIFY(buttonReleaseWithinBounds); + QSignalSpy releaseWithinBoundsTappedSpy(buttonReleaseWithinBounds, SIGNAL(tapped())); + + // ReleaseWithinBounds button stays pressed while touchpoint wanders anywhere, + // then if it comes back within bounds, emits tapped on release + QPoint p1 = buttonReleaseWithinBounds->mapToScene(QPointF(20, 20)).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + p1 += QPoint(50, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + p1 += QPoint(250, 100); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + p1 = buttonReleaseWithinBounds->mapToScene(QPointF(25, 15)).toPoint(); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!buttonReleaseWithinBounds->property("pressed").toBool()); + QCOMPARE(releaseWithinBoundsTappedSpy.count(), 1); + + // ReleaseWithinBounds button does not emit tapped if released out of bounds + releaseWithinBoundsTappedSpy.clear(); + p1 = buttonReleaseWithinBounds->mapToScene(QPointF(20, 20)).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + p1 += QPoint(0, 100); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!buttonReleaseWithinBounds->property("pressed").toBool()); + QCOMPARE(releaseWithinBoundsTappedSpy.count(), 0); +} + +void tst_TapHandler::mouseGesturePolicyReleaseWithinBounds() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "buttons.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *buttonReleaseWithinBounds = window->rootObject()->findChild<QQuickItem*>("ReleaseWithinBounds"); + QVERIFY(buttonReleaseWithinBounds); + QSignalSpy releaseWithinBoundsTappedSpy(buttonReleaseWithinBounds, SIGNAL(tapped())); + + // ReleaseWithinBounds button stays pressed while touchpoint wanders anywhere, + // then if it comes back within bounds, emits tapped on release + QPoint p1 = buttonReleaseWithinBounds->mapToScene(QPointF(20, 20)).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + p1 += QPoint(50, 0); + QTest::mouseMove(window, p1); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + p1 += QPoint(250, 100); + QTest::mouseMove(window, p1); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + p1 = buttonReleaseWithinBounds->mapToScene(QPointF(25, 15)).toPoint(); + QTest::mouseMove(window, p1); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(!buttonReleaseWithinBounds->property("pressed").toBool()); + QCOMPARE(releaseWithinBoundsTappedSpy.count(), 1); + + // ReleaseWithinBounds button does not emit tapped if released out of bounds + releaseWithinBoundsTappedSpy.clear(); + p1 = buttonReleaseWithinBounds->mapToScene(QPointF(20, 20)).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + p1 += QPoint(0, 100); + QTest::mouseMove(window, p1); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(!buttonReleaseWithinBounds->property("pressed").toBool()); + QCOMPARE(releaseWithinBoundsTappedSpy.count(), 0); +} + +void tst_TapHandler::touchMultiTap() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "buttons.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *button = window->rootObject()->findChild<QQuickItem*>("DragThreshold"); + QVERIFY(button); + QSignalSpy tappedSpy(button, SIGNAL(tapped())); + + // Tap once + QPoint p1 = button->mapToScene(QPointF(2, 2)).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(button->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 1); + + // Tap again in exactly the same place (not likely with touch in the real world) + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(button->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 2); + + // Tap a third time, nearby + p1 += QPoint(dragThreshold, dragThreshold); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(button->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 3); + + // Tap a fourth time, drifting farther away + p1 += QPoint(dragThreshold, dragThreshold); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(button->property("pressed").toBool()); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 4); +} + +void tst_TapHandler::mouseMultiTap() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "buttons.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *button = window->rootObject()->findChild<QQuickItem*>("DragThreshold"); + QVERIFY(button); + QSignalSpy tappedSpy(button, SIGNAL(tapped())); + + // Tap once + QPoint p1 = button->mapToScene(QPointF(2, 2)).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(button->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 1); + + // Tap again in exactly the same place (not likely with touch in the real world) + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(button->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 2); + + // Tap a third time, nearby + p1 += QPoint(dragThreshold, dragThreshold); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(button->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 3); + + // Tap a fourth time, drifting farther away + p1 += QPoint(dragThreshold, dragThreshold); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(button->property("pressed").toBool()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 4); +} + +void tst_TapHandler::touchLongPress() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "buttons.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *button = window->rootObject()->findChild<QQuickItem*>("DragThreshold"); + QVERIFY(button); + QQuickTapHandler *tapHandler = button->findChild<QQuickTapHandler*>("DragThreshold"); + QVERIFY(tapHandler); + QSignalSpy tappedSpy(button, SIGNAL(tapped())); + QSignalSpy longPressThresholdChangedSpy(tapHandler, SIGNAL(longPressThresholdChanged())); + QSignalSpy timeHeldSpy(tapHandler, SIGNAL(timeHeldChanged())); + QSignalSpy longPressedSpy(tapHandler, SIGNAL(longPressed())); + + // Reduce the threshold so that we can get a long press quickly + tapHandler->setLongPressThreshold(0.5); + QCOMPARE(longPressThresholdChangedSpy.count(), 1); + + // Press and hold + QPoint p1 = button->mapToScene(button->clipRect().center()).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(button->property("pressed").toBool()); + QTRY_COMPARE(longPressedSpy.count(), 1); + timeHeldSpy.wait(); // the longer we hold it, the more this will occur + qDebug() << "held" << tapHandler->timeHeld() << "secs; timeHeld updated" << timeHeldSpy.count() << "times"; + QVERIFY(timeHeldSpy.count() > 0); + QVERIFY(tapHandler->timeHeld() > 0.5); + + // Release and verify that tapped was not emitted + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 0); +} + +void tst_TapHandler::mouseLongPress() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "buttons.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *button = window->rootObject()->findChild<QQuickItem*>("DragThreshold"); + QVERIFY(button); + QQuickTapHandler *tapHandler = button->findChild<QQuickTapHandler*>("DragThreshold"); + QVERIFY(tapHandler); + QSignalSpy tappedSpy(button, SIGNAL(tapped())); + QSignalSpy longPressThresholdChangedSpy(tapHandler, SIGNAL(longPressThresholdChanged())); + QSignalSpy timeHeldSpy(tapHandler, SIGNAL(timeHeldChanged())); + QSignalSpy longPressedSpy(tapHandler, SIGNAL(longPressed())); + + // Reduce the threshold so that we can get a long press quickly + tapHandler->setLongPressThreshold(0.5); + QCOMPARE(longPressThresholdChangedSpy.count(), 1); + + // Press and hold + QPoint p1 = button->mapToScene(button->clipRect().center()).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(button->property("pressed").toBool()); + QTRY_COMPARE(longPressedSpy.count(), 1); + timeHeldSpy.wait(); // the longer we hold it, the more this will occur + qDebug() << "held" << tapHandler->timeHeld() << "secs; timeHeld updated" << timeHeldSpy.count() << "times"; + QVERIFY(timeHeldSpy.count() > 0); + QVERIFY(tapHandler->timeHeld() > 0.5); + + // Release and verify that tapped was not emitted + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1, 500); + QTRY_VERIFY(!button->property("pressed").toBool()); + QCOMPARE(tappedSpy.count(), 0); +} + +void tst_TapHandler::buttonsMultiTouch() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "buttons.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *buttonDragThreshold = window->rootObject()->findChild<QQuickItem*>("DragThreshold"); + QVERIFY(buttonDragThreshold); + QSignalSpy dragThresholdTappedSpy(buttonDragThreshold, SIGNAL(tapped())); + + QQuickItem *buttonWithinBounds = window->rootObject()->findChild<QQuickItem*>("WithinBounds"); + QVERIFY(buttonWithinBounds); + QSignalSpy withinBoundsTappedSpy(buttonWithinBounds, SIGNAL(tapped())); + + QQuickItem *buttonReleaseWithinBounds = window->rootObject()->findChild<QQuickItem*>("ReleaseWithinBounds"); + QVERIFY(buttonReleaseWithinBounds); + QSignalSpy releaseWithinBoundsTappedSpy(buttonReleaseWithinBounds, SIGNAL(tapped())); + + // can press multiple buttons at the same time + QPoint p1 = buttonDragThreshold->mapToScene(QPointF(20, 20)).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(buttonDragThreshold->property("pressed").toBool()); + QPoint p2 = buttonWithinBounds->mapToScene(QPointF(20, 20)).toPoint(); + QTest::touchEvent(window, touchDevice).stationary(1).press(2, p2, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(buttonWithinBounds->property("pressed").toBool()); + QPoint p3 = buttonReleaseWithinBounds->mapToScene(QPointF(20, 20)).toPoint(); + QTest::touchEvent(window, touchDevice).stationary(1).stationary(2).press(3, p3, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + + // can release top button and press again: others stay pressed the whole time + QTest::touchEvent(window, touchDevice).stationary(2).stationary(3).release(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!buttonDragThreshold->property("pressed").toBool()); + QCOMPARE(dragThresholdTappedSpy.count(), 1); + QVERIFY(buttonWithinBounds->property("pressed").toBool()); + QCOMPARE(withinBoundsTappedSpy.count(), 0); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + QCOMPARE(releaseWithinBoundsTappedSpy.count(), 0); + QTest::touchEvent(window, touchDevice).stationary(2).stationary(3).press(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(buttonDragThreshold->property("pressed").toBool()); + QVERIFY(buttonWithinBounds->property("pressed").toBool()); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + + // can release middle button and press again: others stay pressed the whole time + QTest::touchEvent(window, touchDevice).stationary(1).stationary(3).release(2, p2, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(!buttonWithinBounds->property("pressed").toBool()); + QCOMPARE(withinBoundsTappedSpy.count(), 1); + QVERIFY(buttonDragThreshold->property("pressed").toBool()); + QCOMPARE(dragThresholdTappedSpy.count(), 1); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + QCOMPARE(releaseWithinBoundsTappedSpy.count(), 0); + QTest::touchEvent(window, touchDevice).stationary(1).stationary(3).press(2, p2, window); + QQuickTouchUtils::flush(window); + QVERIFY(buttonDragThreshold->property("pressed").toBool()); + QVERIFY(buttonWithinBounds->property("pressed").toBool()); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); + + // can release bottom button and press again: others stay pressed the whole time + QTest::touchEvent(window, touchDevice).stationary(1).stationary(2).release(3, p3, window); + QQuickTouchUtils::flush(window); + QCOMPARE(releaseWithinBoundsTappedSpy.count(), 1); + QVERIFY(buttonWithinBounds->property("pressed").toBool()); + QCOMPARE(withinBoundsTappedSpy.count(), 1); + QVERIFY(!buttonReleaseWithinBounds->property("pressed").toBool()); + QCOMPARE(dragThresholdTappedSpy.count(), 1); + QTest::touchEvent(window, touchDevice).stationary(1).stationary(2).press(3, p3, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(buttonDragThreshold->property("pressed").toBool()); + QVERIFY(buttonWithinBounds->property("pressed").toBool()); + QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); +} + +QTEST_MAIN(tst_TapHandler) + +#include "tst_qquicktaphandler.moc" + |