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 730cdc5043
)
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
This commit is contained in:
parent
da7cb43b7c
commit
0568b3c4d4
|
@ -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()
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue