diff options
author | Oliver Dawes <olliedawes@gmail.com> | 2022-12-21 15:48:07 +0000 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2023-01-18 05:07:27 +0000 |
commit | 041717ae6a0e79fcf139e392c80f26cf5b52dfcd (patch) | |
tree | 20b1d974f4ab54d61f22ec6b8f75fb93962fddbc | |
parent | f11d719caa0af1824e11d56857f710d913302ffd (diff) |
Fix masked MouseArea hovered state on visibility change
This fixes the issue where given two hoverable MouseAreas A & B where B
masks A, the `containsMouse` property of A will be incorrectly set to
true if the cursor is positioned above both A and B and the visibility
of A is toggled from true to false back to true.
This patch fixes the above issue by checking if the QQuickMouseArea is
marked as a hovered item in its windows QQuickDeliveryAgentPrivate(QDAP)
instance. If the QQuickMouseArea is masked by another QQuickMouseArea
then it will not be a in the QDAPs hovered items list and we skip
setting the hovered property on QQuickMouseArea, fixing the issue.
This patch also adds a test case to prevent future regressions.
Fixes: QTBUG-109567
Change-Id: I5f024a097b56ef5e0836ca9f8ae547983a089b44
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
(cherry picked from commit 000df08653be46420298acae8e16f831056a85b7)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r-- | src/quick/items/qquickmousearea.cpp | 42 | ||||
-rw-r--r-- | tests/auto/quick/qquickmousearea/data/containsMouseMasked.qml | 24 | ||||
-rw-r--r-- | tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp | 49 |
3 files changed, 109 insertions, 6 deletions
diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp index 4e361d833f..a99976473f 100644 --- a/src/quick/items/qquickmousearea.cpp +++ b/src/quick/items/qquickmousearea.cpp @@ -1039,13 +1039,43 @@ void QQuickMouseArea::itemChange(ItemChange change, const ItemChangeData &value) Q_D(QQuickMouseArea); switch (change) { case ItemVisibleHasChanged: - if (d->effectiveEnable && d->enabled && hoverEnabled() && d->hovered != (isVisible() && isUnderMouse())) { - if (!d->hovered) { - QPointF cursorPos = QGuiApplicationPrivate::lastCursorPosition; - d->lastScenePos = d->window->mapFromGlobal(cursorPos.toPoint()); - d->lastPos = mapFromScene(d->lastScenePos); + if (d->effectiveEnable && d->enabled && hoverEnabled() + && d->hovered != (isVisible() && isUnderMouse())) { + if (d->hovered) { + // If hovered but no longer under the mouse then un-hover. + setHovered(false); + } else { + // If under the mouse but not hovered then hover the QQuickMouseArea if it is + // marked as a hovered item under the windows QQuickDeliveryAgentPrivate instance. + // This is required as this QQuickMouseArea may be masked by another hoverable + // QQuickMouseArea higher up in the scenes z-index ordering. + QPointF globalPos{ QGuiApplicationPrivate::lastCursorPosition.toPoint() }; + QPointF scenePos{ d->window->mapFromGlobal(globalPos) }; + + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(d->window); + QQuickDeliveryAgentPrivate *dap = wd->deliveryAgentPrivate(); + + // If the QQuickDeliveryAgentPrivate has not already found a hovered leaf + // item then attempt to find one. + if (!dap->hoveredLeafItemFound) { + dap->deliverHoverEvent(scenePos, scenePos, Qt::NoModifier, + QDateTime::currentSecsSinceEpoch()); + } + + // Now if the QQuickDeliveryAgentPrivate has found a hovered leaf item check + // that this QQuickMouseArea item was one of the hovered items. + if (dap->hoveredLeafItemFound) { + for (auto hoverItem : dap->hoverItems) { + if (hoverItem.first == this) { + // Found a match so update the hover state. + d->lastScenePos = scenePos; + d->lastPos = mapFromScene(d->lastScenePos); + setHovered(true); + break; + } + } + } } - setHovered(!d->hovered); } if (d->pressed && (!isVisible())) { // This happens when the mouse area sets itself disabled or hidden diff --git a/tests/auto/quick/qquickmousearea/data/containsMouseMasked.qml b/tests/auto/quick/qquickmousearea/data/containsMouseMasked.qml new file mode 100644 index 0000000000..35cfd4b7ef --- /dev/null +++ b/tests/auto/quick/qquickmousearea/data/containsMouseMasked.qml @@ -0,0 +1,24 @@ +import QtQuick + +Rectangle { + width: 200 + height: 200 + visible: true + MouseArea { + id: mouseArea1 + objectName: "mouseArea1" + anchors.fill: parent + hoverEnabled: true + visible: true + } + + MouseArea { + id: mouseArea2 + objectName: "mouseArea2" + anchors.centerIn: mouseArea1 + width: 50 + height: 50 + hoverEnabled: true + visible: true + } +} diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 755dad6312..345b42487e 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -136,6 +136,7 @@ private slots: void settingHiddenInPressUngrabs(); void negativeZStackingOrder(); void containsMouseAndVisibility(); + void containsMouseAndVisibilityMasked(); void doubleClickToHide(); void releaseFirstTouchAfterSecond(); #if QT_CONFIG(tabletevent) @@ -2404,6 +2405,54 @@ void tst_QQuickMouseArea::containsMouseAndVisibility() QVERIFY(!mouseArea->hovered()); } +// QTBUG-109567 +void tst_QQuickMouseArea::containsMouseAndVisibilityMasked() +{ + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("containsMouseMasked.qml"))); + + QQuickMouseArea *mouseArea1 = window.rootObject()->findChild<QQuickMouseArea *>("mouseArea1"); + QVERIFY(mouseArea1 != nullptr); + QVERIFY(mouseArea1->isVisible()); + + QQuickMouseArea *mouseArea2 = window.rootObject()->findChild<QQuickMouseArea *>("mouseArea2"); + QVERIFY(mouseArea2 != nullptr); + QVERIFY(mouseArea2->isVisible()); + + QTest::mouseMove(&window, QPoint(window.width() / 2, window.height() / 2)); + + // Check that mouseArea" (i.e. the masking MouseArea) is the only hovered MouseArea. + QTRY_VERIFY(!mouseArea1->hovered()); + QTRY_VERIFY(mouseArea2->hovered()); + + // Toggle the visibility of the masked MouseArea (mouseArea1). + mouseArea1->setVisible(false); + QVERIFY(!mouseArea1->isVisible()); + + mouseArea1->setVisible(true); + QVERIFY(mouseArea1->isVisible()); + + // Check that the masked MouseArea is not now hovered depite being under the mouse after + // changing the visibility to visible. mouseArea2 should be the only hovered MouseArea still. + QTRY_VERIFY(!mouseArea1->hovered()); + QTRY_VERIFY(mouseArea2->hovered()); + + QTest::mouseMove(&window, QPoint(10, 10)); + + QTRY_VERIFY(mouseArea1->hovered()); + QTRY_VERIFY(!mouseArea2->hovered()); + + // Toggle the visibility of the masked MouseArea (mouseArea1). + mouseArea1->setVisible(false); + QVERIFY(!mouseArea1->isVisible()); + + mouseArea1->setVisible(true); + QVERIFY(mouseArea1->isVisible()); + + QTRY_VERIFY(mouseArea1->hovered()); + QTRY_VERIFY(!mouseArea2->hovered()); +} + // QTBUG-35995 and QTBUG-102158 void tst_QQuickMouseArea::doubleClickToHide() { |