aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quick/handlers/qquickpinchhandler.cpp13
-rw-r--r--src/quick/items/qquickwindow.cpp90
-rw-r--r--tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp92
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp9
-rw-r--r--tests/manual/pointer/pinchDragFlingMPTA.qml3
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"
}
}