aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@qt.io>2022-12-16 07:56:15 +0100
committerShawn Rutledge <shawn.rutledge@qt.io>2022-12-19 10:01:36 +0100
commit0568b3c4d456e109a33a1e24be70fc056e2e032d (patch)
treeb988bd41c65a390994a7d8d8aed4f7753563303c
parentda7cb43b7ce3761cd167983c427f046e25b7da3d (diff)
Drawer: don't get stuck in open state after touches outside
If the Drawer does not cover the whole window, it's possible to tap outside the dimmer overlay. In that case, next time you tap on the dimmer, you hit this early return in handleRelease in which pressPoint.isNull(), so it was not setting touchId back to -1 and doing the other cleanup/reset tasks. This is a touch release, and touchId should always be eventually reset after a touch release, regardless of the code path. Now QQuickDrawerPrivate::handleRelease() has a QScopeGuard to ensure that cleanup is done regardless of early returns. That's still not enough; but when we receive TouchEnd in QPopupPriv::handleTouchEvent(), it means that _all_ touchpoints are released. So as a fallback for multi-touch cases, we now ensure that all releases are handled and stored state is reset, regardless of whether an individual point ID matches the stored touchId. Fixes: QTBUG-103811 Change-Id: Ia637bae00d8edb7f7f4c8fb4337b4c3d72fe4e60 Reviewed-by: Doris Verria <doris.verria@qt.io> (cherry picked from commit 730cdc5043a8823dfcdce7d3b2035875f8f987c1) Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
-rw-r--r--src/quicktemplates2/qquickdrawer.cpp16
-rw-r--r--src/quicktemplates2/qquickpopup.cpp2
-rw-r--r--tests/auto/quickcontrols2/qquickdrawer/data/itemPartialOverlayModal.qml13
-rw-r--r--tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp41
4 files changed, 62 insertions, 10 deletions
diff --git a/src/quicktemplates2/qquickdrawer.cpp b/src/quicktemplates2/qquickdrawer.cpp
index c212998f0d..7d1ef96c0b 100644
--- a/src/quicktemplates2/qquickdrawer.cpp
+++ b/src/quicktemplates2/qquickdrawer.cpp
@@ -488,6 +488,12 @@ bool QQuickDrawerPrivate::handleMove(QQuickItem *item, const QPointF &point, ulo
bool QQuickDrawerPrivate::handleRelease(QQuickItem *item, const QPointF &point, ulong timestamp)
{
+ auto cleanup = qScopeGuard([this] {
+ popupItem->setKeepMouseGrab(false);
+ popupItem->setKeepTouchGrab(false);
+ pressPoint = QPointF();
+ touchId = -1;
+ });
if (pressPoint.isNull())
return false;
if (!popupItem->keepMouseGrab() && !popupItem->keepTouchGrab()) {
@@ -547,14 +553,8 @@ bool QQuickDrawerPrivate::handleRelease(QQuickItem *item, const QPointF &point,
}
}
- bool wasGrabbed = popupItem->keepMouseGrab() || popupItem->keepTouchGrab();
- popupItem->setKeepMouseGrab(false);
- popupItem->setKeepTouchGrab(false);
-
- pressPoint = QPointF();
- touchId = -1;
-
- return wasGrabbed;
+ // the cleanup() lambda will run before return
+ return popupItem->keepMouseGrab() || popupItem->keepTouchGrab();
}
void QQuickDrawerPrivate::handleUngrab()
diff --git a/src/quicktemplates2/qquickpopup.cpp b/src/quicktemplates2/qquickpopup.cpp
index b86c164c71..e79985d4e3 100644
--- a/src/quicktemplates2/qquickpopup.cpp
+++ b/src/quicktemplates2/qquickpopup.cpp
@@ -445,7 +445,7 @@ bool QQuickPopupPrivate::handleTouchEvent(QQuickItem *item, QTouchEvent *event)
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
for (const QTouchEvent::TouchPoint &point : event->points()) {
- if (!acceptTouch(point))
+ if (event->type() != QEvent::TouchEnd && !acceptTouch(point))
return blockInput(item, point.position());
switch (point.state()) {
diff --git a/tests/auto/quickcontrols2/qquickdrawer/data/itemPartialOverlayModal.qml b/tests/auto/quickcontrols2/qquickdrawer/data/itemPartialOverlayModal.qml
new file mode 100644
index 0000000000..23aa4ca3c2
--- /dev/null
+++ b/tests/auto/quickcontrols2/qquickdrawer/data/itemPartialOverlayModal.qml
@@ -0,0 +1,13 @@
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+
+Item {
+ width: 400; height: 400
+
+ Drawer {
+ edge: Qt.LeftEdge
+ height: 200
+ width: 200
+ modal: true
+ }
+}
diff --git a/tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp b/tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp
index 6b6713bdde..2c6d50a591 100644
--- a/tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp
+++ b/tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp
@@ -46,14 +46,16 @@
#include <QtGui/private/qguiapplication_p.h>
#include <QtQuick/private/qquickwindow_p.h>
#include <QtQuick/private/qquickflickable_p.h>
+#include <QtQuick/qquickview.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/visualtestutils_p.h>
#include <QtQuickTemplates2/private/qquickapplicationwindow_p.h>
-#include <QtQuickTemplates2/private/qquickoverlay_p.h>
+#include <QtQuickTemplates2/private/qquickoverlay_p_p.h>
#include <QtQuickTemplates2/private/qquickpopup_p_p.h>
#include <QtQuickTemplates2/private/qquickdrawer_p.h>
#include <QtQuickTemplates2/private/qquickbutton_p.h>
#include <QtQuickTemplates2/private/qquickslider_p.h>
+#include <QtQuickTestUtils/private/viewtestutils_p.h>
#include <QtQuickControlsTestUtils/private/controlstestutils_p.h>
#include <QtQuickControlsTestUtils/private/qtest_quickcontrols_p.h>
@@ -121,6 +123,8 @@ private slots:
void topEdgeScreenEdge();
+ void touchOutsideOverlay();
+
private:
QScopedPointer<QPointingDevice> touchDevice;
};
@@ -1393,6 +1397,41 @@ void tst_QQuickDrawer::topEdgeScreenEdge()
QTRY_COMPARE(drawer->position(), 1.0);
}
+void tst_QQuickDrawer::touchOutsideOverlay() // QTBUG-103811
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("itemPartialOverlayModal.qml")));
+ auto *drawer = window.rootObject()->findChild<QQuickDrawer*>();
+ QVERIFY(drawer);
+ QSignalSpy openedSpy(drawer, &QQuickDrawer::opened);
+ QSignalSpy closedSpy(drawer, &QQuickDrawer::closed);
+
+ drawer->open();
+ QVERIFY(openedSpy.size() == 1 || openedSpy.wait());
+ QVERIFY(drawer->isOpened());
+
+ // tap-dance in bottom area beyond the overlay
+ QPoint p1(100, 250);
+ QPoint p2(300, 250);
+ QTest::touchEvent(&window, touchDevice.data()).press(1, p1);
+ p1 -= QPoint(1, 0);
+ QTest::touchEvent(&window, touchDevice.data()).move(1, p1).press(2, p2);
+ p2 -= QPoint(1, 0);
+ QTest::touchEvent(&window, touchDevice.data()).release(1, p1).move(2, p2);
+ QTest::touchEvent(&window, touchDevice.data()).press(1, p1).stationary(2);
+ QTest::touchEvent(&window, touchDevice.data()).release(1, p1).release(2, p2);
+ QQuickTouchUtils::flush(&window);
+
+ // tap the overlay to try to close the drawer
+ QVERIFY(drawer->closePolicy().testFlag(QQuickPopup::CloseOnReleaseOutside));
+ const QPoint p3(300, 100);
+ QTest::touchEvent(&window, touchDevice.data()).press(3, p3);
+ QTest::touchEvent(&window, touchDevice.data()).release(3, p3);
+ QQuickTouchUtils::flush(&window);
+ QVERIFY(closedSpy.size() == 1 || closedSpy.wait());
+ QCOMPARE(drawer->isOpened(), false);
+}
+
QTEST_QUICKCONTROLS_MAIN(tst_QQuickDrawer)
#include "tst_qquickdrawer.moc"