diff options
author | Shawn Rutledge <shawn.rutledge@qt.io> | 2021-05-12 23:22:11 +0200 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2021-06-01 19:56:15 +0200 |
commit | 43c8d1b6f17540d2e5c82603662158251ce49944 (patch) | |
tree | 7d4fd0f5bc54f9ef173c04e0cb8077ef4a6330d0 | |
parent | 5bb3b4ee3ae448b103df9320a6bd316759cb5517 (diff) |
Don't let PointerHandler steal mouse grab from keepMouseGrab layer
As explained in the comment, the handler can override the keepMouseGrab
"veto" if the item is a parent (like a Flickable) that filters events,
but not in other cases. The logic was wrong though, apparently.
Amends 090f404cf80da35734f712b02cc1543acecd5b62
Fixes: QTBUG-78258
Task-number: QTBUG-79163
Change-Id: I9a473ab3b23743f863cb0be13767fdbc29cd5e1c
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
(cherry picked from commit b09ce7dcd8ecf24ef23da8197a64e3fced3fc894)
3 files changed, 105 insertions, 8 deletions
diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index 92788fa0d7..b33b95eec1 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -351,10 +351,14 @@ bool QQuickPointerHandler::approveGrabTransition(QPointerEvent *event, const QEv existingPhGrabber->metaObject()->className() == metaObject()->className()) allowed = true; } else if ((d->grabPermissions & CanTakeOverFromItems)) { + allowed = true; QQuickItem * existingItemGrabber = qobject_cast<QQuickItem *>(event->exclusiveGrabber(point)); - if (existingItemGrabber && !((existingItemGrabber->keepMouseGrab() && QQuickWindowPrivate::isMouseEvent(event)) || - (existingItemGrabber->keepTouchGrab() && QQuickWindowPrivate::isTouchEvent(event)))) { - allowed = true; + auto da = QQuickItemPrivate::get(parentItem())->deliveryAgentPrivate(); + if (existingItemGrabber && + ((existingItemGrabber->keepMouseGrab() && + (QQuickWindowPrivate::isMouseEvent(event) || da->isDeliveringTouchAsMouse())) || + (existingItemGrabber->keepTouchGrab() && QQuickWindowPrivate::isTouchEvent(event)))) { + allowed = false; // If the handler wants to steal the exclusive grab from an Item, the Item can usually veto // by having its keepMouseGrab flag set. But an exception is if that Item is a parent that // normally filters events (such as a Flickable): it needs to be possible for e.g. a @@ -363,15 +367,20 @@ bool QQuickPointerHandler::approveGrabTransition(QPointerEvent *event, const QEv // at first and then expects to be able to steal the grab later on. It cannot respect // Flickable's wishes in that case, because then it would never have a chance. if (existingItemGrabber->keepMouseGrab() && - !(existingItemGrabber->filtersChildMouseEvents() && existingItemGrabber->isAncestorOf(parentItem()))) { - auto da = QQuickItemPrivate::get(parentItem())->deliveryAgentPrivate(); + existingItemGrabber->filtersChildMouseEvents() && existingItemGrabber->isAncestorOf(parentItem())) { Q_ASSERT(da); if (da->isDeliveringTouchAsMouse() && point.id() == da->touchMouseId) { - qCDebug(lcPointerHandlerGrab) << this << "wants to grab touchpoint" << point.id() - << "but declines to steal grab from touch-mouse grabber with keepMouseGrab=true" << existingItemGrabber; - allowed = false; + qCDebug(lcPointerHandlerGrab) << this << "steals touchpoint" << point.id() + << "despite parent touch-mouse grabber with keepMouseGrab=true" << existingItemGrabber; + allowed = true; } } + if (!allowed) { + qCDebug(lcPointerHandlerGrab) << this << "wants to grab point" << point.id() + << "but declines to steal from grabber" << existingItemGrabber + << "with keepMouseGrab=" << existingItemGrabber->keepMouseGrab() + << "keepTouchGrab=" << existingItemGrabber->keepTouchGrab(); + } } } } diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragHandlerUnderModalLayer.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragHandlerUnderModalLayer.qml new file mode 100644 index 0000000000..b24812c914 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragHandlerUnderModalLayer.qml @@ -0,0 +1,34 @@ +import QtQuick 2.15 + +import Test 1.0 + +Item { + width: 640 + height: 480 + + Rectangle { + anchors.fill: parent + color: "grey" + + Rectangle { + x: 200 + y: 200 + width: 100 + height: 100 + color: "orange" + DragHandler { + grabPermissions: DragHandler.CanTakeOverFromAnything // but not anything with keepMouseGrab! + } + } + } + + ModalLayer { + anchors.fill: parent + + Rectangle { + anchors.fill: parent + color: "red" + opacity: 0.4 + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp index 4c9f015679..2cb26124ae 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp @@ -70,6 +70,7 @@ private slots: void touchPassiveGrabbers(); void touchPinchAndMouseMove(); void unsuitableEventDuringDrag(); + void underModalLayer(); private: void sendWheelEvent(QQuickView &window, QPoint pos, QPoint angleDelta, QPoint pixelDelta, Qt::KeyboardModifiers modifiers, Qt::ScrollPhase phase, bool inverted); @@ -894,6 +895,59 @@ void tst_DragHandler::sendWheelEvent(QQuickView &window, QPoint pos, QPoint angl QQuickTouchUtils::flush(&window); } +class ModalLayer : public QQuickItem { +public: + explicit ModalLayer(QQuickItem* parent = nullptr) : QQuickItem(parent) { + this->setAcceptedMouseButtons(Qt::AllButtons); + this->setAcceptTouchEvents(true); + this->setKeepMouseGrab(true); + this->setKeepTouchGrab(true); + } + + bool event(QEvent* event) override { + switch (event->type()) { + case QEvent::KeyPress: + case QEvent::MouseMove: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseTrackingChange: + case QEvent::MouseButtonDblClick: + case QEvent::Wheel: + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchCancel: + case QEvent::TouchEnd: { + qCDebug(lcPointerTests) << "BLOCK!" << event->type(); + return true; + } + default: break; + } + return QQuickItem::event(event); + } +}; + +void tst_DragHandler::underModalLayer() // QTBUG-78258 +{ + qmlRegisterType<ModalLayer>("Test", 1, 0, "ModalLayer"); + + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "dragHandlerUnderModalLayer.qml"); + QQuickView * window = windowPtr.data(); + QPointer<QQuickDragHandler> dragHandler = window->rootObject()->findChild<QQuickDragHandler*>(); + QVERIFY(dragHandler); + + QPoint p1(250, 250); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + p1 += QPoint(dragThreshold, dragThreshold); + QTest::mouseMove(window, p1); + QVERIFY(!dragHandler->active()); + p1 += QPoint(dragThreshold, dragThreshold); + QTest::mouseMove(window, p1); + QVERIFY(!dragHandler->active()); + QTest::mouseRelease(window, Qt::LeftButton); +} + QTEST_MAIN(tst_DragHandler) #include "tst_qquickdraghandler.moc" |