MouseArea: don't get stuck in pressed state when disabling in press

In 8ace780b5a we fixed the case when
MouseArea hides itself on press. This time, the concern is when it
becomes _effectively_ disabled by disabling its parent on press.
(As explained in a comment on QTBUG-38364, the MouseArea.enabled state
is independent, but we can nevertheless also find out when the effective
enabled state changes.)

[ChangeLog][QtQuick][MouseArea] When a MouseArea becomes effectively
disabled due to a parent's enabled property being set false while the
mouse is pressed, the press will now be canceled.

Fixes: QTBUG-39806
Fixes: QTBUG-103788
Change-Id: I8d69b535b6e1b5d51ad1e030f52217de9ab541f1
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
This commit is contained in:
Shawn Rutledge 2022-10-18 13:28:52 +02:00
parent 51ddad5ffd
commit 61a9612f5b
3 changed files with 70 additions and 1 deletions

View File

@ -1038,6 +1038,12 @@ void QQuickMouseArea::itemChange(ItemChange change, const ItemChangeData &value)
{
Q_D(QQuickMouseArea);
switch (change) {
case ItemEnabledHasChanged:
// If MouseArea becomes effectively disabled by disabling a parent
// (for example, onPressed: parent.enabled = false), cancel the pressed state.
if (d->pressed && !d->effectiveEnable)
ungrabMouse();
break;
case ItemVisibleHasChanged:
if (d->effectiveEnable && d->enabled && hoverEnabled() && d->hovered != (isVisible() && isUnderMouse())) {
if (!d->hovered) {
@ -1048,7 +1054,7 @@ void QQuickMouseArea::itemChange(ItemChange change, const ItemChangeData &value)
setHovered(!d->hovered);
}
if (d->pressed && (!isVisible())) {
// This happens when the mouse area sets itself disabled or hidden
// This happens when the mouse area hides itself
// inside the press handler. In that case we should not keep the internal
// state as pressed, since we never became the mouse grabber.
ungrabMouse();

View File

@ -0,0 +1,14 @@
import QtQuick
Rectangle {
id: whiteRect
width: 200
height: 200
color: ma.pressed ? "lightsteelblue" : "white"
MouseArea {
id: ma
objectName: "mousearea"
anchors.fill: parent
onPressed: parent.enabled = false
}
}

View File

@ -110,6 +110,8 @@ private slots:
void subtreeHoverEnabled();
void hoverWhenDisabled();
void disableAfterPress();
void disableParentOnPress_data();
void disableParentOnPress();
void onWheel();
void transformedMouseArea_data();
void transformedMouseArea();
@ -1532,6 +1534,53 @@ void tst_QQuickMouseArea::disableAfterPress()
QCOMPARE(mouseReleaseSpy.size(), 0);
}
void tst_QQuickMouseArea::disableParentOnPress_data()
{
QTest::addColumn<const QPointingDevice *>("device");
QTest::newRow("core pointer") << QPointingDevice::primaryPointingDevice();
QTest::newRow("touch") << device;
}
void tst_QQuickMouseArea::disableParentOnPress() // QTBUG-39806 and QTBUG-103788
{
QFETCH(const QPointingDevice *, device);
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("disableParentOnPress.qml")));
QQuickItem *root = window.rootObject();
QVERIFY(root);
QQuickMouseArea *mouseArea = root->findChild<QQuickMouseArea*>();
QVERIFY(mouseArea);
QSignalSpy pressedChangedSpy(mouseArea, &QQuickMouseArea::pressedChanged);
QSignalSpy canceledSpy(mouseArea, &QQuickMouseArea::canceled);
QSignalSpy enabledSpy(mouseArea, &QQuickMouseArea::enabledChanged);
QSignalSpy parentEnabledSpy(root, &QQuickItem::enabledChanged);
const QPoint p(100, 100);
QQuickTest::pointerPress(device, &window, 0, p);
QTRY_COMPARE(parentEnabledSpy.size(), 1);
QCOMPARE(root->isEnabled(), false);
QCOMPARE(mouseArea->isEnabled(), true); // enabled is independent, unfortunately (inverse of QTBUG-38364)
QCOMPARE(QQuickItemPrivate::get(mouseArea)->effectiveEnable, false);
// bug fix: it knows it got effectively disabled, so now it's no longer pressed
QCOMPARE(mouseArea->pressed(), false);
QCOMPARE(canceledSpy.size(), 1); // ...because the press was canceled
QCOMPARE(pressedChangedSpy.size(), 2); // kerchunk
QQuickTest::pointerRelease(device, &window, 0, p);
// now re-enable it and try again
root->setEnabled(true);
QQuickTest::pointerPress(device, &window, 0, p);
QTRY_COMPARE(root->isEnabled(), false);
QCOMPARE(QQuickItemPrivate::get(mouseArea)->effectiveEnable, false);
QCOMPARE(mouseArea->pressed(), false);
QCOMPARE(canceledSpy.size(), 2);
QCOMPARE(pressedChangedSpy.size(), 4);
QQuickTest::pointerRelease(device, &window, 0, p);
}
void tst_QQuickMouseArea::onWheel()
{
QQuickView window;