From 9f85654f7d4613ad184ffba950e53a82e61e9cc9 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 4 Jan 2024 00:07:05 -0700 Subject: [PATCH] QQuickWidget: accept touchpoint even if it has a passive grab In the test case from QTBUG-113558, a QQuickWidget's only event-handling object is a TapHandler with its default gesturePolicy, so that on touch press, it gets pressed; but it was missing the touch release and getting stuck in pressed state. As explained in dc8f44b14501ecd4acc196f5138aeff3f7502d0a, widgets don't care about (exclusive or passive) grabbers, and rely on points being accepted to make the widget that received the TouchBegin an implicit grabber. If the only thing that happened in the Qt Quick scene in the QQuickWidget is that the touchpoint got a passive grab, it's still a kind of interaction, and we want to ensure that the TapHandler will see the release too, to avoid getting stuck. (This means that passive grabs are not passive from the perspective of widget event delivery: the TapHandler ends up excluding delivery of the touchpoint to other widgets, even though it didn't mean to. But hopefully nobody expects cooperation with touch-handling widgets underneath the Quick scene.) Fixes: QTBUG-113558 Pick-to: 6.6 6.5 Change-Id: Ided6247b43a2405dbfdf9d195bb45ceae4cf58fd Reviewed-by: Volker Hilsheimer (cherry picked from commit 59b0b59090e2ff3f04bd15120720ee6a5b0c3f4b) Reviewed-by: Qt Cherry-pick Bot --- src/quickwidgets/qquickwidget.cpp | 2 +- .../qquickwidget/data/tapHandler.qml | 11 +++++ .../qquickwidget/tst_qquickwidget.cpp | 41 +++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 tests/auto/quickwidgets/qquickwidget/data/tapHandler.qml diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index 5b3e4394ce..c2c0f6f8d3 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -1665,7 +1665,7 @@ bool QQuickWidget::event(QEvent *e) QPointerEvent *pointerEvent = static_cast(e); auto deliveredPoints = pointerEvent->points(); for (auto &point : deliveredPoints) { - if (pointerEvent->exclusiveGrabber(point)) + if (pointerEvent->exclusiveGrabber(point) || !pointerEvent->passiveGrabbers(point).isEmpty()) point.setAccepted(true); } } diff --git a/tests/auto/quickwidgets/qquickwidget/data/tapHandler.qml b/tests/auto/quickwidgets/qquickwidget/data/tapHandler.qml new file mode 100644 index 0000000000..fe3d1925e5 --- /dev/null +++ b/tests/auto/quickwidgets/qquickwidget/data/tapHandler.qml @@ -0,0 +1,11 @@ +import QtQuick + +Rectangle { + width: 100 + height: 100 + color: th.pressed ? "steelblue" : "beige" + + TapHandler { + id: th + } +} diff --git a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp index 0c88c71582..755f2dd64c 100644 --- a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp +++ b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -128,6 +129,8 @@ private slots: void synthMouseFromTouch(); void touchTapMouseArea(); void touchTapButton(); + void touchTapHandler_data(); + void touchTapHandler(); void touchMultipleWidgets(); void tabKey(); void resizeOverlay(); @@ -682,6 +685,44 @@ void tst_qquickwidget::touchTapButton() QTRY_VERIFY(rootItem->property("wasClicked").toBool()); } +void tst_qquickwidget::touchTapHandler_data() +{ + QTest::addColumn("guiSynthMouse"); // AA_SynthesizeMouseForUnhandledTouchEvents + QTest::addColumn("gesturePolicy"); + + // QTest::newRow("nosynth: passive grab") << false << QQuickTapHandler::DragThreshold; // still failing + QTest::newRow("nosynth: exclusive grab") << false << QQuickTapHandler::ReleaseWithinBounds; + QTest::newRow("allowsynth: passive grab") << true << QQuickTapHandler::DragThreshold; // QTBUG-113558 + QTest::newRow("allowsynth: exclusive grab") << true << QQuickTapHandler::ReleaseWithinBounds; +} + +void tst_qquickwidget::touchTapHandler() +{ + QFETCH(bool, guiSynthMouse); + QFETCH(QQuickTapHandler::GesturePolicy, gesturePolicy); + + QCoreApplication::setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, guiSynthMouse); + QQuickWidget quick; + QVERIFY(quick.testAttribute(Qt::WA_AcceptTouchEvents)); + quick.setSource(testFileUrl("tapHandler.qml")); + quick.show(); + QVERIFY(QTest::qWaitForWindowExposed(&quick)); + + QQuickItem *rootItem = quick.rootObject(); + QVERIFY(rootItem); + QQuickTapHandler *th = rootItem->findChild(); + QVERIFY(th); + th->setGesturePolicy(gesturePolicy); + QSignalSpy tappedSpy(th, &QQuickTapHandler::tapped); + + const QPoint p(50, 50); + QTest::touchEvent(&quick, device).press(0, p, &quick); + QTRY_COMPARE(th->isPressed(), true); + QTest::touchEvent(&quick, device).release(0, p, &quick); + QTRY_COMPARE(tappedSpy.size(), 1); + QCOMPARE(th->isPressed(), false); +} + void tst_qquickwidget::touchMultipleWidgets() { QWidget window;