aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiDe Zhang <zhangjide@uniontech.com>2022-07-22 11:51:54 +0800
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2023-02-08 02:58:38 +0000
commitdeb0628a18e87be6b4e57ae7dace9408bc0f4f91 (patch)
tree854cab653eb6e6c119c29b235046975a4084b6cc
parent9ed259613783f43522b70224bb049d7207814f2a (diff)
Flipable: switch sides at 90 degrees regardless of camera distance
QQuickRotation::applyTo(QMatrix4x4 *matrix) uses QMatrix4x4::projectedRotate() to apply rotation in pseudo-3D. QQuickFlipablePrivate::updateSide() was previously applying QQuickRotation to QTransform to decide which side should be showing; but when using QTransform::map(QPointF) to get the position of the transformed point, it is affected by the distance-to-plane value. QMatrix4x4::projectedRotate() uses 1024 as distance from "camera" to plane; so if the items on the plane are too large, some coordinates on the plane may be behind the camera during rotation, which could cause QQuickFlipable to mistakenly think that the current side has become QQuickFlipable::Back. Now we directly calculate the side of QQuickFlipable in 3D space rather than performing projection, to avoid being affected by the camera distance value. We use QMatrix4x4 instead of QTransform. Fixes: QTBUG-75954 Change-Id: Ibb08f6f6de690f5b56c3130a1f7b09beb250c807 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: MikoĊ‚aj Boc <Mikolaj.Boc@qt.io> Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io> (cherry picked from commit 036b6159edeb8e5ffe7a1c5e83ac99c3ab2e9988) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/quick/items/qquickflipable.cpp54
-rw-r--r--tests/auto/quick/qquickflipable/data/flip-y-axis-flipable.qml32
-rw-r--r--tests/auto/quick/qquickflipable/tst_qquickflipable.cpp28
3 files changed, 108 insertions, 6 deletions
diff --git a/src/quick/items/qquickflipable.cpp b/src/quick/items/qquickflipable.cpp
index d599618834..4ea085bb0c 100644
--- a/src/quick/items/qquickflipable.cpp
+++ b/src/quick/items/qquickflipable.cpp
@@ -3,7 +3,7 @@
#include "qquickflipable_p.h"
#include "qquickitem_p.h"
-
+#include "qquicktranslate_p.h"
#include <QtQml/qqmlinfo.h>
@@ -201,9 +201,20 @@ void QQuickFlipable::updatePolish()
d->updateSide();
}
-// determination on the currently visible side of the flipable
-// has to be done on the complete scene transform to give
-// correct results.
+/*! \internal
+ Flipable must use the complete scene transform to correctly determine the
+ currently visible side.
+
+ It must also be independent of camera distance, in case the contents are
+ too wide: for rotation transforms we simply call QMatrix4x4::rotate(),
+ whereas QQuickRotation::applyTo(QMatrix4x4*) calls
+ QMatrix4x4::projectedRotate() which by default assumes the camera distance
+ is 1024 virtual pixels. So for example if contents inside Flipable are to
+ be flipped around the y axis, and are wider than 1024*2, some of the
+ rendering goes behind the "camera". That's expected for rendering (since we
+ didn't provide API to change camera distance), but not ok for deciding when
+ to flip.
+*/
void QQuickFlipablePrivate::updateSide()
{
Q_Q(QQuickFlipable);
@@ -213,8 +224,39 @@ void QQuickFlipablePrivate::updateSide()
sideDirty = false;
- QTransform sceneTransform;
- itemToParentTransform(sceneTransform);
+ QMatrix4x4 sceneTransform;
+
+ const qreal tx = x.value();
+ const qreal ty = y.value();
+ if (!qFuzzyIsNull(tx) || !qFuzzyIsNull(ty))
+ sceneTransform.translate(tx, ty);
+
+ for (const auto *transform : std::as_const(transforms)) {
+ if (const auto *rot = qobject_cast<const QQuickRotation *>(transform)) {
+ // rotation is a special case: we want to call rotate() instead of projectedRotate()
+ const auto angle = rot->angle();
+ const auto axis = rot->axis();
+ if (!(qFuzzyIsNull(angle) || axis.isNull())) {
+ sceneTransform.translate(rot->origin());
+ sceneTransform.rotate(angle, axis.x(), axis.y(), axis.z());
+ sceneTransform.translate(-rot->origin());
+ }
+ } else {
+ transform->applyTo(&sceneTransform);
+ }
+ }
+
+ const bool hasRotation = !qFuzzyIsNull(rotation());
+ const bool hasScale = !qFuzzyCompare(scale(), 1);
+ if (hasScale || hasRotation) {
+ QPointF tp = computeTransformOrigin();
+ sceneTransform.translate(tp.x(), tp.y());
+ if (hasScale)
+ sceneTransform.scale(scale(), scale());
+ if (hasRotation)
+ sceneTransform.rotate(rotation(), 0, 0, 1);
+ sceneTransform.translate(-tp.x(), -tp.y());
+ }
QPointF p1(0, 0);
QPointF p2(1, 0);
diff --git a/tests/auto/quick/qquickflipable/data/flip-y-axis-flipable.qml b/tests/auto/quick/qquickflipable/data/flip-y-axis-flipable.qml
new file mode 100644
index 0000000000..1b6a3f5018
--- /dev/null
+++ b/tests/auto/quick/qquickflipable/data/flip-y-axis-flipable.qml
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 UnionTech Software Technology Co., Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import QtQuick
+
+Flipable {
+ id: flipable
+
+ property real angle: 0
+ width: 3840 // wider than 1024 * 2: part of it goes behind the camera while flipping
+ height: 2160
+
+ front: Rectangle {
+ width: parent.width
+ height: parent.height
+ color: "red"
+ anchors.centerIn: parent
+ }
+ back: Rectangle {
+ color: "yellow"
+ anchors.centerIn: parent
+ width: parent.width
+ height: parent.height
+ }
+ transform: Rotation {
+ id: rotation
+ origin.x: flipable.width / 2
+ origin.y: flipable.height / 2
+ axis.x: 0; axis.y: 1; axis.z: 0
+ angle: flipable.angle
+ }
+}
diff --git a/tests/auto/quick/qquickflipable/tst_qquickflipable.cpp b/tests/auto/quick/qquickflipable/tst_qquickflipable.cpp
index c1eab29759..17b5bba9d5 100644
--- a/tests/auto/quick/qquickflipable/tst_qquickflipable.cpp
+++ b/tests/auto/quick/qquickflipable/tst_qquickflipable.cpp
@@ -27,6 +27,9 @@ private slots:
void QTBUG_9161_crash();
void QTBUG_8474_qgv_abort();
+ void flipRotationAngle_data();
+ void flipRotationAngle();
+
private:
QQmlEngine engine;
};
@@ -112,6 +115,31 @@ void tst_qquickflipable::QTBUG_8474_qgv_abort()
delete window;
}
+void tst_qquickflipable::flipRotationAngle_data()
+{
+ QTest::addColumn<int>("angle");
+ QTest::addColumn<QQuickFlipable::Side>("side");
+
+ QTest::newRow("89") << 89 << QQuickFlipable::Front;
+ QTest::newRow("91") << 91 << QQuickFlipable::Back;
+ QTest::newRow("-89") << -89 << QQuickFlipable::Front;
+ QTest::newRow("-91") << -91 << QQuickFlipable::Back;
+}
+
+void tst_qquickflipable::flipRotationAngle() // QTBUG-75954
+{
+ QFETCH(int, angle);
+ QFETCH(QQuickFlipable::Side, side);
+
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("flip-y-axis-flipable.qml"));
+ QQuickFlipable *obj = qobject_cast<QQuickFlipable*>(c.create());
+ QVERIFY(obj != nullptr);
+ obj->setProperty("angle", angle);
+ QCOMPARE(obj->side(), side);
+ delete obj;
+}
+
QTEST_MAIN(tst_qquickflipable)
#include "tst_qquickflipable.moc"