aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@qt.io>2017-05-18 11:23:25 +0200
committerShawn Rutledge <shawn.rutledge@qt.io>2017-11-14 05:35:37 +0000
commita2e2c8a329768e783b205564e44b2f486b777d74 (patch)
tree28816a9645df182628b6786997b883d60d32e1a2
parentd845bdae79c93b1fd34427a76543152bd679b03f (diff)
Let passive-grabbing PointerHandlers see all point updates
even if all points are accepted or grabbed. A passive grab isn't much good if there are cases where the handler is prevented from monitoring. This enables e.g. the PinchHandler to steal the grab when the right number of touchpoints are present and have moved past the drag threshold, and enables completion of a couple of autotests. Change-Id: I78dc6fc585f80bfb3c13e0c6e757ef815fb94afe Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
-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"
}
}