diff options
5 files changed, 111 insertions, 96 deletions
diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp index 84c4e912d1..4db96d8a99 100644 --- a/src/quick/handlers/qquickpinchhandler.cpp +++ b/src/quick/handlers/qquickpinchhandler.cpp @@ -349,13 +349,18 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event) } } else { bool containsReleasedPoints = event->isReleaseEvent(); - if (!active() && !containsReleasedPoints) { + if (!active()) { // Verify that at least one of the points has moved beyond threshold needed to activate the handler for (QQuickEventPoint *point : qAsConst(m_currentPoints)) { - if (QQuickWindowPrivate::dragOverThreshold(point)) { - if (grabPoints(m_currentPoints)) - setActive(true); + if (!containsReleasedPoints && QQuickWindowPrivate::dragOverThreshold(point) && grabPoints(m_currentPoints)) { + setActive(true); break; + } else { + setPassiveGrab(point); + } + if (point->state() == QQuickEventPoint::Pressed) { + point->setAccepted(false); // don't stop propagation + setPassiveGrab(point); } } if (!active()) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 73a594b281..4bee6dcd9a 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -768,16 +768,12 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) pt->cancelExclusiveGrab(); } point->setGrabberItem(grabber); - for (auto handler : point->passiveGrabbers()) - point->cancelPassiveGrab(handler); } } else { QQuickPointerEvent *event = pointerEventInstance(QQuickPointerDevice::genericMouseDevice()); Q_ASSERT(event->pointCount() == 1); auto point = event->point(0); point->setGrabberItem(grabber); - for (auto handler : point->passiveGrabbers()) - point->cancelPassiveGrab(handler); } @@ -1745,6 +1741,7 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven if (mouseIsReleased) point->setGrabberPointerHandler(nullptr, true); } + deliverToPassiveGrabbers(point->passiveGrabbers(), pointerEvent); } else { bool delivered = false; if (pointerEvent->isPressEvent()) { @@ -2410,6 +2407,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) // Deliver touch points to existing grabbers void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event) { + bool done = false; const auto grabbers = event->exclusiveGrabbers(); for (auto grabber : grabbers) { // The grabber is guaranteed to be either an item or a handler. @@ -2419,52 +2417,52 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve QQuickPointerHandler *handler = static_cast<QQuickPointerHandler *>(grabber); receiver = static_cast<QQuickPointerHandler *>(grabber)->parentItem(); if (sendFilteredPointerEvent(event, receiver)) - return; + done = true; event->localize(receiver); handler->handlePointerEvent(event); if (event->allPointsAccepted()) - return; + done = true; } + if (done) + break; // If the grabber is an item or the grabbing handler didn't handle it, // then deliver the event to the item (which may have multiple handlers). deliverMatchingPointsToItem(receiver, event); } - // If some points weren't grabbed, deliver only to non-grabber PointerHandlers - if (!event->allPointsGrabbed()) { - int pointCount = event->pointCount(); + // Deliver to each eventpoint's passive grabbers (but don't visit any handler more than once) + int pointCount = event->pointCount(); + for (int i = 0; i < pointCount; ++i) { + QQuickEventPoint *point = event->point(i); + deliverToPassiveGrabbers(point->passiveGrabbers(), event); + } + + if (done) + return; - // Deliver to each eventpoint's passive grabbers (but don't visit any handler more than once) + // If some points weren't grabbed, deliver only to non-grabber PointerHandlers in reverse paint order + if (!event->allPointsGrabbed()) { + QVector<QQuickItem *> targetItems; for (int i = 0; i < pointCount; ++i) { QQuickEventPoint *point = event->point(i); - deliverToPassiveGrabbers(point->passiveGrabbers(), event); - } - - // If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order - if (!event->allPointsGrabbed()) { - QVector<QQuickItem *> targetItems; - for (int i = 0; i < pointCount; ++i) { - QQuickEventPoint *point = event->point(i); - if (point->state() == QQuickEventPoint::Pressed) - continue; // presses were delivered earlier; not the responsibility of deliverUpdatedTouchPoints - QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point->scenePosition(), false, false); - if (targetItems.count()) { - targetItems = mergePointerTargets(targetItems, targetItemsForPoint); - } else { - targetItems = targetItemsForPoint; - } - } - - for (QQuickItem *item: targetItems) { - if (grabbers.contains(item)) - continue; - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - event->localize(item); - itemPrivate->handlePointerEvent(event, true); // avoid re-delivering to grabbers - if (event->allPointsGrabbed()) - break; + if (point->state() == QQuickEventPoint::Pressed) + continue; // presses were delivered earlier; not the responsibility of deliverUpdatedTouchPoints + QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point->scenePosition(), false, false); + if (targetItems.count()) { + targetItems = mergePointerTargets(targetItems, targetItemsForPoint); + } else { + targetItems = targetItemsForPoint; } } + for (QQuickItem *item : targetItems) { + if (grabbers.contains(item)) + continue; + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + event->localize(item); + itemPrivate->handlePointerEvent(event, true); // avoid re-delivering to grabbers + if (event->allPointsGrabbed()) + break; + } } } @@ -2503,7 +2501,7 @@ bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event, continue; deliverMatchingPointsToItem(item, event, handlersOnly); if (event->allPointsAccepted()) - break; + handlersOnly = true; } return event->allPointsAccepted(); @@ -2518,8 +2516,9 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo // Let the Item's handlers (if any) have the event first. // However, double click should never be delivered to handlers. if (!pointerEvent->isDoubleClickEvent()) { + bool wasAccepted = pointerEvent->allPointsAccepted(); itemPrivate->handlePointerEvent(pointerEvent); - allowDoubleClick = !(pointerEvent->asPointerMouseEvent() && pointerEvent->isPressEvent() && pointerEvent->allPointsAccepted()); + allowDoubleClick = wasAccepted || !(pointerEvent->asPointerMouseEvent() && pointerEvent->isPressEvent() && pointerEvent->allPointsAccepted()); } if (handlersOnly) return; @@ -2831,17 +2830,10 @@ bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QQuickPointerEvent *event // get a touch event customized for delivery to filteringParent QScopedPointer<QTouchEvent> filteringParentTouchEvent(pte->touchEventForItem(receiver, true)); if (filteringParentTouchEvent) { - QVarLengthArray<QPair<QQuickPointerHandler *, QQuickEventPoint *>, 32> passiveGrabsToCancel; if (filteringParent->childMouseEventFilter(receiver, filteringParentTouchEvent.data())) { qCDebug(DBG_TOUCH) << "touch event intercepted by childMouseEventFilter of " << filteringParent; skipDelivery.append(filteringParent); for (auto point: qAsConst(filteringParentTouchEvent->touchPoints())) { - auto pointerEventPoint = pte->pointById(point.id()); - for (auto handler : pointerEventPoint->passiveGrabbers()) { - QPair<QQuickPointerHandler *, QQuickEventPoint *> grab(handler, pointerEventPoint); - if (!passiveGrabsToCancel.contains(grab)) - passiveGrabsToCancel.append(grab); - } QQuickEventPoint *pt = event->pointById(point.id()); pt->setAccepted(); pt->setGrabberItem(filteringParent); @@ -2887,12 +2879,6 @@ bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QQuickPointerEvent *event touchMouseUnset = false; // We want to leave touchMouseId and touchMouseDevice set if (mouseEvent->isAccepted()) filteringParent->grabMouse(); - auto pointerEventPoint = pte->pointById(tp.id()); - for (auto handler : pointerEventPoint->passiveGrabbers()) { - QPair<QQuickPointerHandler *, QQuickEventPoint *> grab(handler, pointerEventPoint); - if (!passiveGrabsToCancel.contains(grab)) - passiveGrabsToCancel.append(grab); - } } filtered = true; } @@ -2908,8 +2894,6 @@ bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QQuickPointerEvent *event } } } - for (auto grab : passiveGrabsToCancel) - grab.second->cancelPassiveGrab(grab.first); } } } diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp index c6990f5656..a2934eee32 100644 --- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp +++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp @@ -111,10 +111,10 @@ void tst_MptaInterop::touchDrag() QQuickTouchUtils::flush(window); auto pointerEvent = QQuickWindowPrivate::get(window)->pointerEventInstance(touchPointerDevice); QCOMPARE(tp.at(0)->property("pressed").toBool(), false); -// QCOMPARE(pointerEvent->point(0)->exclusiveGrabber(), mpta); + QTRY_VERIFY(pointerEvent->point(0)->passiveGrabbers().contains(drag)); // Start moving - // DragHandler gets keeps monitoring, due to its passive grab, + // DragHandler keeps monitoring, due to its passive grab, // and eventually steals the exclusive grab from MPTA int dragStoleGrab = 0; for (int i = 0; i < 4; ++i) { @@ -123,9 +123,9 @@ void tst_MptaInterop::touchDrag() QQuickTouchUtils::flush(window); if (!dragStoleGrab && pointerEvent->point(0)->exclusiveGrabber() == drag) dragStoleGrab = i; -// QCOMPARE(tp.at(0)->property("pressed").toBool(), !dragStoleGrab); } - qCDebug(lcPointerTests, "DragHandler stole the grab after %d events", dragStoleGrab); + if (dragStoleGrab) + qCDebug(lcPointerTests, "DragHandler stole the grab after %d events", dragStoleGrab); QVERIFY(dragStoleGrab > 1); touch.release(1, p1).commit(); @@ -150,6 +150,7 @@ void tst_MptaInterop::touchesThenPinch() QVERIFY(tp.at(3)); // the QML declares four touchpoints QSignalSpy mptaPressedSpy(mpta, SIGNAL(pressed(QList<QObject*>))); QSignalSpy mptaReleasedSpy(mpta, SIGNAL(released(QList<QObject*>))); + QSignalSpy mptaCanceledSpy(mpta, SIGNAL(canceled(QList<QObject*>))); QTest::QTouchEventSequence touch = QTest::touchEvent(window, touchDevice); auto pointerEvent = QQuickWindowPrivate::get(window)->pointerEventInstance(touchPointerDevice); @@ -162,7 +163,6 @@ void tst_MptaInterop::touchesThenPinch() QQuickTouchUtils::flush(window); QTRY_COMPARE(pointerEvent->point(0)->exclusiveGrabber(), nullptr); QTRY_COMPARE(pointerEvent->point(0)->passiveGrabbers().first(), drag); -// QTRY_VERIFY(tp.at(0)->property("pressed").toBool()); // Press a second touchpoint: MPTA grabs it QPoint p2 = mpta->mapToScene(QPointF(200, 30)).toPoint(); @@ -190,48 +190,59 @@ void tst_MptaInterop::touchesThenPinch() QCOMPARE(tp.at(1)->property("pressed").toBool(), false); QCOMPARE(tp.at(2)->property("pressed").toBool(), false); QCOMPARE(mptaPressedSpy.count(), 1); + QCOMPARE(mptaCanceledSpy.count(), 1); + QTRY_COMPARE(pointerEvent->point(0)->exclusiveGrabber(), pinch); + QTRY_COMPARE(pointerEvent->point(1)->exclusiveGrabber(), pinch); QTRY_COMPARE(pointerEvent->point(2)->exclusiveGrabber(), pinch); QVERIFY(pinch->active()); - // Move some more: PinchHandler reacts + // Start moving: PinchHandler steals the exclusive grab from MPTA as soon as dragThreshold is exceeded + int pinchStoleGrab = 0; for (int i = 0; i < 8; ++i) { - p1 += QPoint(4, 4); - p2 += QPoint(4, 4); - p3 += QPoint(-4, 4); + p1 += QPoint(dragThreshold / 2, dragThreshold / 2); + p2 += QPoint(dragThreshold / 2, dragThreshold / 2); + p3 += QPoint(-dragThreshold / 2, dragThreshold / 2); touch.move(1, p1).move(2, p2).move(3, p3).commit(); + QQuickTouchUtils::flush(window); QTRY_COMPARE(tp.at(0)->property("pressed").toBool(), false); QCOMPARE(tp.at(1)->property("pressed").toBool(), false); QCOMPARE(tp.at(2)->property("pressed").toBool(), false); + if (!pinchStoleGrab && pointerEvent->point(0)->exclusiveGrabber() == pinch) + pinchStoleGrab = i; } - qCDebug(lcPointerTests) << "scale" << pinch->scale() << "rot" << pinch->rotation(); - QTRY_VERIFY(pinch->rotation() > 10); + qCDebug(lcPointerTests) << "pinch started after" << pinchStoleGrab << "moves; ended with scale" << pinch->scale() << "rot" << pinch->rotation(); + QTRY_VERIFY(pinch->rotation() > 8); QVERIFY(pinch->scale() > 1); // Press one more point (pinkie finger) QPoint p4 = mpta->mapToScene(QPointF(300, 200)).toPoint(); - touch.stationary(1).stationary(2).stationary(3).press(4, p4).commit(); - // MPTA grabs p4 (which is at index 3) -// QTRY_COMPARE(pointerEvent->point(3)->exclusiveGrabber(), mpta); - // PinchHandler wantsPointerEvent declines, because it wants exactly 3 touchpoints, and there are now 4. - // Move some more... MPTA reacts, in spite of not grabbing all the points + touch.move(1, p1).move(2, p2).move(3, p3).press(4, p4).commit(); + // PinchHandler gives up its grabs (only on non-stationary points at this time: see QQuickPointerHandler::handlePointerEvent()) + // because it has minimum touch points 3, maximum touch points 3, and now there are 4 points. + // MPTA grabs all points which are not already grabbed + QTRY_COMPARE(pointerEvent->point(0)->exclusiveGrabber(), mpta); + QCOMPARE(pointerEvent->point(1)->exclusiveGrabber(), mpta); + QCOMPARE(pointerEvent->point(2)->exclusiveGrabber(), mpta); + QCOMPARE(pointerEvent->point(3)->exclusiveGrabber(), mpta); + // Move some more... MPTA keeps reacting for (int i = 0; i < 8; ++i) { p1 += QPoint(4, 4); p2 += QPoint(4, 4); p3 += QPoint(-4, 4); p4 += QPoint(-4, -4); touch.move(1, p1).move(2, p2).move(3, p3).move(4, p4).commit(); -// QTRY_COMPARE(pointerEvent->point(0)->exclusiveGrabber(), nullptr); -// QCOMPARE(pointerEvent->point(1)->exclusiveGrabber(), nullptr); -// QCOMPARE(pointerEvent->point(2)->exclusiveGrabber(), nullptr); -// QCOMPARE(pointerEvent->point(3)->exclusiveGrabber(), mpta); -// QCOMPARE(tp.at(0)->property("pressed").toBool(), true); -// QCOMPARE(tp.at(1)->property("pressed").toBool(), true); -// QCOMPARE(tp.at(2)->property("pressed").toBool(), true); -// QCOMPARE(tp.at(3)->property("pressed").toBool(), true); + QCOMPARE(pointerEvent->point(0)->exclusiveGrabber(), mpta); + QCOMPARE(pointerEvent->point(1)->exclusiveGrabber(), mpta); + QCOMPARE(pointerEvent->point(2)->exclusiveGrabber(), mpta); + QCOMPARE(pointerEvent->point(3)->exclusiveGrabber(), mpta); + QCOMPARE(tp.at(0)->property("pressed").toBool(), true); + QCOMPARE(tp.at(1)->property("pressed").toBool(), true); + QCOMPARE(tp.at(2)->property("pressed").toBool(), true); + QCOMPARE(tp.at(3)->property("pressed").toBool(), true); } - // Release the pinkie - touch.stationary(1).stationary(2).stationary(3).release(4, p4).commit(); + // Release the pinkie: PinchHandler acquires passive grabs on the 3 remaining points + touch.move(1, p1).move(2, p2).move(3, p3).release(4, p4).commit(); // Move some more: PinchHander grabs again, and reacts for (int i = 0; i < 8; ++i) { p1 -= QPoint(4, 4); @@ -239,21 +250,36 @@ void tst_MptaInterop::touchesThenPinch() p3 -= QPoint(-4, 4); touch.move(1, p1).move(2, p2).move(3, p3).commit(); QTRY_COMPARE(pointerEvent->point(0)->exclusiveGrabber(), pinch); + QCOMPARE(pointerEvent->point(1)->exclusiveGrabber(), pinch); + QCOMPARE(pointerEvent->point(2)->exclusiveGrabber(), pinch); } // Release the first finger touch.stationary(2).stationary(3).release(1, p1).commit(); - // Move some more: PinchHander isn't interested in a mere 2 points, and MPTA should react... but it doesn't (TODO?) + // Move some more: PinchHander isn't interested in a mere 2 points. + // MPTA could maybe react; but QQuickWindowPrivate::deliverTouchEvent() calls + // deliverPressOrReleaseEvent() in a way which "starts over" with event delivery + // only for handlers, not for Items; therefore MPTA is not visited at this time. for (int i = 0; i < 8; ++i) { - p1 -= QPoint(4, 4); - p2 += QPoint(4, 4); - touch.move(1, p1).move(2, p2).commit(); - QTest::qWait(100); + p2 -= QPoint(4, 4); + p3 += QPoint(4, 4); + touch.move(2, p2).move(3, p3).commit(); + QQuickTouchUtils::flush(window); + } + + // Release another finger + touch.stationary(2).release(3, p3).commit(); + // Move some more: DragHandler reacts. + // It had a passive grab this whole time; now it activates and gets an exclusive grab. + for (int i = 0; i < 8; ++i) { + p2 += QPoint(8, -8); + touch.move(2, p2).commit(); + QTRY_COMPARE(pointerEvent->point(0)->exclusiveGrabber(), drag); } - touch.release(1, p1).release(2, p2).release(3, p3).commit(); + touch.release(2, p2).commit(); QQuickTouchUtils::flush(window); -// QTRY_COMPARE(mptaReleasedSpy.count(), 1); // all points at once + QTRY_COMPARE(mptaReleasedSpy.count(), 1); } QTEST_MAIN(tst_MptaInterop) diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp index cd60be6a4c..8dc035949e 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp @@ -314,11 +314,10 @@ void tst_DragHandler::touchDragMultiSliders_data() 0 << QVector<int> { 0, 1, 2 } << QVector<int> { 0, 0, 0 } << QVector<QVector2D> { {0, 60}, {0, 60}, {0, 60} }; QTest::newRow("Drag Knob: start on the knobs, drag diagonally downward") << 0 << QVector<int> { 0, 1, 2 } << QVector<int> { 0, 0, 0 } << QVector<QVector2D> { {20, 40}, {20, 60}, {20, 80} }; - // TOOD these fail -// QTest::newRow("Drag Anywhere: start on the knobs, drag down") << -// 1 << QVector<int> { 0, 1, 2 } << QVector<int> { 0, 0, 0 } << QVector<QVector2D> { {0, 60}, {0, 60}, {0, 60} }; -// QTest::newRow("Drag Anywhere: start on the knobs, drag diagonally downward") << -// 1 << QVector<int> { 0, 1, 2 } << QVector<int> { 0, 0, 0 } << QVector<QVector2D> { {20, 40}, {20, 60}, {20, 80} }; + QTest::newRow("Drag Anywhere: start on the knobs, drag down") << + 1 << QVector<int> { 0, 1, 2 } << QVector<int> { 0, 0, 0 } << QVector<QVector2D> { {0, 60}, {0, 60}, {0, 60} }; + QTest::newRow("Drag Anywhere: start on the knobs, drag diagonally downward") << + 1 << QVector<int> { 0, 1, 2 } << QVector<int> { 0, 0, 0 } << QVector<QVector2D> { {20, 40}, {20, 60}, {20, 80} }; // TODO these next two fail because the DragHandler grabs when a finger // drags across it from outside, but should rather start only if it is pressed inside // QTest::newRow("Drag Knob: start above the knobs, drag down") << diff --git a/tests/manual/pointer/pinchDragFlingMPTA.qml b/tests/manual/pointer/pinchDragFlingMPTA.qml index 07b2dc2c45..90812967ce 100644 --- a/tests/manual/pointer/pinchDragFlingMPTA.qml +++ b/tests/manual/pointer/pinchDragFlingMPTA.qml @@ -92,6 +92,7 @@ Rectangle { } Text { anchors.bottom: parent.bottom - text: pinch3.active ? getTransformationDetails(container, pinch3) : "Pinch with 3 fingers to scale, rotate and translate" + text: pinch3.active ? getTransformationDetails(container, pinch3) : + "Pinch with 3 fingers to scale, rotate and translate\nHold down Meta to drag with one finger or mouse" } } |