diff options
author | Shawn Rutledge <[email protected]> | 2022-11-16 21:39:31 +0100 |
---|---|---|
committer | Shawn Rutledge <[email protected]> | 2022-11-17 10:56:25 +0100 |
commit | 3046fe153dd2c5679479eb512dcee6bdc80bd1cf (patch) | |
tree | c2ef37f2ec4a66c4d39c8dde0d1b5ad44578e196 | |
parent | b0a3a07a68d64fb8f32969e1dc88923fd58c0adf (diff) |
PinchHandler null target: remember accumulated scale between pinches
This restores behavior from b4d31c9ff5f0c5821ea127c663532d9fc2cae43e
which got broken in fc636af3a723ee8b4ee42cf71864ae0df5ca4621.
As documented, PinchHandler.scale is the accumulated scale that would
be applied to the target item (even if there is no target), whereas
activeScale is the scale during one pinch gesture. After the first
gesture, these two values are supposed to diverge, even if there is
no target; that way you can bind scale to some property, to scale
something else in the same way that PinchHandler would normally scale
its target.
Pick-to: 6.2 6.4
Fixes: QTBUG-108549
Task-number: QTBUG-68941
Task-number: QTBUG-92064
Change-Id: I32ff37e394fd8466128603eddd5697ba1cc1a0ed
Reviewed-by: Qt CI Bot <[email protected]>
Reviewed-by: Richard Moe Gustavsen <[email protected]>
4 files changed, 133 insertions, 5 deletions
diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp index 33610749c5..aaf09c1624 100644 --- a/src/quick/handlers/qquickpinchhandler.cpp +++ b/src/quick/handlers/qquickpinchhandler.cpp @@ -203,8 +203,8 @@ void QQuickPinchHandler::onActiveChanged() m_startRotation = t->rotation(); m_startPos = t->position(); } else { - m_startScale = 1; - m_startRotation = 0; + m_startScale = m_accumulatedScale; + m_startRotation = 0; // TODO m_accumulatedRotation (QTBUG-94168) } qCDebug(lcPinchHandler) << "activated with starting scale" << m_startScale << "rotation" << m_startRotation; } else { diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/nullTarget.qml b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/nullTarget.qml new file mode 100644 index 0000000000..a348938aca --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/nullTarget.qml @@ -0,0 +1,33 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick 2.15 + +Item { + width: 320; height: 320 + property alias pinchScale: pinch.scale + + Rectangle { + objectName: "blackrect" + width: 200; height: 200 + color: "black" + antialiasing: true + scale: pinch.scale + rotation: pinch.rotation + x: pinch.translation.x + y: pinch.translation.y + + PinchHandler { + id: pinch + target: null + minimumScale: 0.5 + maximumScale: 4 + } + + Text { + color: "cyan" + anchors.centerIn: parent + text: "scale " + pinch.scale.toFixed(2) + " activeScale " + pinch.activeScale.toFixed(2) + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp index 1ef095e4cf..a6476ac0d5 100644 --- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp @@ -29,6 +29,7 @@ public: private slots: void cleanupTestCase(); void pinchProperties(); + void scale_data(); void scale(); void scaleThreeFingers(); void scaleNativeGesture_data(); @@ -179,16 +180,26 @@ QEventPoint makeTouchPoint(int id, QPoint p, QQuickView *v, QQuickItem *i) return touchPoint; } +void tst_QQuickPinchHandler::scale_data() +{ + QTest::addColumn<QUrl>("qmlfile"); + QTest::addColumn<bool>("hasTarget"); + QTest::newRow("targetModifying") << testFileUrl("pinchproperties.qml") << true; + QTest::newRow("nullTarget") << testFileUrl("nullTarget.qml") << false; +} + void tst_QQuickPinchHandler::scale() { - QQuickView window; - QVERIFY(QQuickTest::showView(window, testFileUrl("pinchproperties.qml"))); + QFETCH(QUrl, qmlfile); + QFETCH(bool, hasTarget); + QQuickView window; + QVERIFY(QQuickTest::showView(window, qmlfile)); QQuickItem *root = qobject_cast<QQuickItem*>(window.rootObject()); QVERIFY(root != nullptr); auto *pinchHandler = static_cast<PinchHandler *>(root->findChild<QQuickPinchHandler*>()); QVERIFY(pinchHandler != nullptr); - QQuickItem *blackRect = pinchHandler->target(); + QQuickItem *blackRect = (hasTarget ? pinchHandler->target() : pinchHandler->parentItem()); QVERIFY(blackRect != nullptr); QSignalSpy grabChangedSpy(pinchHandler, SIGNAL(grabChanged(QPointingDevice::GrabTransition, QEventPoint))); @@ -240,6 +251,40 @@ void tst_QQuickPinchHandler::scale() QCOMPARE(pinchHandler->centroid().scenePosition(), expectedCentroid); } + qreal lastScale = pinchHandler->scale(); + pinchSequence.release(0, p0, &window).release(1, p1, &window).commit(); + QQuickTouchUtils::flush(&window); + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); + // scale property is persistent after release + QCOMPARE(pinchHandler->scale(), lastScale); + + // pinch a second time: scale picks up where we left off + p0 = QPoint(80, 80); + p1 = QPoint(100, 100); + pinchSequence.press(0, p0, &window).press(1, p1, &window).commit(); + // move one point until PinchHandler activates + for (int pi = 0; pi < 10 && !pinchHandler->active(); ++pi) { + p1 += pd; + pinchSequence.stationary(0).move(1, p1, &window).commit(); + QQuickTouchUtils::flush(&window); + } + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); + QCOMPARE(pinchHandler->active(), true); + QCOMPARE(pinchHandler->scale(), lastScale); // just activated, not scaling further yet + for (int i = 0; i < 2; ++i) { + lastScale = pinchHandler->scale(); + p1 += pd; + pinchSequence.stationary(0).move(1, p1, &window).commit(); + QQuickTouchUtils::flush(&window); + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); + QCOMPARE_GT(pinchHandler->scale(), lastScale); + line.setP2(p1); + qreal expectedActiveScale = line.length() / startLength; + QVERIFY(qFloatDistance(pinchHandler->activeScale(), expectedActiveScale) < 10); + QCOMPARE(pinchHandler->scale(), root->property("pinchScale").toReal()); + QCOMPARE_NE(pinchHandler->scale(), pinchHandler->activeScale()); // not in sync anymore + } + // scale beyond maximumScale p1 = QPoint(310, 310); pinchSequence.stationary(0).move(1, p1, &window).commit(); diff --git a/tests/manual/pointer/pinchNullTarget.qml b/tests/manual/pointer/pinchNullTarget.qml new file mode 100644 index 0000000000..79c6047991 --- /dev/null +++ b/tests/manual/pointer/pinchNullTarget.qml @@ -0,0 +1,50 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick 2.15 + +Rectangle { + width: 1024; height: 600 + color: "#eee" + + function getTransformationDetails(item, pinchhandler) { + return "\n\npinch.scale:" + pinchhandler.scale.toFixed(2) + + "\npinch.activeScale:" + pinchhandler.activeScale.toFixed(2) + + "\npinch.rotation:" + pinchhandler.rotation.toFixed(2) + + "\npinch.translation:" + "(" + pinchhandler.translation.x.toFixed(2) + "," + pinchhandler.translation.y.toFixed(2) + ")" + + "\nrect.scale: " + item.scale.toFixed(2) + + "\nrect.rotation: " + item.rotation.toFixed(2) + + "\nrect.position: " + "(" + item.x.toFixed(2) + "," + item.y.toFixed(2) + ")" + } + + Rectangle { + width: parent.width - 100; height: parent.height - 100; x: 50; y: 50 + color: "lightsteelblue" + antialiasing: true + scale: pinch.scale + + PinchHandler { + id: pinch + target: null + minimumScale: 0.5 + maximumScale: 3 + } + + Text { + text: "Pinch with 2 fingers to scale, rotate and translate" + + getTransformationDetails(parent, pinch) + } + } + + Rectangle { + id: centroidIndicator + x: pinch.centroid.scenePosition.x - radius + y: pinch.centroid.scenePosition.y - radius + z: 1 + visible: pinch.active + radius: width / 2 + width: 10 + height: width + color: "red" + } +} |