diff options
author | Shawn Rutledge <shawn.rutledge@qt.io> | 2022-12-16 07:56:15 +0100 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2022-12-19 10:01:36 +0100 |
commit | 0568b3c4d456e109a33a1e24be70fc056e2e032d (patch) | |
tree | b988bd41c65a390994a7d8d8aed4f7753563303c | |
parent | da7cb43b7ce3761cd167983c427f046e25b7da3d (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>
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" |