diff options
4 files changed, 188 insertions, 17 deletions
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 65d69a3bcf..45c14f10a1 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1669,6 +1669,36 @@ QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *t return me; } +void QQuickWindowPrivate::deliverToPassiveGrabbers(const QVector<QPointer <QQuickPointerHandler> > &passiveGrabbers, + QQuickPointerEvent *pointerEvent) +{ + const QVector<QQuickPointerHandler *> &eventDeliveryTargets = pointerEvent->device()->eventDeliveryTargets(); + QVarLengthArray<QPair<QQuickItem *, bool>, 4> sendFilteredPointerEventResult; + for (auto handler : passiveGrabbers) { + // a null pointer in passiveGrabbers is unlikely, unless the grabbing handler was deleted dynamically + if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) { + bool alreadyFiltered = false; + QQuickItem *par = handler->parentItem(); + + // see if we already have sent a filter event to the parent + auto it = std::find_if(sendFilteredPointerEventResult.begin(), sendFilteredPointerEventResult.end(), + [par](const QPair<QQuickItem *, bool> &pair) { return pair.first == par; }); + if (it != sendFilteredPointerEventResult.end()) { + // Yes, the event was already filtered to that parent, do not call it again but use + // the result of the previous call to determine if we should call the handler. + alreadyFiltered = it->second; + } else { + alreadyFiltered = sendFilteredPointerEvent(pointerEvent, par); + sendFilteredPointerEventResult << qMakePair<QQuickItem*, bool>(par, alreadyFiltered); + } + if (!alreadyFiltered) + handler->handlePointerEvent(pointerEvent); + } + } +} + + + void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent) { auto point = pointerEvent->point(0); @@ -1717,14 +1747,8 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven // if this is an update or release from an actual mouse, // and the point wasn't grabbed, deliver only to PointerHandlers: // passive grabbers first, then the rest - const QVector<QQuickPointerHandler *> &eventDeliveryTargets = pointerEvent->device()->eventDeliveryTargets(); - for (auto handler : point->passiveGrabbers()) { - // a null pointer in passiveGrabbers is unlikely, unless the grabbing handler was deleted dynamically - if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) { - if (!sendFilteredPointerEvent(pointerEvent, handler->parentItem())) - handler->handlePointerEvent(pointerEvent); - } - } + deliverToPassiveGrabbers(point->passiveGrabbers(), pointerEvent); + // If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order if (!pointerEvent->allPointsGrabbed() && pointerEvent->buttons()) { QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point->scenePosition(), false, false); @@ -2398,16 +2422,9 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve int pointCount = event->pointCount(); // Deliver to each eventpoint's passive grabbers (but don't visit any handler more than once) - const QVector<QQuickPointerHandler *> &eventDeliveryTargets = event->device()->eventDeliveryTargets(); for (int i = 0; i < pointCount; ++i) { QQuickEventPoint *point = event->point(i); - for (auto handler : point->passiveGrabbers()) { - if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) { - if (sendFilteredPointerEvent(event, handler->parentItem())) - return; - handler->handlePointerEvent(event); - } - } + deliverToPassiveGrabbers(point->passiveGrabbers(), event); } // If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index ae49d5e304..2d7b81cec8 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -146,6 +146,7 @@ public: void grabTouchPoints(QObject *grabber, const QVector<int> &ids); void removeGrabber(QQuickItem *grabber, bool mouse = true, bool touch = true); static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0); + void deliverToPassiveGrabbers(const QVector<QPointer <QQuickPointerHandler> > &passiveGrabbers, QQuickPointerEvent *pointerEvent); void deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent); bool sendFilteredMouseEvent(QEvent *event, QQuickItem *receiver, QQuickItem *filteringParent); bool sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent = nullptr); diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/flickableWithHandlers.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/flickableWithHandlers.qml index 833fef0a81..651f290570 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/data/flickableWithHandlers.qml +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/flickableWithHandlers.qml @@ -43,7 +43,7 @@ import Qt.labs.handlers 1.0 Rectangle { id: root - width: 400 + width: 500 height: 480 objectName: "root" color: "#222222" @@ -81,6 +81,82 @@ Rectangle { gesturePolicy: TapHandler.ReleaseWithinBounds // the default } } + Column { + spacing: 6 + Rectangle { + width: 50 + height: 50 + color: "aqua" + border.color: drag1.active ? "darkgreen" : "transparent" + border.width: 3 + objectName: "drag" + DragHandler { + id: drag1 + } + Text { + anchors.centerIn: parent + enabled: false + text: "drag" + } + } + Rectangle { + width: 50 + height: 50 + color: "aqua" + objectName: "tap" + border.color: tap1.isPressed ? "red" : "transparent" + border.width: 3 + TapHandler { + id: tap1 + gesturePolicy: TapHandler.DragThreshold + } + Text { + anchors.centerIn: parent + enabled: false + text: "tap" + } + } + Rectangle { + width: 50 + height: 50 + color: "aqua" + border.color: tap2.isPressed ? "red" : drag2.active ? "darkgreen" : "transparent" + border.width: 3 + objectName: "dragAndTap" + DragHandler { + id: drag2 + } + TapHandler { + id: tap2 + gesturePolicy: TapHandler.DragThreshold + } + Text { + anchors.centerIn: parent + enabled: false + text: "drag\nand\ntap" + } + } + Rectangle { + width: 50 + height: 50 + color: "aqua" + border.color: tap3.isPressed ? "red" : drag3.active ? "darkgreen" : "transparent" + border.width: 3 + objectName: "tapAndDrag" + TapHandler { + id: tap3 + gesturePolicy: TapHandler.DragThreshold + } + DragHandler { + id: drag3 + } + Text { + anchors.centerIn: parent + enabled: false + text: "tap\nand\ndrag" + } + } + } } } } diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp b/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp index c8fe6052fb..7862d72db8 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp +++ b/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp @@ -71,6 +71,10 @@ private slots: void touchDragFlickableBehindSlider(); void mouseDragSlider(); void mouseDragFlickableBehindSlider(); + void touchDragFlickableBehindItemWithHandlers_data(); + void touchDragFlickableBehindItemWithHandlers(); + void mouseDragFlickableBehindItemWithHandlers_data(); + void mouseDragFlickableBehindItemWithHandlers(); private: void createView(QScopedPointer<QQuickView> &window, const char *fileName); @@ -469,6 +473,79 @@ void tst_FlickableInterop::mouseDragFlickableBehindSlider() QCOMPARE(translationChangedSpy.count(), 0); } +void tst_FlickableInterop::touchDragFlickableBehindItemWithHandlers_data() +{ + QTest::addColumn<QByteArray>("nameOfRectangleToDrag"); + QTest::addColumn<bool>("expectedFlickableMoving"); + QTest::newRow("drag") << QByteArray("drag") << false; + QTest::newRow("tap") << QByteArray("tap") << true; + QTest::newRow("dragAndTap") << QByteArray("dragAndTap") << false; + QTest::newRow("tapAndDrag") << QByteArray("tapAndDrag") << false; +} + +void tst_FlickableInterop::touchDragFlickableBehindItemWithHandlers() +{ + QFETCH(bool, expectedFlickableMoving); + QFETCH(QByteArray, nameOfRectangleToDrag); + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "flickableWithHandlers.qml"); + QQuickView * window = windowPtr.data(); + QQuickItem *rect = window->rootObject()->findChild<QQuickItem*>(nameOfRectangleToDrag); + QVERIFY(rect); + QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>(); + QVERIFY(flickable); + QPoint p1 = rect->mapToScene(rect->clipRect().center()).toPoint(); + QPoint originP1 = p1; + + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + for (int i = 0; i < dragThreshold * 3; ++i) { + p1 = originP1; + p1.rx() += i; + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + } + QCOMPARE(flickable->isMoving(), expectedFlickableMoving); + if (!expectedFlickableMoving) { + QVERIFY(rect->mapToScene(rect->clipRect().center()).toPoint().x() > originP1.x()); + } + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); +} + +void tst_FlickableInterop::mouseDragFlickableBehindItemWithHandlers_data() +{ + touchDragFlickableBehindItemWithHandlers_data(); +} + +void tst_FlickableInterop::mouseDragFlickableBehindItemWithHandlers() +{ + QFETCH(bool, expectedFlickableMoving); + QFETCH(QByteArray, nameOfRectangleToDrag); + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "flickableWithHandlers.qml"); + QQuickView * window = windowPtr.data(); + QQuickItem *rect = window->rootObject()->findChild<QQuickItem*>(nameOfRectangleToDrag); + QVERIFY(rect); + QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>(); + QVERIFY(flickable); + QPoint p1 = rect->mapToScene(rect->clipRect().center()).toPoint(); + QPoint originP1 = p1; + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + for (int i = 0; i < 3; ++i) { + p1 += QPoint(dragThreshold, 0); + QTest::mouseMove(window, p1); + QQuickTouchUtils::flush(window); + } + QCOMPARE(flickable->isMoving(), expectedFlickableMoving); + if (!expectedFlickableMoving) { + QCOMPARE(originP1 + QPoint(3*dragThreshold, 0), p1); + } + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); +} + QTEST_MAIN(tst_FlickableInterop) #include "tst_flickableinterop.moc" |