PinchHandler null target: remember accumulated scale between pinches

This restores behavior from b4d31c9ff5
which got broken in fc636af3a7.
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 <qt_ci_bot@qt-project.org>
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
This commit is contained in:
Shawn Rutledge 2022-11-16 21:39:31 +01:00
parent b0a3a07a68
commit 3046fe153d
4 changed files with 133 additions and 5 deletions

View File

@ -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 {

View File

@ -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)
}
}
}

View File

@ -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();

View File

@ -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"
}
}