From ff4c2c311f1957ebacb66f0d35406a51da2a544e Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 27 Dec 2023 21:08:40 -0700 Subject: [PATCH] Ensure that HoverHandler reacts if a touchpoint moves out of bounds We rely on QQuickDeliveryAgentPrivate::flushFrameSynchronousEvents() mostly, but it doesn't get invoked without a window update request. So there is a special case when a touchpoint moves _out_ of an item that has a HoverHandler but is not reacting to touch in other ways: we just need to send another artificial hover event for each touchpoint to each hovered item to inform handlers about the new hover position. Fixes: QTBUG-120346 Pick-to: 6.2 6.5 6.6 6.7 Change-Id: I479362a2663943eb495fe0be418009165c7134bd Reviewed-by: Qt CI Bot Reviewed-by: Santhosh Kumar --- src/quick/util/qquickdeliveryagent.cpp | 10 +++++ .../qquickhoverhandler/data/hoverHandler.qml | 17 ++++++++ .../tst_qquickhoverhandler.cpp | 43 +++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverHandler.qml diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp index de110b2660..e2bb83e638 100644 --- a/src/quick/util/qquickdeliveryagent.cpp +++ b/src/quick/util/qquickdeliveryagent.cpp @@ -2092,6 +2092,16 @@ void QQuickDeliveryAgentPrivate::deliverUpdatedPoints(QPointerEvent *event) } if (!relevantPassiveGrabbers.isEmpty()) deliverToPassiveGrabbers(relevantPassiveGrabbers, event); + + // Ensure that HoverHandlers are updated, in case no items got dirty so far and there's no update request + if (event->type() == QEvent::TouchUpdate) { + for (auto hoverItem : hoverItems) { + if (auto item = hoverItem.first) { + deliverHoverEventToItem(item, point.scenePosition(), point.sceneLastPosition(), + event->modifiers(), event->timestamp(), false); + } + } + } } if (done) 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/tst_qquickhoverhandler.cpp b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp index 85f1cbda57..ace99daa6a 100644 --- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp @@ -49,9 +49,12 @@ private slots: void addHandlerFromCpp(); void ensureHoverHandlerWorksWhenItemHasHoverDisabled(); void changeCursor(); + void touchDrag(); private: void createView(QScopedPointer &window, const char *fileName); + + QScopedPointer touchscreen = QScopedPointer(QTest::createTouchDevice()); }; void tst_HoverHandler::createView(QScopedPointer &window, const char *fileName) @@ -700,6 +703,46 @@ void tst_HoverHandler::changeCursor() #endif } +void tst_HoverHandler::touchDrag() +{ + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("hoverHandler.qml"))); + const QQuickItem *root = window.rootObject(); + QQuickHoverHandler *handler = root->findChild(); + 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"