aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOliver Dawes <olliedawes@gmail.com>2022-12-21 15:48:07 +0000
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2023-01-18 05:07:27 +0000
commit041717ae6a0e79fcf139e392c80f26cf5b52dfcd (patch)
tree20b1d974f4ab54d61f22ec6b8f75fb93962fddbc
parentf11d719caa0af1824e11d56857f710d913302ffd (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.cpp42
-rw-r--r--tests/auto/quick/qquickmousearea/data/containsMouseMasked.qml24
-rw-r--r--tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp49
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()
{