diff options
Diffstat (limited to 'tests/auto/quick/pointerhandlers')
73 files changed, 1378 insertions, 212 deletions
diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST b/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST index 5c5f2c9ed9..fd3d9d96b8 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST +++ b/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST @@ -5,8 +5,6 @@ opensuse-leap windows gcc [touchDragFlickableBehindButton] windows gcc -[touchDragSliderAndFlickable] -* # QTBUG-86729 # QTBUG-95887 [mouseDragSlider] opensuse-leap @@ -26,3 +24,7 @@ android android [touchAndDragHandlerOnFlickable] android +# QTBUG-118063 +[nativeGesturePinchOnFlickableWithParentTapHandler] +opensuse-leap + diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/CMakeLists.txt b/tests/auto/quick/pointerhandlers/flickableinterop/CMakeLists.txt index b3c93bf804..3836aefbb2 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/CMakeLists.txt +++ b/tests/auto/quick/pointerhandlers/flickableinterop/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_flickableinterop Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_flickableinterop 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/flickableinterop/data/FlashAnimation.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/FlashAnimation.qml index e3f15f399f..9483a12d1c 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/data/FlashAnimation.qml +++ b/tests/auto/quick/pointerhandlers/flickableinterop/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/flickableinterop/data/GrooveDragSlider.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/GrooveDragSlider.qml index e4b2fb512d..ec790c9b99 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/data/GrooveDragSlider.qml +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/GrooveDragSlider.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/flickableinterop/data/KnobDragSlider.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/KnobDragSlider.qml index 0afd397a62..cba135269f 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/data/KnobDragSlider.qml +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/KnobDragSlider.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/flickableinterop/data/TapHandlerButton.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/TapHandlerButton.qml index 7f3045595d..99b53e6afb 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/data/TapHandlerButton.qml +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/TapHandlerButton.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/flickableinterop/data/dragOnFlickable.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnFlickable.qml index a253465a78..f6748da19c 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnFlickable.qml +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnFlickable.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 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/flickableinterop/data/dragOnList.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnList.qml index 224172b9d9..562dc156f9 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnList.qml +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnList.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 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/flickableinterop/data/dragOnTable.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnTable.qml index 2cbb40e416..bbd5f5b278 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnTable.qml +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnTable.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 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/flickableinterop/data/flickableWithHandlers.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/flickableWithHandlers.qml index 5468b5d98c..740b698fd4 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/data/flickableWithHandlers.qml +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/flickableWithHandlers.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/flickableinterop/data/pinchOnFlickable.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/pinchOnFlickable.qml new file mode 100644 index 0000000000..e594f165b2 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/pinchOnFlickable.qml @@ -0,0 +1,49 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick + +Flickable { + id: root + width: 800 + height: 480 + contentWidth: 1000 + contentHeight: 600 + + Rectangle { + id: pinchable + objectName: "pinchable" + border.color: "black" + color: pinch.active ? "salmon" : "peachpuff" + x: 100 + y: 100 + width: 200 + height: 200 + radius: 80 + PinchHandler { + id: pinch + } + PointHandler { + id: p1 + target: Rectangle { + parent: pinchable + color: "green" + visible: p1.active + x: p1.point.position.x - width / 2 + y: p1.point.position.y - height / 2 + width: 9; height: width; radius: width / 2 + } + } + PointHandler { + id: p0 + target: Rectangle { + parent: pinchable + color: "red" + visible: p0.active + x: p0.point.position.x - width / 2 + y: p0.point.position.y - height / 2 + width: 9; height: width; radius: width / 2 + } + } + } +} diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/pinchOnFlickableWithParentTapHandler.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/pinchOnFlickableWithParentTapHandler.qml new file mode 100644 index 0000000000..2660952f16 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/pinchOnFlickableWithParentTapHandler.qml @@ -0,0 +1,24 @@ +import QtQuick + +Rectangle { + width: 320 + height: 320 + + TapHandler { + onTapped: color = "tomato" + } + + Flickable { + anchors.fill: parent + contentWidth: content.width + contentHeight: content.height + Rectangle { + id: content + objectName: "pinchable" + width: 150 + height: 150 + color: "wheat" + PinchHandler {} + } + } +} diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnFlickable.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnFlickable.qml index 6c2854b28d..dc32b1d82c 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnFlickable.qml +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnFlickable.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 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/flickableinterop/data/tapOnList.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnList.qml index 99f3c2a98b..f4c0e5daaa 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnList.qml +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnList.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 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/flickableinterop/data/tapOnTable.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnTable.qml index baa6d99cd2..0293ad03b0 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnTable.qml +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnTable.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 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/flickableinterop/tst_flickableinterop.cpp b/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp index 05b764029a..790c7c771e 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp +++ b/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp @@ -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 #include <QtTest/QtTest> @@ -11,6 +11,7 @@ #include <QtQuick/private/qquickitemview_p.h> #include <QtQuick/private/qquickpointerhandler_p.h> #include <QtQuick/private/qquickdraghandler_p.h> +#include <QtQuick/private/qquickpinchhandler_p.h> #include <QtQuick/private/qquicktaphandler_p.h> #include <QtQuick/private/qquicktableview_p.h> #include <qpa/qwindowsysteminterface.h> @@ -31,7 +32,6 @@ class tst_FlickableInterop : public QQmlDataTest public: tst_FlickableInterop() : QQmlDataTest(QT_QMLTEST_DATADIR) - , touchDevice(QTest::createTouchDevice()) {} private slots: @@ -55,10 +55,15 @@ private slots: void touchDragSliderAndFlickable(); void touchAndDragHandlerOnFlickable_data(); void touchAndDragHandlerOnFlickable(); + void pinchHandlerOnFlickable(); + void nativeGesturePinchOnFlickableWithParentTapHandler_data(); + void nativeGesturePinchOnFlickableWithParentTapHandler(); private: void createView(QScopedPointer<QQuickView> &window, const char *fileName); - QPointingDevice *touchDevice; + QScopedPointer<QPointingDevice> touchDevice = QScopedPointer<QPointingDevice>(QTest::createTouchDevice()); + QScopedPointer<QPointingDevice> touchpad = QScopedPointer<QPointingDevice>( + QTest::createTouchDevice(QInputDevice::DeviceType::TouchPad)); }; void tst_FlickableInterop::createView(QScopedPointer<QQuickView> &window, const char *fileName) @@ -97,24 +102,24 @@ void tst_FlickableInterop::touchTapButton() // Button changes pressed state and emits tapped on release QPoint p1 = button->mapToScene(QPointF(20, 20)).toPoint(); - QTest::touchEvent(window, touchDevice).press(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).press(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(button->property("pressed").toBool()); - QTest::touchEvent(window, touchDevice).release(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).release(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(!button->property("pressed").toBool()); QCOMPARE(tappedSpy.size(), 1); // We can drag <= dragThreshold and the button still acts normal, Flickable doesn't grab p1 = button->mapToScene(QPointF(20, 20)).toPoint(); - QTest::touchEvent(window, touchDevice).press(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).press(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(button->property("pressed").toBool()); p1 += QPoint(dragThreshold, 0); - QTest::touchEvent(window, touchDevice).move(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).move(1, p1, window); QQuickTouchUtils::flush(window); QVERIFY(button->property("pressed").toBool()); - QTest::touchEvent(window, touchDevice).release(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).release(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(!button->property("pressed").toBool()); QCOMPARE(tappedSpy.size(), 2); @@ -145,11 +150,11 @@ void tst_FlickableInterop::touchDragFlickableBehindButton() tappedSpy.clear(); QPoint p1 = button->mapToScene(QPointF(20, 20)).toPoint(); - QTest::touchEvent(window, touchDevice).press(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).press(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(button->property("pressed").toBool()); p1 += QPoint(dragThreshold, 0); - QTest::touchEvent(window, touchDevice).move(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).move(1, p1, window); QQuickTouchUtils::flush(window); QVERIFY(button->property("pressed").toBool()); int i = 0; @@ -157,14 +162,14 @@ void tst_FlickableInterop::touchDragFlickableBehindButton() // Button is no longer pressed because Flickable steals the grab for (; i < 100 && !flickable->isMoving(); ++i) { p1 += QPoint(1, 0); - QTest::touchEvent(window, touchDevice).move(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).move(1, p1, window); QQuickTouchUtils::flush(window); } qCDebug(lcPointerTests) << "flickable started moving after" << i << "moves, when we got to" << p1; QVERIFY(flickable->isMoving()); QCOMPARE(i, 2); QVERIFY(!button->property("pressed").toBool()); - QTest::touchEvent(window, touchDevice).release(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).release(1, p1, window); QQuickTouchUtils::flush(window); QVERIFY(!button->property("pressed").toBool()); QCOMPARE(tappedSpy.size(), 0); @@ -241,7 +246,7 @@ void tst_FlickableInterop::mouseDragFlickableBehindButton() QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); QTRY_VERIFY(button->property("pressed").toBool()); p1 += QPoint(dragThreshold, 0); - QTest::touchEvent(window, touchDevice).move(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).move(1, p1, window); QVERIFY(button->property("pressed").toBool()); int i = 0; for (; i < 100 && !flickable->isMoving(); ++i) { @@ -278,19 +283,19 @@ void tst_FlickableInterop::touchDragSlider() // Drag the slider in the allowed (vertical) direction tappedSpy.clear(); QPoint p1 = knob->mapToScene(knob->clipRect().center()).toPoint() - QPoint(0, 8); - QTest::touchEvent(window, touchDevice).press(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).press(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(slider->property("pressed").toBool()); p1 += QPoint(0, dragThreshold); - QTest::touchEvent(window, touchDevice).move(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).move(1, p1, window); QQuickTouchUtils::flush(window); QVERIFY(slider->property("pressed").toBool()); QCOMPARE(slider->property("value").toInt(), 49); p1 += QPoint(0, 1); - QTest::touchEvent(window, touchDevice).move(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).move(1, p1, window); QQuickTouchUtils::flush(window); p1 += QPoint(0, 10); - QTest::touchEvent(window, touchDevice).move(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).move(1, p1, window); QQuickTouchUtils::flush(window); QVERIFY(slider->property("value").toInt() < 49); QVERIFY(!flickable->isMoving()); @@ -299,12 +304,12 @@ void tst_FlickableInterop::touchDragSlider() // Now that the DragHandler is active, the Flickable will not steal the grab // even if we move a large distance horizontally p1 += QPoint(dragThreshold * 2, 0); - QTest::touchEvent(window, touchDevice).move(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).move(1, p1, window); QQuickTouchUtils::flush(window); QVERIFY(!flickable->isMoving()); // Release, and do not expect the tapped signal - QTest::touchEvent(window, touchDevice).release(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).release(1, p1, window); QQuickTouchUtils::flush(window); QCOMPARE(tappedSpy.size(), 0); QCOMPARE(translationChangedSpy.size(), 1); @@ -417,24 +422,24 @@ void tst_FlickableInterop::touchDragFlickableBehindSlider() // because Flickable steals the grab tappedSpy.clear(); QPoint p1 = knob->mapToScene(knob->clipRect().center()).toPoint(); - QTest::touchEvent(window, touchDevice).press(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).press(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(slider->property("pressed").toBool()); p1 += QPoint(dragThreshold, 0); - QTest::touchEvent(window, touchDevice).move(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).move(1, p1, window); QQuickTouchUtils::flush(window); QVERIFY(slider->property("pressed").toBool()); int i = 0; for (; i < 100 && !flickable->isMoving(); ++i) { p1 += QPoint(1, 0); - QTest::touchEvent(window, touchDevice).move(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).move(1, p1, window); QQuickTouchUtils::flush(window); } qCDebug(lcPointerTests) << "flickable started moving after" << i << "moves, when we got to" << p1; QVERIFY(flickable->isMoving()); QCOMPARE(i, 2); QVERIFY(!slider->property("pressed").toBool()); - QTest::touchEvent(window, touchDevice).release(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).release(1, p1, window); QQuickTouchUtils::flush(window); QVERIFY(!slider->property("pressed").toBool()); QCOMPARE(tappedSpy.size(), 0); @@ -508,19 +513,19 @@ void tst_FlickableInterop::touchDragFlickableBehindItemWithHandlers() QPoint p1 = rect->mapToScene(rect->clipRect().center()).toPoint(); QPoint originP1 = p1; - QTest::touchEvent(window, touchDevice).press(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).press(1, p1, window); QQuickTouchUtils::flush(window); for (int i = 0; i < dragThreshold * 3; ++i) { p1 = originP1; p1.rx() += i; - QTest::touchEvent(window, touchDevice).move(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).move(1, p1, window); QQuickTouchUtils::flush(window); } QCOMPARE(flickable->isMoving(), expectedFlickableMoving); if (!expectedFlickableMoving) { QVERIFY(rect->mapToScene(rect->clipRect().center()).toPoint().x() > originP1.x()); } - QTest::touchEvent(window, touchDevice).release(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).release(1, p1, window); QQuickTouchUtils::flush(window); } @@ -585,7 +590,7 @@ void tst_FlickableInterop::touchDragSliderAndFlickable() QVERIFY(knob); QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>(); QVERIFY(flickable); - QTest::QTouchEventSequence touchSeq = QTest::touchEvent(window, touchDevice, false); + QTest::QTouchEventSequence touchSeq = QTest::touchEvent(window, touchDevice.get(), false); // The knob is initially centered over the slider's "groove" qreal initialXOffset = qAbs(knob->mapToScene(knob->clipRect().center()).x() - slider->mapToScene @@ -623,7 +628,10 @@ void tst_FlickableInterop::touchDragSliderAndFlickable() QQuickTouchUtils::flush(window); qCDebug(lcPointerTests) << "step" << i << ": fingers @" << p1 << p2 << "is Flickable moving yet?" << flickable->isMoving(); } - QVERIFY(flickable->isMoving()); + // In Qt 6, Flickable doesn't see the second touchpoint, so it doesn't move. + // One way to see this is that Flickable is more immune to stray touches than it otherwise would be. + // But time will tell if we are missing out on something useful, which was possible in Qt 5 (QTBUG-123490). + QCOMPARE(flickable->isMoving(), false); qreal knobSliderXOffset = qAbs(knob->mapToScene(knob->clipRect().center()).toPoint().x() - slider->mapToScene(slider->clipRect().center()).toPoint().x()) - initialXOffset; if (knobSliderXOffset > 1) @@ -715,7 +723,7 @@ void tst_FlickableInterop::touchAndDragHandlerOnFlickable() } // Drag one finger on the Flickable (between delegates) and make sure it flicks - QTest::QTouchEventSequence touchSeq = QTest::touchEvent(window, touchDevice, false); + QTest::QTouchEventSequence touchSeq = QTest::touchEvent(window, touchDevice.get(), false); QPoint p1(780, 460); if (delegate) p1 = delegate->mapToScene(delegate->clipRect().bottomRight()).toPoint() + QPoint(-1, 1); @@ -782,6 +790,187 @@ void tst_FlickableInterop::touchAndDragHandlerOnFlickable() touchSeq.release(1, p1, window).commit(); } +void tst_FlickableInterop::pinchHandlerOnFlickable() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("pinchOnFlickable.qml"))); + QQuickFlickable *flickable = qmlobject_cast<QQuickFlickable*>(window.rootObject()); + QVERIFY(flickable); + QQuickPointerHandler *pinchHandler = flickable->findChild<QQuickPointerHandler*>(); + QVERIFY(pinchHandler); + QQuickItem *pinchable = pinchHandler->target(); + QVERIFY(pinchable); + + QSignalSpy flickMoveSpy(flickable, &QQuickFlickable::movementStarted); + QSignalSpy grabChangedSpy(touchDevice.get(), &QPointingDevice::grabChanged); + + QObject *grabber = nullptr; + connect(touchDevice.get(), &QPointingDevice::grabChanged, + [&grabber](QObject *g, QPointingDevice::GrabTransition transition, const QPointerEvent *, const QEventPoint &) { + if (transition == QPointingDevice::GrabTransition::GrabExclusive) + grabber = g; + }); + + QPoint p0 = pinchable->mapToScene({50, 100}).toPoint(); + QPoint p1 = pinchable->mapToScene({150, 100}).toPoint(); + QTest::QTouchEventSequence touch = QTest::touchEvent(&window, touchDevice.get()); + + touch.press(0, p0, &window).press(1, p1, &window).commit(); + QQuickTouchUtils::flush(&window); + int activeStep = -1; + int grabTransitionCount = 0; + // drag two fingers down: PinchHandler moves the item; Flickable doesn't grab, because there are 2 points + for (int i = 0; i < 4; ++i) { + p0 += QPoint(0, dragThreshold); + p1 += QPoint(0, dragThreshold); + touch.move(0, p0, &window).move(1, p1, &window).commit(); + QQuickTouchUtils::flush(&window); + if (pinchHandler->active() && activeStep < 0) { + qCDebug(lcPointerTests) << "pinch began at step" << i; + activeStep = i; + QCOMPARE(grabber, pinchHandler); + grabTransitionCount = grabChangedSpy.count(); + } + } + QVERIFY(pinchHandler->active()); + QCOMPARE(grabChangedSpy.count(), grabTransitionCount); + QCOMPARE(grabber, pinchHandler); + qreal scale = pinchable->scale(); + QCOMPARE(scale, 1); + qreal rot = pinchable->rotation(); + QCOMPARE(rot, 0); + // start expanding and rotating + for (int i = 0; i < 4; ++i) { + p0 += QPoint(-5, 10); + p1 += QPoint(5, -10); + touch.move(0, p0, &window).move(1, p1, &window).commit(); + QQuickTouchUtils::flush(&window); + QVERIFY(pinchHandler->active()); + // PinchHandler keeps grab: no more transitions + QCOMPARE(grabChangedSpy.count(), grabTransitionCount); + QCOMPARE(grabber, pinchHandler); + QTRY_COMPARE_GT(pinchable->scale(), scale); + scale = pinchable->scale(); + QCOMPARE_LT(pinchable->rotation(), rot); + rot = pinchable->rotation(); + } + touch.release(0, p0, &window).release(1, p1, &window).commit(); + QQuickTouchUtils::flush(&window); + QTRY_COMPARE(pinchHandler->active(), false); + QCOMPARE(flickMoveSpy.count(), 0); // Flickable never moved +} + +void tst_FlickableInterop::nativeGesturePinchOnFlickableWithParentTapHandler_data() +{ + QTest::addColumn<const QPointingDevice*>("device"); + QTest::addColumn<Qt::MouseButton>("button"); + QTest::addColumn<Qt::NativeGestureType>("gesture"); + QTest::addColumn<qreal>("value"); + QTest::addColumn<qreal>("expectedPropertyValue"); + + const QPointingDevice *constTouchPad = touchpad.data(); + + QTest::newRow("touchpad: left and rotate") << constTouchPad << Qt::LeftButton << Qt::RotateNativeGesture << 5.0 << 10.0; + QTest::newRow("touchpad: right and rotate") << constTouchPad << Qt::RightButton << Qt::RotateNativeGesture << 5.0 << 10.0; + QTest::newRow("touchpad: left and scale") << constTouchPad << Qt::LeftButton << Qt::ZoomNativeGesture << 0.1 << 1.21; + QTest::newRow("touchpad: right and scale") << constTouchPad << Qt::RightButton << Qt::ZoomNativeGesture << 0.1 << 1.21; + + const auto *mouse = QPointingDevice::primaryPointingDevice(); + if (mouse->type() == QInputDevice::DeviceType::Mouse) { + QTest::newRow("mouse: left and rotate") << mouse << Qt::LeftButton << Qt::RotateNativeGesture << 5.0 << 10.0; + QTest::newRow("mouse: right and rotate") << mouse << Qt::RightButton << Qt::RotateNativeGesture << 5.0 << 10.0; + QTest::newRow("mouse: left and scale") << mouse << Qt::LeftButton << Qt::ZoomNativeGesture << 0.1 << 1.21; + QTest::newRow("mouse: right and scale") << mouse << Qt::RightButton << Qt::ZoomNativeGesture << 0.1 << 1.21; + } else { + qCWarning(lcPointerTests) << "skipping mouse tests: primary device is not a mouse" << mouse; + } +} + +void tst_FlickableInterop::nativeGesturePinchOnFlickableWithParentTapHandler() +{ + QFETCH(const QPointingDevice*, device); + QFETCH(Qt::MouseButton, button); + QFETCH(Qt::NativeGestureType, gesture); + QFETCH(qreal, value); + QFETCH(qreal, expectedPropertyValue); + + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("pinchOnFlickableWithParentTapHandler.qml"))); + QQuickFlickable *flickable = window.rootObject()->findChild<QQuickFlickable*>(); + QVERIFY(flickable); + QQuickPointerHandler *pinchHandler = flickable->findChild<QQuickPinchHandler*>(); + QVERIFY(pinchHandler); + QQuickItem *pinchable = pinchHandler->target(); + QVERIFY(pinchable); + QQuickTapHandler *tapHandler = window.rootObject()->findChild<QQuickTapHandler*>(); + QVERIFY(tapHandler); + const bool expectTap = button & tapHandler->acceptedButtons(); + + QSignalSpy flickMoveSpy(flickable, &QQuickFlickable::movementStarted); + QSignalSpy grabChangedSpy(touchDevice.get(), &QPointingDevice::grabChanged); + QSignalSpy tapActiveSpy(tapHandler, &QQuickTapHandler::activeChanged); + QSignalSpy tapSpy(tapHandler, &QQuickTapHandler::tapped); + + QObject *grabber = nullptr; + connect(device, &QPointingDevice::grabChanged, + [&grabber](QObject *g, QPointingDevice::GrabTransition transition, const QPointerEvent *, const QEventPoint &) { + if (transition == QPointingDevice::GrabTransition::GrabExclusive) + grabber = g; + }); + + const QPoint pinchPos(75, 75); + const QPoint outsidePos(200, 200); + + // move to position + QTest::mouseMove(&window, pinchPos); + + // pinch via native gesture + ulong ts = 502; // after the mouse move, which is at time 501 in practice + QWindowSystemInterface::handleGestureEvent(&window, ts++, touchpad.get(), + Qt::BeginNativeGesture, pinchPos, pinchPos); + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); + for (int i = 0; i < 2; ++i) { + QWindowSystemInterface::handleGestureEventWithRealValue(&window, ts++, touchpad.get(), + gesture, value, pinchPos, pinchPos); + } + if (gesture == Qt::RotateNativeGesture) + QTRY_COMPARE(pinchHandler->parentItem()->rotation(), expectedPropertyValue); + else if (gesture == Qt::ZoomNativeGesture) + QTRY_COMPARE(pinchHandler->parentItem()->scale(), expectedPropertyValue); + QVERIFY(pinchHandler->active()); + QCOMPARE(grabChangedSpy.count(), 0); + QCOMPARE(grabber, nullptr); + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); + QWindowSystemInterface::handleGestureEvent(&window, ts++, touchpad.get(), + Qt::EndNativeGesture, pinchPos, pinchPos); + + // tap in square: TapHandler detects tap iff acceptedButtons permits + // TODO delay; unfortunately this also begins at timestamp 502 because we don't have testlib + // functions to send gesture events, and QQuickTest::pointerPress() doesn't take a delay value + QQuickTest::pointerPress(device, &window, 0, pinchPos, button); + QCOMPARE(tapHandler->point().id(), expectTap ? 0 : -1); + QQuickTest::pointerRelease(device, &window, 0, pinchPos, button); + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); + QCOMPARE(tapSpy.size() != 0, expectTap); + QCOMPARE(tapActiveSpy.size(), 0); + QCOMPARE(tapHandler->point().id(), -1); // does not keep tracking after release + + // move outside: nothing should happen; + // but QTBUG-108896 happened because TapHandler was setting pointInfo to track this moving point + QQuickTest::pointerMove(device, &window, 0, outsidePos); + QCOMPARE(tapHandler->point().id(), -1); // does not track after mouse move + + // tap outside: nothing happens + tapSpy.clear(); + tapActiveSpy.clear(); + QQuickTest::pointerPress(device, &window, 0, outsidePos, button); + QQuickTest::pointerRelease(device, &window, 0, outsidePos, button); + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); + QCOMPARE(tapSpy.size(), 0); + QCOMPARE(tapActiveSpy.size(), 0); +} + QTEST_MAIN(tst_FlickableInterop) #include "tst_flickableinterop.moc" diff --git a/tests/auto/quick/pointerhandlers/mousearea_interop/CMakeLists.txt b/tests/auto/quick/pointerhandlers/mousearea_interop/CMakeLists.txt index f66a7e8d0d..22123e6385 100644 --- a/tests/auto/quick/pointerhandlers/mousearea_interop/CMakeLists.txt +++ b/tests/auto/quick/pointerhandlers/mousearea_interop/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_mousearea_interop Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_mousearea_interop 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/mousearea_interop/data/dragTakeOverFromSibling.qml b/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragTakeOverFromSibling.qml index 846c31cc61..1bde433dba 100644 --- a/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragTakeOverFromSibling.qml +++ b/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragTakeOverFromSibling.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 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/mousearea_interop/tst_mousearea_interop.cpp b/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp index 3c288cbb3b..b1a480b9cf 100644 --- a/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp +++ b/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2019 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> @@ -16,6 +16,8 @@ #include <QtQuickTestUtils/private/qmlutils_p.h> #include <QtQuickTestUtils/private/viewtestutils_p.h> +#include <QtCore/qpointer.h> + Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") class tst_MouseAreaInterop : public QQmlDataTest diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/BLACKLIST b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/BLACKLIST index 3f01d3a7d4..80d6dd0ee8 100644 --- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/BLACKLIST +++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/BLACKLIST @@ -1,2 +1,3 @@ -[touchesThenPinch] -* # QTBUG-86729 +# QTBUG-118062 +[touchDrag] +opensuse-leap diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/CMakeLists.txt b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/CMakeLists.txt index b0843f726b..9f19e8b427 100644 --- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/CMakeLists.txt +++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_multipointtoucharea_interop Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_multipointtoucharea_interop 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/multipointtoucharea_interop/data/dragParentOfMPTA.qml b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/dragParentOfMPTA.qml index e6ebfdd552..ad4494eb25 100644 --- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/dragParentOfMPTA.qml +++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/dragParentOfMPTA.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 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/multipointtoucharea_interop/data/pinchDragMPTA.qml b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/pinchDragMPTA.qml index b6fad6fade..199292e015 100644 --- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/pinchDragMPTA.qml +++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/pinchDragMPTA.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 @@ -35,8 +35,8 @@ Rectangle { Item { id: crosshairs property TouchPoint touchPoint - x: touchPoint.x - width / 2 - y: touchPoint.y - height / 2 + x: touchPoint?.x - width / 2 + y: touchPoint?.y - height / 2 width: 300; height: 300 visible: touchPoint.pressed rotation: touchPoint.rotation diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/unloadHandlerOnPress.qml b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/unloadHandlerOnPress.qml index e8b8fe769e..868a5265a6 100644 --- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/unloadHandlerOnPress.qml +++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/unloadHandlerOnPress.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 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/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp index ab64f6bb24..a4cc182422 100644 --- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp +++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.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> @@ -15,6 +15,8 @@ #include <QtQuickTestUtils/private/qmlutils_p.h> #include <QtQuickTestUtils/private/viewtestutils_p.h> +#include <QtCore/qpointer.h> + Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") class tst_MptaInterop : public QQmlDataTest @@ -250,7 +252,7 @@ void tst_MptaInterop::touchesThenPinch() } qCDebug(lcPointerTests) << "drag started after" << dragTookGrab << "moves; ended with translation" << drag->activeTranslation(); - QCOMPARE(devPriv->pointById(1)->exclusiveGrabber, drag); + QCOMPARE(devPriv->pointById(2)->exclusiveGrabber, drag); QTRY_VERIFY(drag->activeTranslation().x() > 0); touch.release(2, p2).commit(); diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/CMakeLists.txt b/tests/auto/quick/pointerhandlers/qquickdraghandler/CMakeLists.txt index 06cc34441a..23087c1d48 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/CMakeLists.txt +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qquickdraghandler Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qquickdraghandler 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/qquickdraghandler/data/DragAnywhereSlider.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/DragAnywhereSlider.qml index 7a305e7b79..065b0aaed8 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/DragAnywhereSlider.qml +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/DragAnywhereSlider.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 diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/FlashAnimation.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/FlashAnimation.qml index e3f15f399f..9483a12d1c 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/FlashAnimation.qml +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/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/qquickdraghandler/data/Slider.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/Slider.qml index 502891cd1b..200e846207 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/Slider.qml +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/Slider.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 diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draggables.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draggables.qml index 1041cd4f07..47be6052ad 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draggables.qml +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draggables.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.15 diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draghandler_and_pinchhandler.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draghandler_and_pinchhandler.qml index 7a4dc3a69a..5d700cdd08 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draghandler_and_pinchhandler.qml +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draghandler_and_pinchhandler.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 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/qquickdraghandler/data/multipleSliders.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/multipleSliders.qml index 146881e0a4..ea71da5623 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/multipleSliders.qml +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/multipleSliders.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 diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/simpleTapAndDragHandlers.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/simpleTapAndDragHandlers.qml index e805adbcb8..6c7a25c148 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/simpleTapAndDragHandlers.qml +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/simpleTapAndDragHandlers.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 diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml index 0a3d3618a8..30b28ac9e8 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 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/qquickdraghandler/tst_qquickdraghandler.cpp b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp index 9d6e4409d0..15df656b93 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp @@ -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 #include <QtTest/QtTest> @@ -17,6 +17,8 @@ #include <QtQuickTestUtils/private/qmlutils_p.h> #include <QtQuickTestUtils/private/viewtestutils_p.h> +#include <QtCore/qpointer.h> + Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") class tst_DragHandler : public QQmlDataTest diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/BLACKLIST b/tests/auto/quick/pointerhandlers/qquickhoverhandler/BLACKLIST index 1c4499753a..c0d73ff05f 100644 --- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/BLACKLIST +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/BLACKLIST @@ -3,3 +3,6 @@ macos # Can't move cursor (QTBUG-76312) # QTBUG-103065 [movingItemWithHoverHandler] android +[window] +opensuse-leap # QTBUG-122405 + diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/CMakeLists.txt b/tests/auto/quick/pointerhandlers/qquickhoverhandler/CMakeLists.txt index 25c8dfd604..a2e7d640d5 100644 --- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/CMakeLists.txt +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qquickhoverhandler Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qquickhoverhandler 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/qquickhoverhandler/data/changingCursor.qml b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/changingCursor.qml new file mode 100644 index 0000000000..42b658a4d4 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/changingCursor.qml @@ -0,0 +1,37 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick 2.15 + + +Rectangle { + id: brownRect + objectName: "brownRect" + + width: 400 + height: 400 + + HoverHandler { + id: hh + cursorShape: parent.colorIndex == 0 ? + Qt.CrossCursor : + Qt.OpenHandCursor + } + + property list<color> colors: ["beige", "brown"] + property int colorIndex: 0 + + color: colors[colorIndex] + + Timer { + id: colorTimer + interval: 200 + running: true + repeat: true + + onTriggered: { + parent.colorIndex = (parent.colorIndex + 1) % parent.colors.length; + parent.color = parent.colors[parent.colorIndex]; + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverDeviceCursors.qml b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverDeviceCursors.qml index edb56ffdc6..48e130a35e 100644 --- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverDeviceCursors.qml +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverDeviceCursors.qml @@ -3,47 +3,53 @@ import QtQuick Item { width: 200; height: 200 - HoverHandler { - objectName: "stylus" - acceptedDevices: PointerDevice.Stylus - acceptedPointerTypes: PointerDevice.Pen - cursorShape: Qt.CrossCursor - } + Rectangle { + width: 100; height: 100 + anchors.centerIn: parent + border.color: "black" - HoverHandler { - objectName: "stylus eraser" - acceptedDevices: PointerDevice.Stylus - acceptedPointerTypes: PointerDevice.Eraser - cursorShape: Qt.PointingHandCursor - } + HoverHandler { + objectName: "stylus" + acceptedDevices: PointerDevice.Stylus + acceptedPointerTypes: PointerDevice.Pen + cursorShape: Qt.CrossCursor + } - HoverHandler { - objectName: "airbrush" - acceptedDevices: PointerDevice.Airbrush - acceptedPointerTypes: PointerDevice.Pen - cursorShape: Qt.BusyCursor - } + HoverHandler { + objectName: "stylus eraser" + acceptedDevices: PointerDevice.Stylus + acceptedPointerTypes: PointerDevice.Eraser + cursorShape: Qt.PointingHandCursor + } - HoverHandler { - objectName: "airbrush eraser" - acceptedDevices: PointerDevice.Airbrush - acceptedPointerTypes: PointerDevice.Eraser - cursorShape: Qt.OpenHandCursor - } + HoverHandler { + objectName: "airbrush" + acceptedDevices: PointerDevice.Airbrush + acceptedPointerTypes: PointerDevice.Pen + cursorShape: Qt.BusyCursor + } - HoverHandler { - objectName: "mouse" - acceptedDevices: PointerDevice.Mouse - // acceptedPointerTypes can be omitted because Mouse is not ambiguous. - // When a genuine mouse move is sent, there's a conflict, and this one should win. - cursorShape: Qt.IBeamCursor - } + HoverHandler { + objectName: "airbrush eraser" + acceptedDevices: PointerDevice.Airbrush + acceptedPointerTypes: PointerDevice.Eraser + cursorShape: Qt.OpenHandCursor + } + + HoverHandler { + objectName: "mouse" + acceptedDevices: PointerDevice.Mouse + // acceptedPointerTypes can be omitted because Mouse is not ambiguous. + // When a genuine mouse move is sent, there's a conflict, and this one should win. + cursorShape: Qt.IBeamCursor + } - HoverHandler { - objectName: "conflictingMouse" - acceptedDevices: PointerDevice.Mouse - // acceptedPointerTypes can be omitted because Mouse is not ambiguous. - // When a genuine mouse move is sent, there's a conflict, and this one should lose. - cursorShape: Qt.ClosedHandCursor + HoverHandler { + objectName: "conflictingMouse" + acceptedDevices: PointerDevice.Mouse + // acceptedPointerTypes can be omitted because Mouse is not ambiguous. + // When a genuine mouse move is sent, there's a conflict, and this one should lose. + cursorShape: Qt.ClosedHandCursor + } } } diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverHandler.qml b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverHandler.qml new file mode 100644 index 0000000000..60dfc53c40 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverHandler.qml @@ -0,0 +1,17 @@ +import QtQuick + +Item { + width: 320 + height: 240 + + Rectangle { + width: 100 + height: 100 + anchors.centerIn: parent + color: hh.hovered ? "lightsteelblue" : "beige" + + HoverHandler { + id: hh + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/lesHoverables.qml b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/lesHoverables.qml index 43d9827ad7..ca30a7fe99 100644 --- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/lesHoverables.qml +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/lesHoverables.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.15 diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp index e488c0486f..0569fed472 100644 --- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp @@ -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 #include <QtTest/QtTest> @@ -48,9 +48,13 @@ private slots: void deviceCursor(); void addHandlerFromCpp(); void ensureHoverHandlerWorksWhenItemHasHoverDisabled(); + void changeCursor(); + void touchDrag(); private: void createView(QScopedPointer<QQuickView> &window, const char *fileName); + + QScopedPointer<QPointingDevice> touchscreen = QScopedPointer<QPointingDevice>(QTest::createTouchDevice()); }; void tst_HoverHandler::createView(QScopedPointer<QQuickView> &window, const char *fileName) @@ -567,6 +571,17 @@ void tst_HoverHandler::deviceCursor() QCOMPARE(eraserHandler->isHovered(), false); QCOMPARE(aibrushHandler->isHovered(), false); QCOMPARE(airbrushEraserHandler->isHovered(), true); // there was no fresh QTabletEvent to tell it not to be hovered + + // hover with the stylus again, then move the mouse outside the handlers' parent item + testStylusDevice(QInputDevice::DeviceType::Stylus, QPointingDevice::PointerType::Pen, + Qt::CrossCursor, stylusHandler); + QTest::mouseMove(&window, QPoint(180, 180)); + // the mouse has left the item: all its HoverHandlers should be unhovered (QTBUG-116505) + QCOMPARE(stylusHandler->isHovered(), false); + QCOMPARE(eraserHandler->isHovered(), false); + QCOMPARE(aibrushHandler->isHovered(), false); + QCOMPARE(airbrushEraserHandler->isHovered(), false); + QCOMPARE(mouseHandler->isHovered(), false); } void tst_HoverHandler::addHandlerFromCpp() @@ -671,6 +686,74 @@ void tst_HoverHandler::ensureHoverHandlerWorksWhenItemHasHoverDisabled() QCOMPARE(spy.size(), 2); } +void tst_HoverHandler::changeCursor() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "changingCursor.qml"); + QQuickView * window = windowPtr.data(); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + QQuickItem *item = window->findChild<QQuickItem *>("brownRect"); + QVERIFY(item); + QQuickHoverHandler *hh = item->findChild<QQuickHoverHandler *>(); + QVERIFY(hh); + + QPoint itemCenter(item->mapToScene(QPointF(item->width() / 2, item->height() / 2)).toPoint()); + QSignalSpy hoveredSpy(hh, SIGNAL(hoveredChanged())); + + QTest::mouseMove(window, itemCenter); + + QTRY_COMPARE(hoveredSpy.size(), 1); + +#if QT_CONFIG(cursor) + QTRY_COMPARE(window->cursor().shape(), Qt::CrossCursor); + QTRY_COMPARE(window->cursor().shape(), Qt::OpenHandCursor); + QTRY_COMPARE(window->cursor().shape(), Qt::CrossCursor); + QTRY_COMPARE(window->cursor().shape(), Qt::OpenHandCursor); +#endif +} + +void tst_HoverHandler::touchDrag() +{ + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("hoverHandler.qml"))); + const QQuickItem *root = window.rootObject(); + QQuickHoverHandler *handler = root->findChild<QQuickHoverHandler *>(); + QVERIFY(handler); + + // polishAndSync() calls flushFrameSynchronousEvents() before emitting afterAnimating() + QSignalSpy frameSyncSpy(&window, &QQuickWindow::afterAnimating); + + const QPoint out(root->width() - 1, root->height() / 2); + QPoint in(root->width() / 2, root->height() / 2); + + QTest::touchEvent(&window, touchscreen.get()).press(0, out, &window); + QQuickTouchUtils::flush(&window); + QCOMPARE(handler->isHovered(), false); + + frameSyncSpy.clear(); + QTest::touchEvent(&window, touchscreen.get()).move(0, in, &window); + QQuickTouchUtils::flush(&window); + QTRY_COMPARE(handler->isHovered(), true); + QCOMPARE(handler->point().scenePosition(), in); + + in += {10, 10}; + QTest::touchEvent(&window, touchscreen.get()).move(0, in, &window); + QQuickTouchUtils::flush(&window); + // ensure that the color change is visible + QTRY_COMPARE_GE(frameSyncSpy.size(), 1); + QCOMPARE(handler->isHovered(), true); + QCOMPARE(handler->point().scenePosition(), in); + + QTest::touchEvent(&window, touchscreen.get()).move(0, out, &window); + QQuickTouchUtils::flush(&window); + QTRY_COMPARE_GE(frameSyncSpy.size(), 2); + QCOMPARE(handler->isHovered(), false); + + QTest::touchEvent(&window, touchscreen.get()).release(0, out, &window); +} + QTEST_MAIN(tst_HoverHandler) #include "tst_qquickhoverhandler.moc" diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/CMakeLists.txt b/tests/auto/quick/pointerhandlers/qquickpinchhandler/CMakeLists.txt index f16dfa3421..1334607ab2 100644 --- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/CMakeLists.txt +++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qquickpinchhandler Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qquickpinchhandler 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/qquickpinchhandler/data/nullTarget.qml b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/nullTarget.qml index a348938aca..9d9903fc0e 100644 --- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/nullTarget.qml +++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/nullTarget.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 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 2.15 diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchAndDrag.qml b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchAndDrag.qml index 70c105836f..ddd63ec720 100644 --- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchAndDrag.qml +++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchAndDrag.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 diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchproperties.qml b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchproperties.qml index 37f22c949a..2b9b3eb156 100644 --- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchproperties.qml +++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchproperties.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 @@ -35,7 +35,14 @@ Rectangle { } } - Text { color: "magenta"; z: 1; text: "scale: " + blackRect.scale} + Text { + color: "magenta" + z: 1 + text: "scale: " + blackRect.scale + + "\npos: " + blackRect.x.toFixed(2) + ", " + blackRect.y.toFixed(2) + + "\ntranslation: active " + pincharea.activeTranslation.x.toFixed(2) + ", " + pincharea.activeTranslation.y.toFixed(2) + + "\n persistent " + pincharea.persistentTranslation.x.toFixed(2) + ", " + pincharea.persistentTranslation.y.toFixed(2) + } Rectangle { id: blackRect diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/threeFingers.qml b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/threeFingers.qml index dbebf92933..ed9220f99e 100644 --- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/threeFingers.qml +++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/threeFingers.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 diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/transformedPinchHandler.qml b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/transformedPinchHandler.qml index 56fa8c9f8e..f25d0e9f38 100644 --- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/transformedPinchHandler.qml +++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/transformedPinchHandler.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/qquickpinchhandler/tst_qquickpinchhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp index a61cbd76db..78483c5bdb 100644 --- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 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> #include <QtTest/QSignalSpy> @@ -34,6 +34,8 @@ private slots: void scaleThreeFingers(); void scaleNativeGesture_data(); void scaleNativeGesture(); + void cumulativeNativeGestures_data(); + void cumulativeNativeGestures(); void pan(); void dragAxesEnabled_data(); void dragAxesEnabled(); @@ -43,8 +45,8 @@ private slots: void transformedpinchHandler(); private: - QPointingDevice *touchscreen = QTest::createTouchDevice(); - QPointingDevice *touchpad = QTest::createTouchDevice(QInputDevice::DeviceType::TouchPad); + QScopedPointer<QPointingDevice> touchscreen = QScopedPointer<QPointingDevice>(QTest::createTouchDevice()); + QScopedPointer<QPointingDevice> touchpad = QScopedPointer<QPointingDevice>(QTest::createTouchDevice(QInputDevice::DeviceType::TouchPad)); }; void tst_QQuickPinchHandler::cleanupTestCase() @@ -230,13 +232,13 @@ void tst_QQuickPinchHandler::scale() QVERIFY(pinchHandler != nullptr); QQuickItem *blackRect = (hasTarget ? pinchHandler->target() : pinchHandler->parentItem()); QVERIFY(blackRect != nullptr); - QSignalSpy grabChangedSpy(pinchHandler, SIGNAL(grabChanged(QPointingDevice::GrabTransition, QEventPoint))); + QSignalSpy grabChangedSpy(pinchHandler, SIGNAL(grabChanged(QPointingDevice::GrabTransition,QEventPoint))); QSignalSpy scaleChangedSpy(pinchHandler, &QQuickPinchHandler::scaleChanged); if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); QPoint p0(80, 80); QPoint p1(100, 100); - QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(&window, touchscreen); + QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(&window, touchscreen.get()); pinchSequence.press(0, p0, &window).commit(); QQuickTouchUtils::flush(&window); // In order for the stationary point to remember its previous position, @@ -383,7 +385,7 @@ void tst_QQuickPinchHandler::scaleThreeFingers() QPoint p1(220, 80); QPoint p2(150, 220); { - QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen); + QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen.get()); pinchSequence.press(0, p0, window).commit(); QQuickTouchUtils::flush(window); // In order for the stationary point to remember its previous position, @@ -488,10 +490,10 @@ void tst_QQuickPinchHandler::scaleNativeGesture() // so as to compensate for the change in size, to hold the centroid in place const QPointF expectedPos = targetPos + QPointF( (pinchPos.x() - target->x()) * (expectedScale - 1), (pinchPos.y() - target->y()) * (expectedScale - 1) ); - QWindowSystemInterface::handleGestureEvent(window, ts++, touchpad, + QWindowSystemInterface::handleGestureEvent(window, ts++, touchpad.get(), Qt::BeginNativeGesture, pinchPos, pinchPos); if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); - QWindowSystemInterface::handleGestureEventWithRealValue(window, ts++, touchpad, + QWindowSystemInterface::handleGestureEventWithRealValue(window, ts++, touchpad.get(), Qt::ZoomNativeGesture, scale - 1, pinchPos, pinchPos); if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); QTRY_COMPARE(target->scale(), expectedScale); @@ -509,7 +511,7 @@ void tst_QQuickPinchHandler::scaleNativeGesture() QCOMPARE(pinchHandler->activeRotation(), 0); QCOMPARE(pinchHandler->rotationAxis()->persistentValue(), 0); QCOMPARE(pinchHandler->rotationAxis()->activeValue(), 0); - QWindowSystemInterface::handleGestureEvent(window, ts++, touchpad, + QWindowSystemInterface::handleGestureEvent(window, ts++, touchpad.get(), Qt::EndNativeGesture, pinchPos, pinchPos); QTRY_COMPARE(pinchHandler->active(), false); QCOMPARE(target->scale(), expectedScale); @@ -528,9 +530,9 @@ void tst_QQuickPinchHandler::scaleNativeGesture() const qreal reverseScale = (1 / expectedScale); pinchPos = QPointF(110, 110); pinchLocalPos = target->mapFromScene(pinchPos); - QWindowSystemInterface::handleGestureEvent(window, ts++, touchpad, + QWindowSystemInterface::handleGestureEvent(window, ts++, touchpad.get(), Qt::BeginNativeGesture, pinchPos, pinchPos); - QWindowSystemInterface::handleGestureEventWithRealValue(window, ts++, touchpad, + QWindowSystemInterface::handleGestureEventWithRealValue(window, ts++, touchpad.get(), Qt::ZoomNativeGesture, reverseScale - 1, pinchPos, pinchPos); QTRY_COMPARE(target->scale(), 1); QCOMPARE(pinchHandler->active(), true); @@ -541,7 +543,7 @@ void tst_QQuickPinchHandler::scaleNativeGesture() QCOMPARE(pinchHandler->persistentScale(), 1); QCOMPARE(pinchHandler->activeScale(), reverseScale); QCOMPARE(pinchHandler->scaleAxis()->activeValue(), reverseScale); - QWindowSystemInterface::handleGestureEvent(window, ts++, touchpad, + QWindowSystemInterface::handleGestureEvent(window, ts++, touchpad.get(), Qt::EndNativeGesture, pinchPos, pinchPos); QTRY_COMPARE(pinchHandler->active(), false); QCOMPARE(target->scale(), 1); @@ -550,6 +552,127 @@ void tst_QQuickPinchHandler::scaleNativeGesture() QCOMPARE(pinchHandler->scaleAxis()->activeValue(), 1); } +void tst_QQuickPinchHandler::cumulativeNativeGestures_data() +{ + QTest::addColumn<const QPointingDevice*>("device"); + QTest::addColumn<Qt::NativeGestureType>("gesture"); + QTest::addColumn<qreal>("value"); + QTest::addColumn<QList<QPoint>>("expectedTargetTranslations"); + + const auto *touchpadDevice = touchpad.get(); + const auto *mouse = QPointingDevice::primaryPointingDevice(); + + QTest::newRow("touchpad: rotate") << touchpadDevice << Qt::RotateNativeGesture << 5.0 + << QList<QPoint>{{-2, 2}, {-5, 4}, {-7, 6}, {-10, 7}}; + QTest::newRow("touchpad: scale") << touchpadDevice << Qt::ZoomNativeGesture << 0.1 + << QList<QPoint>{{3, 3}, {5, 5}, {8, 8}, {12, 12}}; + if (mouse->type() == QInputDevice::DeviceType::Mouse) { + QTest::newRow("mouse: rotate") << mouse << Qt::RotateNativeGesture << 5.0 + << QList<QPoint>{{-2, 2}, {-5, 4}, {-7, 6}, {-10, 7}}; + QTest::newRow("mouse: scale") << mouse << Qt::ZoomNativeGesture << 0.1 + << QList<QPoint>{{3, 3}, {5, 5}, {8, 8}, {12, 12}}; + } else { + qCWarning(lcPointerTests) << "skipping mouse tests: primary device is not a mouse" << mouse; + } +} + +void tst_QQuickPinchHandler::cumulativeNativeGestures() +{ + QFETCH(const QPointingDevice*, device); + QFETCH(Qt::NativeGestureType, gesture); + QFETCH(qreal, value); + QFETCH(QList<QPoint>, expectedTargetTranslations); + + QCOMPARE(expectedTargetTranslations.size(), 4); + + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("pinchproperties.qml"))); + QVERIFY(window.rootObject() != nullptr); + qApp->processEvents(); + + QQuickItem *root = qobject_cast<QQuickItem*>(window.rootObject()); + QVERIFY(root != nullptr); + QQuickPinchHandler *pinchHandler = root->findChild<QQuickPinchHandler*>("pinchHandler"); + QVERIFY(pinchHandler != nullptr); + QQuickItem *target = root->findChild<QQuickItem*>("blackrect"); + QVERIFY(target != nullptr); + QCOMPARE(pinchHandler->target(), target); + + ulong ts = 1; + qreal expectedScale = 1; + qreal expectedRotation = 0; + QPointF pinchPos(75, 75); + const QPointF initialTargetPos(target->position()); + QWindowSystemInterface::handleGestureEvent(&window, ts++, device, + Qt::BeginNativeGesture, pinchPos, pinchPos); + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); + for (int i = 1; i <= 4; ++i) { + QWindowSystemInterface::handleGestureEventWithRealValue(&window, ts++, device, + gesture, value, pinchPos, pinchPos); + qApp->processEvents(); + switch (gesture) { + case Qt::ZoomNativeGesture: + expectedScale = qBound(qreal(0.5), qPow(1 + value, i), qreal(4)); + break; + case Qt::RotateNativeGesture: + expectedRotation = qBound(qreal(0), value * i, qreal(90)); + break; + default: + break; // PinchHandler doesn't react to the others + } + + qCDebug(lcPointerTests) << i << gesture << "with value" << value + << ": scale" << target->scale() << "expected" << expectedScale + << ": rotation" << target->rotation() << "expected" << expectedRotation; + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); + QCOMPARE(target->scale(), expectedScale); + QCOMPARE(target->rotation(), expectedRotation); + QCOMPARE(pinchHandler->persistentScale(), expectedScale); + QCOMPARE(pinchHandler->activeScale(), expectedScale); + QCOMPARE(pinchHandler->scaleAxis()->persistentValue(), expectedScale); + QCOMPARE(pinchHandler->scaleAxis()->activeValue(), expectedScale); + QCOMPARE(pinchHandler->persistentRotation(), expectedRotation); + QCOMPARE(pinchHandler->activeRotation(), expectedRotation); + QCOMPARE(pinchHandler->rotationAxis()->persistentValue(), expectedRotation); + QCOMPARE(pinchHandler->rotationAxis()->activeValue(), expectedRotation); + // The target gets transformed around the gesture position, for which + // QQuickItemPrivate::adjustedPosForTransform() computes its new position to compensate. + QPointF delta = target->position() - initialTargetPos; + qCDebug(lcPointerTests) << "target moved by" << delta << "to" << target->position() + << "active trans" << pinchHandler->activeTranslation() + << "perst trans" << pinchHandler->persistentTranslation(); + QCOMPARE_NE(target->position(), initialTargetPos); + QCOMPARE(delta.toPoint(), expectedTargetTranslations.at(i - 1)); + // The native pinch gesture cannot include a translation component (and + // the cursor doesn't move while you are performing the gesture on a touchpad). + QCOMPARE(pinchHandler->activeTranslation(), QPointF()); + // The target only moves to compensate for scale and rotation changes, and that's + // not reflected in PinchHandler.persistentTranslation. + QCOMPARE(pinchHandler->persistentTranslation(), QPointF()); + } + QCOMPARE(pinchHandler->active(), true); + qCDebug(lcPointerTests) << "centroid: local" << pinchHandler->centroid().position() + << "scene" << pinchHandler->centroid().scenePosition(); + QCOMPARE(pinchHandler->persistentScale(), expectedScale); + QCOMPARE(pinchHandler->activeScale(), expectedScale); + QCOMPARE(pinchHandler->scaleAxis()->activeValue(), expectedScale); + QWindowSystemInterface::handleGestureEvent(&window, ts++, device, + Qt::EndNativeGesture, pinchPos, pinchPos); + QTRY_COMPARE(pinchHandler->active(), false); + QCOMPARE(target->scale(), expectedScale); + QCOMPARE(target->rotation(), expectedRotation); + QCOMPARE(pinchHandler->persistentScale(), expectedScale); + QCOMPARE(pinchHandler->activeScale(), 1); + QCOMPARE(pinchHandler->scaleAxis()->persistentValue(), expectedScale); + QCOMPARE(pinchHandler->scaleAxis()->activeValue(), 1); + QCOMPARE(pinchHandler->persistentRotation(), expectedRotation); + QCOMPARE(pinchHandler->activeRotation(), 0); + QCOMPARE(pinchHandler->rotationAxis()->persistentValue(), expectedRotation); + QCOMPARE(pinchHandler->rotationAxis()->activeValue(), 0); + QCOMPARE(pinchHandler->activeTranslation(), QPointF()); + QCOMPARE(pinchHandler->persistentTranslation(), QPointF()); +} + void tst_QQuickPinchHandler::pan() { QQuickView *window = QQuickViewTestUtils::createView(); @@ -575,7 +698,7 @@ void tst_QQuickPinchHandler::pan() QPoint p1(100, 100); { const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); - QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen); + QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen.get()); pinchSequence.press(0, p0, window).commit(); QQuickTouchUtils::flush(window); // In order for the stationary point to remember its previous position, @@ -649,7 +772,7 @@ void tst_QQuickPinchHandler::pan() // pan x beyond bound p0 += QPoint(100,100); p1 += QPoint(100,100); - QTest::touchEvent(window, touchscreen).move(0, p0, window).move(1, p1, window); + QTest::touchEvent(window, touchscreen.get()).move(0, p0, window).move(1, p1, window); QQuickTouchUtils::flush(window); QCOMPARE(blackRect->x(), 140.0); @@ -657,7 +780,7 @@ void tst_QQuickPinchHandler::pan() QCOMPARE(translationChangedSpy.size(), 5); QCOMPARE(translationChangedSpy.last().first().value<QVector2D>(), QVector2D(100, 100)); - QTest::touchEvent(window, touchscreen).release(0, p0, window).release(1, p1, window); + QTest::touchEvent(window, touchscreen.get()).release(0, p0, window).release(1, p1, window); QQuickTouchUtils::flush(window); QVERIFY(!root->property("pinchActive").toBool()); } @@ -697,7 +820,7 @@ void tst_QQuickPinchHandler::dragAxesEnabled() QPoint blackRectPos = blackRect->position().toPoint(); // press two points, one above the rectangle's center and one below - QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen); + QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen.get()); pinchSequence.press(0, p0, window).press(1, p1, window).commit(); QQuickTouchUtils::flush(window); @@ -734,7 +857,7 @@ void tst_QQuickPinchHandler::dragAxesEnabled() QCOMPARE(blackRect->position().toPoint().x(), xEnabled ? 140 : blackRectPos.x()); // because of xAxis.maximum QCOMPARE(blackRect->position().toPoint().y(), yEnabled ? 170 : blackRectPos.y()); // because of yAxis.maximum - QTest::touchEvent(window, touchscreen).release(0, p0, window).release(1, p1, window); + QTest::touchEvent(window, touchscreen.get()).release(0, p0, window).release(1, p1, window); QQuickTouchUtils::flush(window); } @@ -763,7 +886,7 @@ void tst_QQuickPinchHandler::retouch() QPoint p0(80, 80); QPoint p1(100, 100); { - QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen); + QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen.get()); pinchSequence.press(0, p0, window).commit(); QQuickTouchUtils::flush(window); // In order for the stationary point to remember its previous position, @@ -846,7 +969,7 @@ void tst_QQuickPinchHandler::cancel() QPoint p0(80, 80); QPoint p1(100, 100); { - QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen); + QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen.get()); pinchSequence.press(0, p0, window).commit(); QQuickTouchUtils::flush(window); // In order for the stationary point to remember its previous position, @@ -876,7 +999,7 @@ void tst_QQuickPinchHandler::cancel() QSKIP("cancel is not supported atm"); - QTouchEvent cancelEvent(QEvent::TouchCancel, touchscreen); + QTouchEvent cancelEvent(QEvent::TouchCancel, touchscreen.get()); QCoreApplication::sendEvent(window, &cancelEvent); QQuickTouchUtils::flush(window); @@ -932,7 +1055,7 @@ void tst_QQuickPinchHandler::transformedpinchHandler() const int threshold = qApp->styleHints()->startDragDistance(); { - QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(view, touchscreen); + QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(view, touchscreen.get()); // start pinchHandler pinchSequence.press(0, p0, view).commit(); QQuickTouchUtils::flush(view); diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/CMakeLists.txt b/tests/auto/quick/pointerhandlers/qquickpointerhandler/CMakeLists.txt index da110a6398..e15b802814 100644 --- a/tests/auto/quick/pointerhandlers/qquickpointerhandler/CMakeLists.txt +++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qquickpointerhandler Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qquickpointerhandler 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/qquickpointerhandler/data/clip.qml b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/clip.qml new file mode 100644 index 0000000000..7bc3907c8c --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/clip.qml @@ -0,0 +1,36 @@ +import QtQuick +import Qt.test 1.0 + +Item { + width: 200 + height: 200 + + Rectangle { + id: circle + y: 0 + width: 100 + height: width + radius: width/2 + color: "#3e1" + clip: true + + // Rectangle contains() is not affected by its 'radius' property + containmentMask: QtObject { + property alias radius: circle.radius + function contains(point: point) : bool { + return (Math.pow(point.x - radius, 2) + Math.pow(point.y - radius, 2)) < Math.pow(radius, 2) + } + } + EventHandler { + objectName: "circle eventHandler" + } + Rectangle { + width: circle.width/2 + height: width + color: "red" + EventHandler { + objectName: "eventHandler" + } + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/grabberSceneChange.qml b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/grabberSceneChange.qml new file mode 100644 index 0000000000..7bc6028bc2 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/grabberSceneChange.qml @@ -0,0 +1,78 @@ +import QtQuick + +Window { + id: root + visible: true + objectName: "root" + width: 320 + height: 480 + + property bool useTimer : false + property int grabChangedCounter : 0 + + Item { + id: back + anchors.fill: parent + + Rectangle { + id: background + anchors.fill: parent + color: "blue" + } + + Rectangle { + id: container + objectName: "container" + anchors.fill: parent + anchors.margins: 50 + z: 2 + + Rectangle { + id: likeButton + color: "gray" + anchors.centerIn: parent + width: 200 + height: 200 + + DragHandler { + id: handler + objectName: "dragHandler" + grabPermissions: PointerHandler.CanTakeOverFromItems + onGrabChanged: { + ++grabChangedCounter + } + } + } + } + + Timer { + id: reparentTimer + running: false + interval: 100 + repeat: false + onTriggered: { + container.parent = null + } + } + + Rectangle { + id: likeButton2 + color: "yellow" + anchors.centerIn: parent + width: 100 + height: 100 + z: 3 + + MultiPointTouchArea { + id: press + anchors.fill: parent + onPressed: { + if (useTimer) + reparentTimer.running = true + else + container.parent = null + } + } + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp index aa89ad0631..1120cb54c2 100644 --- a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp @@ -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 #include <QtTest/QtTest> @@ -162,6 +162,7 @@ public: class EventHandler : public QQuickPointerHandler { + Q_OBJECT public: EventHandler(QQuickItem *parent = nullptr) : QQuickPointerHandler(parent) {} @@ -231,6 +232,9 @@ private slots: void dynamicCreationInWindow(); void cppConstruction(); void reparenting(); + void grabberSceneChange_data(); + void grabberSceneChange(); + void clip(); protected: bool eventFilter(QObject *, QEvent *event) override @@ -749,6 +753,112 @@ void tst_PointerHandlers::reparenting() } } +/*! + Verify that removing an item that has a grabbing handler from the scene + does not result in crashes in our event dispatching code. The item's window() + pointer will be nullptr, so the handler must have released the grab, or never + gotten the grab, depending on when the item gets removed. + + See QTBUG-114475. +*/ +void tst_PointerHandlers::grabberSceneChange_data() +{ + QTest::addColumn<bool>("useTimer"); + QTest::addColumn<int>("grabChangedCount"); + + QTest::addRow("Immediately") << false << 0; + QTest::addRow("Delayed") << true << 2; +} + +void tst_PointerHandlers::grabberSceneChange() +{ + QFETCH(const bool, useTimer); + QFETCH(const int, grabChangedCount); + + QQmlEngine engine; + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("grabberSceneChange.qml")); + QQuickWindow *window = qobject_cast<QQuickWindow*>(component.create()); + QScopedPointer<QQuickWindow> cleanup(window); + QVERIFY(window); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + window->setProperty("useTimer", useTimer); + + QQuickItem *container = window->findChild<QQuickItem *>("container"); + + QPoint p1 = QPoint(window->width() / 2, window->height() / 2); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + // The container gets removed from this window, either immediately on + // press, or through a timer. + QTRY_COMPARE(container->parentItem(), nullptr); + + QEXPECT_FAIL("Delayed", + "PointerHandlers don't release their grab when item is removed", Continue); + QCOMPARE(window->property("grabChangedCounter").toInt(), grabChangedCount); + + // this should not crash + QTest::mouseMove(window, p1 + QPoint(5, 5)); +} + +void tst_PointerHandlers::clip() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "clip.qml"); + QQuickView * window = windowPtr.data(); + QVERIFY(window); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + EventHandler *handler = window->contentItem()->findChild<EventHandler*>("eventHandler"); + EventHandler *circleHandler = window->contentItem()->findChild<EventHandler*>("circle eventHandler"); + + QCOMPARE(handler->pressEventCount, 0); + QCOMPARE(circleHandler->pressEventCount, 0); + QCOMPARE(handler->releaseEventCount, 0); + QCOMPARE(circleHandler->releaseEventCount, 0); + + const QPoint rectPt = QPoint(1, 1); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, rectPt); + QCOMPARE(handler->pressEventCount, 1); + QCOMPARE(circleHandler->pressEventCount, 0); + + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, rectPt); + QCOMPARE(handler->releaseEventCount, 1); + QCOMPARE(circleHandler->releaseEventCount, 0); + + + handler->pressEventCount = 0; + circleHandler->pressEventCount = 0; + handler->releaseEventCount = 0; + circleHandler->releaseEventCount = 0; + + const QPoint rectAndCirclePt = QPoint(49 ,49); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, rectAndCirclePt); + QCOMPARE(handler->pressEventCount, 1); + QCOMPARE(circleHandler->pressEventCount, 1); + + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, rectAndCirclePt); + QCOMPARE(handler->releaseEventCount, 1); + QCOMPARE(circleHandler->releaseEventCount, 1); + + + handler->pressEventCount = 0; + circleHandler->pressEventCount = 0; + handler->releaseEventCount = 0; + circleHandler->releaseEventCount = 0; + + const QPoint circlePt = QPoint(51 ,51); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, circlePt); + QCOMPARE(handler->pressEventCount, 0); + QCOMPARE(circleHandler->pressEventCount, 1); + + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, circlePt); + QCOMPARE(handler->releaseEventCount, 0); + QCOMPARE(circleHandler->releaseEventCount, 1); +} + QTEST_MAIN(tst_PointerHandlers) #include "tst_qquickpointerhandler.moc" diff --git a/tests/auto/quick/pointerhandlers/qquickpointhandler/CMakeLists.txt b/tests/auto/quick/pointerhandlers/qquickpointhandler/CMakeLists.txt index 969df8a4ce..aa73218361 100644 --- a/tests/auto/quick/pointerhandlers/qquickpointhandler/CMakeLists.txt +++ b/tests/auto/quick/pointerhandlers/qquickpointhandler/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qquickpointhandler Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qquickpointhandler 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/qquickpointhandler/data/multiPointTracker.qml b/tests/auto/quick/pointerhandlers/qquickpointhandler/data/multiPointTracker.qml index d813160f44..616d526592 100644 --- a/tests/auto/quick/pointerhandlers/qquickpointhandler/data/multiPointTracker.qml +++ b/tests/auto/quick/pointerhandlers/qquickpointhandler/data/multiPointTracker.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/qquickpointhandler/data/pointTracker.qml b/tests/auto/quick/pointerhandlers/qquickpointhandler/data/pointTracker.qml index 54d7ff7212..2de03703e8 100644 --- a/tests/auto/quick/pointerhandlers/qquickpointhandler/data/pointTracker.qml +++ b/tests/auto/quick/pointerhandlers/qquickpointhandler/data/pointTracker.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/qquickpointhandler/tst_qquickpointhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpointhandler/tst_qquickpointhandler.cpp index 4435580d92..e1641c282e 100644 --- a/tests/auto/quick/pointerhandlers/qquickpointhandler/tst_qquickpointhandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickpointhandler/tst_qquickpointhandler.cpp @@ -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 #include <QtTest/QtTest> @@ -18,6 +18,8 @@ #include <QtQuickTestUtils/private/viewtestutils_p.h> #include <QtQuickTestUtils/private/visualtestutils_p.h> +#include <QtCore/qpointer.h> + Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") class tst_PointHandler : public QQmlDataTest 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" diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/CMakeLists.txt b/tests/auto/quick/pointerhandlers/qquickwheelhandler/CMakeLists.txt index b5cfe1fa5a..d50181dfe4 100644 --- a/tests/auto/quick/pointerhandlers/qquickwheelhandler/CMakeLists.txt +++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qquickwheelhandler Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qquickwheelhandler 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/qquickwheelhandler/data/nested.qml b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml index d50f6ac005..9c43df46da 100644 --- a/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml +++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 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.14 diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml index c0c5e776e9..ca6053ebcf 100644 --- a/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml +++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 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.14 @@ -14,7 +14,23 @@ Rectangle { } } + Rectangle { + color: "red" + width: 6; height: 6; radius: 3 + x: wheelHandler.point.position.x - radius + y: wheelHandler.point.position.y - radius + } + + Text { + anchors.centerIn: parent + anchors.verticalCenterOffset: 20 + color: "white" + font.pixelSize: 18 + text: parent.x.toFixed(2) + ", " + parent.y.toFixed(2) + } + WheelHandler { + id: wheelHandler activeTimeout: 0.5 } } diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp b/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp index d87acc3200..c88b1af2ea 100644 --- a/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 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> #include <QtTest/QSignalSpy> |