diff options
author | Richard Moe Gustavsen <richard.gustavsen@qt.io> | 2022-03-16 13:14:38 +0100 |
---|---|---|
committer | Richard Moe Gustavsen <richard.gustavsen@qt.io> | 2022-04-03 07:11:50 +0000 |
commit | 21b0c3ed9f4a94bdd843c3be1bb95e7d96246f6c (patch) | |
tree | 1fe7ad56eb4e263ecbe56f8b0cc4c554ec7e030c | |
parent | 55ac0e04f5a8fe403a393a70c0dc53f8a5998bee (diff) |
QQuickDeliverAgent: don't propagate hover to siblings
From testing Qt 6.1, hover events should not propagate
between siblings. As soon as a leaf item is found that
receives a hover events, the event should only propagate
up the parent chain starting from the leaf item.
Change-Id: I7448f5322f529addb2260b0ee2b02d2cfadb55e1
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
(cherry picked from commit 499828b855d125ac236917f6ed01d8f1e7d88505)
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
5 files changed, 109 insertions, 12 deletions
diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp index 36f1128a39..20faa23a09 100644 --- a/src/quick/util/qquickdeliveryagent.cpp +++ b/src/quick/util/qquickdeliveryagent.cpp @@ -990,6 +990,7 @@ bool QQuickDeliveryAgentPrivate::deliverHoverEvent( // list at the end of this function and look for items with an old hoverId, // remove them from the list, and update their state accordingly. currentHoverId++; + hoveredLeafItemFound = false; const bool itemsWasHovered = !hoverItems.isEmpty(); deliverHoverEventRecursive(rootItem, scenePos, lastScenePos, modifiers, timestamp); @@ -1037,6 +1038,11 @@ bool QQuickDeliveryAgentPrivate::deliverHoverEvent( end up as the only one hovered. Any other HoverHandler that may be a child of an item that is stacked underneath, will not. Note that since siblings can overlap, there can be more than one leaf item under the mouse. + + For legacy reasons (Qt 6.1), as soon as we find a leaf item that has hover + enabled, and therefore receives the event, we stop recursing into the remaining + siblings (even if the event was ignored). This means that we only allow hover + events to propagate up the direct parent-child hierarchy, and not to siblings. */ bool QQuickDeliveryAgentPrivate::deliverHoverEventRecursive( QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos, @@ -1066,6 +1072,10 @@ bool QQuickDeliveryAgentPrivate::deliverHoverEventRecursive( // Stop propagation / recursion return true; } + if (hoveredLeafItemFound) { + // Don't propagate to siblings, only to ancestors + break; + } } // All decendants have been visited. @@ -1098,6 +1108,9 @@ bool QQuickDeliveryAgentPrivate::deliverHoverEventToItem( qCDebug(lcHoverTrace) << "item:" << item << "scene pos:" << scenePos << "localPos:" << localPos << "wasHovering:" << wasHovering << "isHovering:" << isHovering; + if (isHovering) + hoveredLeafItemFound = true; + // Send enter/move/leave event to the item bool accepted = false; if (isHovering && !clearHover) { diff --git a/src/quick/util/qquickdeliveryagent_p_p.h b/src/quick/util/qquickdeliveryagent_p_p.h index 3e551beb27..b962864103 100644 --- a/src/quick/util/qquickdeliveryagent_p_p.h +++ b/src/quick/util/qquickdeliveryagent_p_p.h @@ -120,6 +120,7 @@ public: bool allowChildEventFiltering = true; bool allowDoubleClick = true; bool frameSynchronousHoverEnabled = true; + bool hoveredLeafItemFound = false; bool isSubsceneAgent = false; static bool subsceneAgentsExist; diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/BLACKLIST b/tests/auto/quick/pointerhandlers/qquickhoverhandler/BLACKLIST index 62a6c67407..c815c0baf9 100644 --- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/BLACKLIST +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/BLACKLIST @@ -1,10 +1,2 @@ [movingItemWithHoverHandler] macos # Can't move cursor (QTBUG-76312) - -# QTBUG-98492 -[mouseAreaAndUnderlyingHoverHandler] -macos - -# QTBUG-98492 -[hoverHandlerAndUnderlyingMouseArea] -macos diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp index b26a055e60..d4ce666c4d 100644 --- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp @@ -272,8 +272,8 @@ void tst_HoverHandler::hoverHandlerAndUnderlyingMouseArea() #endif QTest::mouseMove(window, buttonCenter); - QCOMPARE(bottomSidebarMA->hovered(), true); - QCOMPARE(sidebarHoveredSpy.count(), 1); + QCOMPARE(bottomSidebarMA->hovered(), false); + QCOMPARE(sidebarHoveredSpy.count(), 2); QCOMPARE(buttonHH->isHovered(), true); QCOMPARE(buttonHoveredSpy.count(), 1); #if QT_CONFIG(cursor) @@ -282,7 +282,7 @@ void tst_HoverHandler::hoverHandlerAndUnderlyingMouseArea() QTest::mouseMove(window, rightOfButton); QCOMPARE(bottomSidebarMA->hovered(), true); - QCOMPARE(sidebarHoveredSpy.count(), 1); + QCOMPARE(sidebarHoveredSpy.count(), 3); QCOMPARE(buttonHH->isHovered(), false); QCOMPARE(buttonHoveredSpy.count(), 2); #if QT_CONFIG(cursor) @@ -291,7 +291,7 @@ void tst_HoverHandler::hoverHandlerAndUnderlyingMouseArea() QTest::mouseMove(window, outOfSidebar); QCOMPARE(bottomSidebarMA->hovered(), false); - QCOMPARE(sidebarHoveredSpy.count(), 2); + QCOMPARE(sidebarHoveredSpy.count(), 4); QCOMPARE(buttonHH->isHovered(), false); QCOMPARE(buttonHoveredSpy.count(), 2); #if QT_CONFIG(cursor) diff --git a/tests/auto/quick/qquickdeliveryagent/tst_qquickdeliveryagent.cpp b/tests/auto/quick/qquickdeliveryagent/tst_qquickdeliveryagent.cpp index 2de8f403d1..ac00cfede7 100644 --- a/tests/auto/quick/qquickdeliveryagent/tst_qquickdeliveryagent.cpp +++ b/tests/auto/quick/qquickdeliveryagent/tst_qquickdeliveryagent.cpp @@ -62,6 +62,26 @@ struct ViewportTransformHelper : public QQuickDeliveryAgent::Transform } }; +struct HoverItem : public QQuickItem +{ + HoverItem(QQuickItem *parent) : QQuickItem(parent){} + void hoverEnterEvent(QHoverEvent *e) override + { + hoverEnter = true; + e->setAccepted(block); + } + + void hoverLeaveEvent(QHoverEvent *e) override + { + hoverLeave = true; + e->setAccepted(block); + } + + bool hoverEnter = false; + bool hoverLeave = false; + bool block = false; +}; + // A QQuick3DViewport simulator class SubsceneRootItem : public QQuickShaderEffectSource { @@ -135,6 +155,9 @@ private slots: void passiveGrabberOrder(); void tapHandlerDoesntOverrideSubsceneGrabber(); void touchCompression(); + void hoverPropagation_nested_data(); + void hoverPropagation_nested(); + void hoverPropagation_siblings(); private: QScopedPointer<QPointingDevice> touchDevice = QScopedPointer<QPointingDevice>(QTest::createTouchDevice()); @@ -278,6 +301,74 @@ void tst_qquickdeliveryagent::touchCompression() QCOMPARE(agentPriv->compressedTouchCount, 0); } +void tst_qquickdeliveryagent::hoverPropagation_nested_data() +{ + QTest::addColumn<bool>("block"); + QTest::newRow("block=false") << false; + QTest::newRow("block=true") << true; +} + +void tst_qquickdeliveryagent::hoverPropagation_nested() +{ + QFETCH(bool, block); + + QQuickWindow window; + window.resize(200, 200); + window.show(); + QVERIFY(QTest::qWaitForWindowActive(&window)); + + HoverItem child(window.contentItem()); + child.setAcceptHoverEvents(true); + child.setWidth(100); + child.setHeight(100); + + HoverItem grandChild(&child); + grandChild.setAcceptHoverEvents(true); + grandChild.block = block; + grandChild.setWidth(100); + grandChild.setHeight(100); + + // Start by moving the mouse to the window + QTest::mouseMove(&window, QPoint(150, 150)); + QCOMPARE(child.hoverEnter, false); + QCOMPARE(grandChild.hoverEnter, false); + + // Move the mouse inside the items. If block is true, only + // the grandchild should be hovered. Otherwise both. + QTest::mouseMove(&window, QPoint(50, 50)); + QCOMPARE(child.hoverEnter, !block); + QCOMPARE(grandChild.hoverEnter, true); +} + +void tst_qquickdeliveryagent::hoverPropagation_siblings() +{ + QQuickWindow window; + window.resize(200, 200); + window.show(); + QVERIFY(QTest::qWaitForWindowActive(&window)); + + HoverItem sibling1(window.contentItem()); + sibling1.setAcceptHoverEvents(true); + sibling1.setWidth(100); + sibling1.setHeight(100); + + HoverItem sibling2(window.contentItem()); + sibling2.setAcceptHoverEvents(true); + sibling2.setWidth(100); + sibling2.setHeight(100); + + // Start by moving the mouse to the window + QTest::mouseMove(&window, QPoint(150, 150)); + QCOMPARE(sibling1.hoverEnter, false); + QCOMPARE(sibling2.hoverEnter, false); + + // Move the mouse inside the items. Only the + // sibling on the top should receive hover + QTest::mouseMove(&window, QPoint(50, 50)); + QCOMPARE(sibling1.hoverEnter, false); + QCOMPARE(sibling2.hoverEnter, true); +} + QTEST_MAIN(tst_qquickdeliveryagent) #include "tst_qquickdeliveryagent.moc" |