diff options
Diffstat (limited to 'tests/auto/quick/pointerhandlers/qquicktaphandler')
12 files changed, 400 insertions, 77 deletions
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/BLACKLIST b/tests/auto/quick/pointerhandlers/qquicktaphandler/BLACKLIST index d13d25390b..1559014480 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/BLACKLIST +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/BLACKLIST @@ -1,11 +1,3 @@ -# QTBUG-95939 -[touchGesturePolicyDragThreshold] -opensuse-leap - -# QTBUG-95939 -[mouseGesturePolicyDragThreshold] -opensuse-leap - # QTBUG-103072 [gesturePolicyDragWithinBounds] android diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/CMakeLists.txt b/tests/auto/quick/pointerhandlers/qquicktaphandler/CMakeLists.txt index cab8fc1a0d..94834e04c6 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/CMakeLists.txt +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qquicktaphandler Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qquicktaphandler LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml index fa58d76e4a..fc1e623902 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 @@ -10,13 +10,20 @@ Rectangle { property alias pressed: tap.pressed property bool checked: false property alias gesturePolicy: tap.gesturePolicy + property alias longPressThreshold: tap.longPressThreshold property point tappedPosition: Qt.point(0, 0) + property real timeHeldWhenTapped: 0 + property real timeHeldWhenLongPressed: 0 signal tapped signal canceled width: label.implicitWidth * 1.5; height: label.implicitHeight * 2.0 border.color: "#9f9d9a"; border.width: 1; radius: height / 4; antialiasing: true + function assignUndefinedLongPressThreshold() { + tap.longPressThreshold = undefined + } + gradient: Gradient { GradientStop { position: 0.0; color: tap.pressed ? "#b8b5b2" : "#efebe7" } GradientStop { position: 1.0; color: "#b8b5b2" } @@ -25,14 +32,17 @@ Rectangle { TapHandler { id: tap objectName: label.text - longPressThreshold: 100 // CI can be insanely slow, so don't demand a timely release to generate onTapped onSingleTapped: console.log("Single tap") onDoubleTapped: console.log("Double tap") - onTapped: { - console.log("Tapped") + onTapped: (eventPoint, button) => { + console.log("Tapped", button, eventPoint) tapFlash.start() root.tappedPosition = point.scenePosition root.tapped() + root.timeHeldWhenTapped = tap.timeHeld // eventPoint.timeHeld is already 0 + } + onLongPressed: { + root.timeHeldWhenLongPressed = tap.timeHeld } onCanceled: root.canceled() } diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/FlashAnimation.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/FlashAnimation.qml index e3f15f399f..9483a12d1c 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/FlashAnimation.qml +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/FlashAnimation.qml @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttonOverrideHandler.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttonOverrideHandler.qml index c5f2f6e1ae..b36fcdef08 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttonOverrideHandler.qml +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttonOverrideHandler.qml @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttons.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttons.qml index 5731f51f38..04fbbc176b 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttons.qml +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttons.qml @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 @@ -9,19 +9,25 @@ Item { Button { objectName: "DragThreshold" label: "DragThreshold" - x: 10; y: 10; width: parent.width - 20; height: 40 + x: 10; y: 10; width: 300; height: 40 gesturePolicy: TapHandler.DragThreshold } Button { objectName: "WithinBounds" label: "WithinBounds" - x: 10; y: 60; width: parent.width - 20; height: 40 + x: 10; y: 60; width: 300; height: 40 gesturePolicy: TapHandler.WithinBounds } Button { objectName: "ReleaseWithinBounds" label: "ReleaseWithinBounds" - x: 10; y: 110; width: parent.width - 20; height: 40 + x: 10; y: 110; width: 300; height: 40 gesturePolicy: TapHandler.ReleaseWithinBounds } + Button { + objectName: "DragWithinBounds" + label: "DragWithinBounds" + x: 10; y: 160; width: 300; height: 40 + gesturePolicy: TapHandler.DragWithinBounds + } } diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/dragReleaseMenu.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/dragReleaseMenu.qml index 1f819a937f..5f60ef879e 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/dragReleaseMenu.qml +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/dragReleaseMenu.qml @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nested.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nested.qml index f9db2d7179..c0f0b10bf4 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nested.qml +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nested.qml @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 Item { diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nestedAndSibling.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nestedAndSibling.qml new file mode 100644 index 0000000000..7732c42082 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nestedAndSibling.qml @@ -0,0 +1,39 @@ +import QtQuick 2.15 +import QtQuick.Window 2.15 +import QtQuick.Controls 2.15 + +Item { + width: 360 + height: 280 + + Rectangle { + width: 200; height: 200; x: 100; y: 10 + color: th1.pressed ? "blue" : "lightblue" + + TapHandler { + id: th1 + objectName: "th1" + } + + Rectangle { + width: 200; height: 200; x: 50; y: 50 + color: th2.pressed ? "steelblue" : "lightsteelblue" + + TapHandler { + id: th2 + objectName: "th2" + } + } + } + + Rectangle { + width: 200; height: 200; x: 10; y: 50 + color: th3.pressed ? "goldenrod" : "beige" + + TapHandler { + id: th3 + objectName: "th3" + } + } +} + diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/rightTapHandler.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/rightTapHandler.qml index 3517afe9c3..85f5c87b39 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/rightTapHandler.qml +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/rightTapHandler.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/simpleTapHandler.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/simpleTapHandler.qml index 54adc87c54..026be48f83 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/simpleTapHandler.qml +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/simpleTapHandler.qml @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp index f000d48171..1dab0836cd 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> @@ -44,10 +44,12 @@ private slots: void touchMultiTap(); void mouseMultiTap_data(); void mouseMultiTap(); + void mouseMultiTapLeftRight_data(); + void mouseMultiTapLeftRight(); void singleTapDoubleTap_data(); void singleTapDoubleTap(); - void touchLongPress(); - void mouseLongPress(); + void longPress_data(); + void longPress(); void buttonsMultiTouch(); void componentUserBehavioralOverride(); void rightLongPressIgnoreWheel(); @@ -55,6 +57,8 @@ private slots: void nonTopLevelParentWindow(); void nestedDoubleTap_data(); void nestedDoubleTap(); + void nestedAndSiblingPropagation_data(); + void nestedAndSiblingPropagation(); private: void createView(QScopedPointer<QQuickView> &window, const char *fileName, @@ -507,6 +511,23 @@ void tst_TapHandler::touchMultiTap() QQuickTouchUtils::flush(window); QTRY_VERIFY(!button->property("pressed").toBool()); QCOMPARE(tappedSpy.size(), 4); + + // Test a stray touch begin + tappedSpy.clear(); + constexpr int count = 2; + for (int i = 0; i < count; ++i) { + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + p1 -= QPoint(dragThreshold, dragThreshold); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + p1 += QPoint(dragThreshold, dragThreshold); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + } + QCOMPARE(tappedSpy.count(), count); } void tst_TapHandler::mouseMultiTap_data() @@ -592,6 +613,66 @@ void tst_TapHandler::mouseMultiTap() QCOMPARE(singleTapSpy.size(), expectedSingleTapsAfterWaiting); } +void tst_TapHandler::mouseMultiTapLeftRight_data() +{ + QTest::addColumn<QQuickTapHandler::ExclusiveSignals>("exclusiveSignals"); + QTest::addColumn<int>("expectedSingleTaps"); + QTest::addColumn<int>("expectedDoubleTaps"); + QTest::addColumn<int>("expectedTabCount2"); + QTest::addColumn<int>("expectedTabCount3"); + + QTest::newRow("NotExclusive") << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::NotExclusive) + << 3 << 0 << 1 << 1; + QTest::newRow("SingleTap") << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::SingleTap) + << 3 << 0 << 1 << 1; + QTest::newRow("DoubleTap") << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::DoubleTap) + << 0 << 0 << 1 << 1; + QTest::newRow("SingleTap|DoubleTap") << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::SingleTap | QQuickTapHandler::DoubleTap) + << 0 << 0 << 1 << 1; +} + +void tst_TapHandler::mouseMultiTapLeftRight() //QTBUG-111557 +{ + QFETCH(QQuickTapHandler::ExclusiveSignals, exclusiveSignals); + QFETCH(int, expectedSingleTaps); + QFETCH(int, expectedDoubleTaps); + QFETCH(int, expectedTabCount2); + QFETCH(int, expectedTabCount3); + + 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*>(); + QVERIFY(tapHandler); + tapHandler->setExclusiveSignals(exclusiveSignals); + tapHandler->setAcceptedButtons(Qt::LeftButton | Qt::RightButton); + QSignalSpy tappedSpy(button, SIGNAL(tapped())); + QSignalSpy singleTapSpy(tapHandler, &QQuickTapHandler::singleTapped); + QSignalSpy doubleTapSpy(tapHandler, &QQuickTapHandler::doubleTapped); + + // Click once with the left button + QPoint p1 = button->mapToScene(QPointF(2, 2)).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1, 10); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1, 10); + + // Click again with the right button -> should reset tabCount() + QTest::mousePress(window, Qt::RightButton, Qt::NoModifier, p1, 10); + QTest::mouseRelease(window, Qt::RightButton, Qt::NoModifier, p1, 10); + + QCOMPARE(tapHandler->tapCount(), expectedTabCount2); + + // Click again with the left button -> should reset tabCount() + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1, 10); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1, 10); + + QCOMPARE(tapHandler->tapCount(), expectedTabCount3); + QCOMPARE(singleTapSpy.size(), expectedSingleTaps); + QCOMPARE(doubleTapSpy.size(), expectedDoubleTaps); +} + void tst_TapHandler::singleTapDoubleTap_data() { QTest::addColumn<QPointingDevice::DeviceType>("deviceType"); @@ -653,12 +734,13 @@ void tst_TapHandler::singleTapDoubleTap() QSignalSpy singleTapSpy(tapHandler, &QQuickTapHandler::singleTapped); QSignalSpy doubleTapSpy(tapHandler, &QQuickTapHandler::doubleTapped); - auto tap = [window, tapHandler, deviceType, this](const QPoint &p1) { + auto tap = [window, tapHandler, deviceType, this](const QPoint &p1, int delay = 10) { switch (static_cast<QPointingDevice::DeviceType>(deviceType)) { case QPointingDevice::DeviceType::Mouse: - QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, p1, 10); + QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, p1, delay); break; case QPointingDevice::DeviceType::TouchScreen: + QTest::qWait(delay); QTest::touchEvent(window, touchDevice).press(0, p1, window); QTRY_VERIFY(tapHandler->isPressed()); QTest::touchEvent(window, touchDevice).release(0, p1, window); @@ -679,78 +761,211 @@ void tst_TapHandler::singleTapDoubleTap() QTRY_COMPARE(doubleTapSpy.size(), expectedDoubleTapCount); QCOMPARE(tappedSpy.size(), 2); QCOMPARE(singleTapSpy.size(), expectedEndingSingleTapCount); -} -void tst_TapHandler::touchLongPress() -{ - QScopedPointer<QQuickView> windowPtr; - createView(windowPtr, "buttons.qml"); - QQuickView * window = windowPtr.data(); + // wait past the double-tap interval, then do it again + const auto delay = qApp->styleHints()->mouseDoubleClickInterval() + 10; + tappedSpy.clear(); + singleTapSpy.clear(); + doubleTapSpy.clear(); - 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())); + // tap once with delay + tap(p1, delay); + QCOMPARE(tappedSpy.size(), 1); + QCOMPARE(doubleTapSpy.size(), 0); - // Reduce the threshold so that we can get a long press quickly - tapHandler->setLongPressThreshold(0.5); - QCOMPARE(longPressThresholdChangedSpy.size(), 1); + // tap again immediately afterwards + tap(p1); + QTRY_COMPARE(doubleTapSpy.size(), expectedDoubleTapCount); + QCOMPARE(tappedSpy.size(), 2); + QCOMPARE(singleTapSpy.size(), expectedEndingSingleTapCount); +} - // 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.size(), 1); - timeHeldSpy.wait(); // the longer we hold it, the more this will occur - qDebug() << "held" << tapHandler->timeHeld() << "secs; timeHeld updated" << timeHeldSpy.size() << "times"; - QVERIFY(timeHeldSpy.size() > 0); - QVERIFY(tapHandler->timeHeld() > 0.4); // Should be > 0.5 but slow CI and timer granularity can interfere +void tst_TapHandler::longPress_data() +{ + QTest::addColumn<const QPointingDevice *>("device"); + QTest::addColumn<QString>("buttonName"); + QTest::addColumn<qreal>("longPressThreshold"); + QTest::addColumn<QPoint>("releaseOffset"); + QTest::addColumn<bool>("expectLongPress"); + QTest::addColumn<bool>("expectTapped"); - // 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.size(), 0); + const QPointingDevice *constTouchDevice = touchDevice; + + // Reduce the threshold so that we can get a long press quickly (faster in CI) + const qreal longPressThreshold = 0.3; + QTest::newRow("mouse, lpt longPressThreshold: DragThreshold") + << QPointingDevice::primaryPointingDevice() << "DragThreshold" + << longPressThreshold << QPoint(0, 0) << true << false; + QTest::newRow("touch, lpt longPressThreshold: DragThreshold") + << constTouchDevice << "DragThreshold" << longPressThreshold + << QPoint(0, 0) << true << false; + QTest::newRow("mouse, lpt longPressThreshold: DragThreshold, drag") + << QPointingDevice::primaryPointingDevice() << "DragThreshold" + << longPressThreshold << QPoint(50, 0) << false << false; + QTest::newRow("touch, lpt longPressThreshold: DragThreshold, drag") + << constTouchDevice << "DragThreshold" + << longPressThreshold << QPoint(50, 0) << false << false; + + QTest::newRow("mouse, lpt longPressThreshold: WithinBounds") + << QPointingDevice::primaryPointingDevice() << "WithinBounds" + << longPressThreshold << QPoint(0, 0) << true << false; + QTest::newRow("touch, lpt longPressThreshold: WithinBounds") + << constTouchDevice << "WithinBounds" + << longPressThreshold << QPoint(0, 0) << true << false; + QTest::newRow("mouse, lpt longPressThreshold: WithinBounds, drag") + << QPointingDevice::primaryPointingDevice() << "WithinBounds" + << longPressThreshold << QPoint(50, 0) << true << false; + QTest::newRow("touch, lpt longPressThreshold: WithinBounds, drag") + << constTouchDevice << "WithinBounds" + << longPressThreshold << QPoint(50, 0) << true << false; + QTest::newRow("mouse, lpt longPressThreshold: WithinBounds, drag out") + << QPointingDevice::primaryPointingDevice() << "WithinBounds" + << longPressThreshold << QPoint(155, 0) << false << false; + QTest::newRow("touch, lpt longPressThreshold: WithinBounds, drag out") + << constTouchDevice << "WithinBounds" + << longPressThreshold << QPoint(155, 0) << false << false; + + QTest::newRow("mouse, lpt longPressThreshold: ReleaseWithinBounds") + << QPointingDevice::primaryPointingDevice() << "ReleaseWithinBounds" + << longPressThreshold << QPoint(0, 0) << true << false; + QTest::newRow("touch, lpt longPressThreshold: ReleaseWithinBounds") + << constTouchDevice << "ReleaseWithinBounds" + << longPressThreshold << QPoint(0, 0) << true << false; + QTest::newRow("mouse, lpt longPressThreshold: ReleaseWithinBounds, drag") + << QPointingDevice::primaryPointingDevice() << "ReleaseWithinBounds" + << longPressThreshold << QPoint(50, 0) << true << false; + QTest::newRow("touch, lpt longPressThreshold: ReleaseWithinBounds, drag") + << constTouchDevice << "ReleaseWithinBounds" + << longPressThreshold << QPoint(50, 0) << true << false; + QTest::newRow("mouse, lpt longPressThreshold: ReleaseWithinBounds, drag out") + << QPointingDevice::primaryPointingDevice() << "ReleaseWithinBounds" + << longPressThreshold << QPoint(155, 0) << false << false; + QTest::newRow("touch, lpt longPressThreshold: ReleaseWithinBounds, drag out") + << constTouchDevice << "ReleaseWithinBounds" + << longPressThreshold << QPoint(155, 0) << false << false; + + QTest::newRow("mouse, lpt longPressThreshold: DragWithinBounds") + << QPointingDevice::primaryPointingDevice() << "DragWithinBounds" + << longPressThreshold << QPoint(0, 0) << true << false; + QTest::newRow("touch, lpt longPressThreshold: DragWithinBounds") + << constTouchDevice << "DragWithinBounds" + << longPressThreshold << QPoint(0, 0) << true << false; + QTest::newRow("mouse, lpt longPressThreshold: DragWithinBounds, drag") + << QPointingDevice::primaryPointingDevice() << "DragWithinBounds" + << longPressThreshold << QPoint(50, 0) << true << false; + QTest::newRow("touch, lpt longPressThreshold: DragWithinBounds, drag") + << constTouchDevice << "DragWithinBounds" + << longPressThreshold << QPoint(50, 0) << true << false; + QTest::newRow("mouse, lpt longPressThreshold: DragWithinBounds, drag out") + << QPointingDevice::primaryPointingDevice() << "DragWithinBounds" + << longPressThreshold << QPoint(155, 0) << false << false; + QTest::newRow("touch, lpt longPressThreshold: DragWithinBounds, drag out") + << constTouchDevice << "DragWithinBounds" + << longPressThreshold << QPoint(155, 0) << false << false; + + // Zero or negative threshold means long press is disabled + QTest::newRow("mouse, lpt 0: DragThreshold") + << QPointingDevice::primaryPointingDevice() << "DragThreshold" + << qreal(0) << QPoint(0, 0) << false << true; + QTest::newRow("mouse, lpt -1: DragThreshold") + << QPointingDevice::primaryPointingDevice() << "DragThreshold" + << qreal(-1) << QPoint(0, 0) << true << false; + QTest::newRow("touch, lpt 0: DragThreshold") + << constTouchDevice << "DragThreshold" + << qreal(0) << QPoint(0, 0) << false << true; + + QTest::newRow("mouse, lpt 0: WithinBounds") + << QPointingDevice::primaryPointingDevice() << "WithinBounds" + << qreal(0) << QPoint(0, 0) << false << true; + QTest::newRow("mouse, lpt -1: WithinBounds") + << QPointingDevice::primaryPointingDevice() << "WithinBounds" + << qreal(-1) << QPoint(0, 0) << true << false; + QTest::newRow("touch, lpt 0: WithinBounds") + << constTouchDevice << "WithinBounds" + << qreal(0) << QPoint(0, 0) << false << true; + + QTest::newRow("mouse, lpt 0: ReleaseWithinBounds") + << QPointingDevice::primaryPointingDevice() << "ReleaseWithinBounds" + << qreal(0) << QPoint(0, 0) << false << true; + QTest::newRow("mouse, lpt -1: ReleaseWithinBounds") + << QPointingDevice::primaryPointingDevice() << "ReleaseWithinBounds" + << qreal(-1) << QPoint(0, 0) << true << false; + QTest::newRow("touch, lpt 0: ReleaseWithinBounds") + << constTouchDevice << "ReleaseWithinBounds" + << qreal(0) << QPoint(0, 0) << false << true; + + QTest::newRow("mouse, lpt 0: DragWithinBounds") + << QPointingDevice::primaryPointingDevice() << "DragWithinBounds" + << qreal(0) << QPoint(0, 0) << false << true; + QTest::newRow("mouse, lpt -1: DragWithinBounds") + << QPointingDevice::primaryPointingDevice() << "DragWithinBounds" + << qreal(-1) << QPoint(0, 0) << true << false; + QTest::newRow("touch, lpt 0: DragWithinBounds") + << constTouchDevice << "DragWithinBounds" + << qreal(0) << QPoint(0, 0) << false << true; } -void tst_TapHandler::mouseLongPress() +void tst_TapHandler::longPress() { - QScopedPointer<QQuickView> windowPtr; - createView(windowPtr, "buttons.qml"); - QQuickView * window = windowPtr.data(); + QFETCH(const QPointingDevice *, device); + QFETCH(QString, buttonName); + QFETCH(qreal, longPressThreshold); + QFETCH(QPoint, releaseOffset); + QFETCH(bool, expectLongPress); + QFETCH(bool, expectTapped); - QQuickItem *button = window->rootObject()->findChild<QQuickItem*>("DragThreshold"); + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("buttons.qml"))); + + QQuickItem *button = window.rootObject()->findChild<QQuickItem*>(buttonName); QVERIFY(button); - QQuickTapHandler *tapHandler = button->findChild<QQuickTapHandler*>("DragThreshold"); + QQuickTapHandler *tapHandler = button->findChild<QQuickTapHandler*>(buttonName); 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.size(), 1); + const qreal defaultThreshold = tapHandler->longPressThreshold(); + qsizetype changedCount = 0; + QCOMPARE_GT(defaultThreshold, 0); + tapHandler->setLongPressThreshold(longPressThreshold); + if (longPressThreshold > 0) + QCOMPARE(longPressThresholdChangedSpy.size(), ++changedCount); + QVERIFY(QMetaObject::invokeMethod(button, "assignUndefinedLongPressThreshold")); + if (longPressThreshold > 0) + QCOMPARE(longPressThresholdChangedSpy.size(), ++changedCount); + QCOMPARE(tapHandler->longPressThreshold(), defaultThreshold); + tapHandler->setLongPressThreshold(longPressThreshold); // Press and hold QPoint p1 = button->mapToScene(button->clipRect().center()).toPoint(); - QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QQuickTest::pointerPress(device, &window, 1, p1); QTRY_VERIFY(button->property("pressed").toBool()); - QTRY_COMPARE(longPressedSpy.size(), 1); + QTRY_COMPARE(longPressedSpy.size(), expectLongPress ? 1 : 0); timeHeldSpy.wait(); // the longer we hold it, the more this will occur qDebug() << "held" << tapHandler->timeHeld() << "secs; timeHeld updated" << timeHeldSpy.size() << "times"; - QVERIFY(timeHeldSpy.size() > 0); - QVERIFY(tapHandler->timeHeld() > 0.4); // Should be > 0.5 but slow CI and timer granularity can interfere + QCOMPARE_GT(timeHeldSpy.size(), 0); + if (expectLongPress) { + // Should be > longPressThreshold but slow CI and timer granularity can interfere + QCOMPARE_GT(tapHandler->timeHeld(), longPressThreshold - 0.1); + } else { + // Should be quite small, but event delivery is not instantaneous + QCOMPARE_LT(tapHandler->timeHeld(), 0.3); + } + + // If we have an offset, we need a move between press and release for realistic simulation + if (!releaseOffset.isNull()) + QQuickTest::pointerMove(device, &window, 1, p1 + releaseOffset); - // Release and verify that tapped was not emitted - QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1, 500); + // Release (optionally at an offset) and check whether tapped was emitted + QQuickTest::pointerRelease(device, &window, 1, p1 + releaseOffset); QTRY_VERIFY(!button->property("pressed").toBool()); - QCOMPARE(tappedSpy.size(), 0); + if (expectLongPress) + QCOMPARE_GT(button->property("timeHeldWhenLongPressed").toReal(), longPressThreshold - 0.1); + QCOMPARE(tapHandler->timeHeld(), -1); + QCOMPARE(tappedSpy.size(), expectTapped ? 1 : 0); + QCOMPARE(longPressedSpy.size(), expectLongPress ? 1 : 0); } void tst_TapHandler::buttonsMultiTouch() @@ -866,8 +1081,8 @@ void tst_TapHandler::componentUserBehavioralOverride() QQuickTapHandler *userTapHandler = button->findChild<QQuickTapHandler*>("override"); QVERIFY(userTapHandler); QSignalSpy tappedSpy(button, SIGNAL(tapped())); - QSignalSpy innerGrabChangedSpy(innerTapHandler, SIGNAL(grabChanged(QPointingDevice::GrabTransition, QEventPoint))); - QSignalSpy userGrabChangedSpy(userTapHandler, SIGNAL(grabChanged(QPointingDevice::GrabTransition, QEventPoint))); + QSignalSpy innerGrabChangedSpy(innerTapHandler, SIGNAL(grabChanged(QPointingDevice::GrabTransition,QEventPoint))); + QSignalSpy userGrabChangedSpy(userTapHandler, SIGNAL(grabChanged(QPointingDevice::GrabTransition,QEventPoint))); QSignalSpy innerPressedChangedSpy(innerTapHandler, SIGNAL(pressedChanged())); QSignalSpy userPressedChangedSpy(userTapHandler, SIGNAL(pressedChanged())); @@ -1019,6 +1234,61 @@ void tst_TapHandler::nestedDoubleTap() // QTBUG-102625 childGesturePolicy == QQuickTapHandler::GesturePolicy::DragThreshold ? 4 : 2); } +void tst_TapHandler::nestedAndSiblingPropagation_data() +{ + QTest::addColumn<const QPointingDevice *>("device"); + QTest::addColumn<QQuickTapHandler::GesturePolicy>("gesturePolicy"); + QTest::addColumn<bool>("expectPropagation"); + + const QPointingDevice *constTouchDevice = touchDevice; + + QTest::newRow("primary, DragThreshold") << QPointingDevice::primaryPointingDevice() + << QQuickTapHandler::GesturePolicy::DragThreshold << true; + QTest::newRow("primary, WithinBounds") << QPointingDevice::primaryPointingDevice() + << QQuickTapHandler::GesturePolicy::WithinBounds << false; + QTest::newRow("primary, ReleaseWithinBounds") << QPointingDevice::primaryPointingDevice() + << QQuickTapHandler::GesturePolicy::ReleaseWithinBounds << false; + QTest::newRow("primary, DragWithinBounds") << QPointingDevice::primaryPointingDevice() + << QQuickTapHandler::GesturePolicy::DragWithinBounds << false; + + QTest::newRow("touch, DragThreshold") << constTouchDevice + << QQuickTapHandler::GesturePolicy::DragThreshold << true; + QTest::newRow("touch, WithinBounds") << constTouchDevice + << QQuickTapHandler::GesturePolicy::WithinBounds << false; + QTest::newRow("touch, ReleaseWithinBounds") << constTouchDevice + << QQuickTapHandler::GesturePolicy::ReleaseWithinBounds << false; + QTest::newRow("touch, DragWithinBounds") << constTouchDevice + << QQuickTapHandler::GesturePolicy::DragWithinBounds << false; +} + +void tst_TapHandler::nestedAndSiblingPropagation() // QTBUG-117387 +{ + QFETCH(const QPointingDevice *, device); + QFETCH(QQuickTapHandler::GesturePolicy, gesturePolicy); + QFETCH(bool, expectPropagation); + + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("nestedAndSibling.qml"))); + QQuickItem *root = window.rootObject(); + QQuickTapHandler *th1 = root->findChild<QQuickTapHandler*>("th1"); + QVERIFY(th1); + th1->setGesturePolicy(gesturePolicy); + QQuickTapHandler *th2 = root->findChild<QQuickTapHandler*>("th2"); + QVERIFY(th2); + th2->setGesturePolicy(gesturePolicy); + QQuickTapHandler *th3 = root->findChild<QQuickTapHandler*>("th3"); + QVERIFY(th3); + th3->setGesturePolicy(gesturePolicy); + + QPoint middle(180, 140); + QQuickTest::pointerPress(device, &window, 0, middle); + QVERIFY(th3->isPressed()); // it's on top + QCOMPARE(th2->isPressed(), expectPropagation); + QCOMPARE(th1->isPressed(), expectPropagation); + + QQuickTest::pointerRelease(device, &window, 0, middle); +} + QTEST_MAIN(tst_TapHandler) #include "tst_qquicktaphandler.moc" |