aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabian Kosmale <fabian.kosmale@qt.io>2022-02-07 15:48:30 +0100
committerFabian Kosmale <fabian.kosmale@qt.io>2022-02-10 09:15:19 +0100
commitc2b6f09d280db2862e9559590312b612b3dd42d0 (patch)
tree671dbed0d437b91a9396ec0678fff2c0409bc269
parentbb4aa6ef99a7c3600432f1b66284e0239e7d9d6f (diff)
Correctly handle QQuickState::when
64c1fbe96c68b1286a70242ff4922be140128cb2 fixed that one could not assign a constant value to QQuickState::when. However, as alluded to by a comment in QQState's code, this caused state oscillation, breaking clean transitions between two states. The reason for this issue is that as soon as once of the when condition changes, we check the when condition of all states. However, in the case of logically exclusive cases we can run into the issue that the binding of the second state has not been re-evaluated yet. Thus, we might end up with all states being false, even though the second one becomes true immediately afterwards. To avoid this issue, we force the reevaluation of the binding. However, instead of doing this by using a QQmlBinding property (with all the tooling and runtime issues that it would entail), we keep using a normal property. We then ask the engine for the binding, and if it exists, we reevaluate it to obtain the current value. Fixes: QTBUG-86695 Change-Id: Id10a3549935794b93b29450b0f5dfb6d1df691a1 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> (cherry picked from commit a8c729d83979fb0b9939044d246e73b1d578e65b) Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
-rw-r--r--src/quick/util/qquickstategroup.cpp10
-rw-r--r--tests/auto/quick/qquickstates/data/noStateOsciallation.qml22
-rw-r--r--tests/auto/quick/qquickstates/tst_qquickstates.cpp15
3 files changed, 46 insertions, 1 deletions
diff --git a/src/quick/util/qquickstategroup.cpp b/src/quick/util/qquickstategroup.cpp
index b86333943c..afde0e2f24 100644
--- a/src/quick/util/qquickstategroup.cpp
+++ b/src/quick/util/qquickstategroup.cpp
@@ -376,7 +376,15 @@ bool QQuickStateGroupPrivate::updateAutoState()
QQuickState *state = states.at(ii);
if (state->isWhenKnown()) {
if (state->isNamed()) {
- if (state->when()) {
+ bool whenValue = state->when();
+ const QQmlProperty whenProp(state, u"when"_qs);
+ const auto potentialWhenBinding = QQmlAnyBinding::ofProperty(whenProp);
+ Q_ASSERT(!potentialWhenBinding.isUntypedPropertyBinding());
+ // if there is a binding, the value in when might not be up-to-date at this point
+ // so we manually reevaluate the binding
+ if (auto abstractBinding = dynamic_cast<QQmlBinding *>( potentialWhenBinding.asAbstractBinding()))
+ whenValue = abstractBinding->evaluate().toBool();
+ if (whenValue) {
if (stateChangeDebug())
qWarning() << "Setting auto state due to expression";
if (currentState != state->name()) {
diff --git a/tests/auto/quick/qquickstates/data/noStateOsciallation.qml b/tests/auto/quick/qquickstates/data/noStateOsciallation.qml
new file mode 100644
index 0000000000..f0d7aeeb6d
--- /dev/null
+++ b/tests/auto/quick/qquickstates/data/noStateOsciallation.qml
@@ -0,0 +1,22 @@
+import QtQuick 2.15
+
+Item {
+ id: root
+ property int number: 2
+ property int stateChangeCounter: 0
+
+ Item {
+ id: item
+ onStateChanged: ++stateChangeCounter
+ states: [
+ State {
+ name: "n1"
+ when: root.number === 1
+ },
+ State {
+ name: "n2"
+ when: root.number === 2
+ }
+ ]
+ }
+}
diff --git a/tests/auto/quick/qquickstates/tst_qquickstates.cpp b/tests/auto/quick/qquickstates/tst_qquickstates.cpp
index 87d39228f4..5120f40a3f 100644
--- a/tests/auto/quick/qquickstates/tst_qquickstates.cpp
+++ b/tests/auto/quick/qquickstates/tst_qquickstates.cpp
@@ -199,6 +199,7 @@ private slots:
void revertListMemoryLeak();
void duplicateStateName();
void trivialWhen();
+ void noStateOsciallation();
void parentChangeCorrectReversal();
void revertNullObjectBinding();
void bindableProperties();
@@ -1714,6 +1715,20 @@ void tst_qquickstates::trivialWhen()
QVERIFY(root);
}
+void tst_qquickstates::noStateOsciallation()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("noStateOsciallation.qml"));
+ QScopedPointer<QObject> root {component.create()};
+ QVERIFY(root);
+ // set to 1 on initial transition from "" to "n2"
+ QCOMPARE(root->property("stateChangeCounter").toInt(), 1);
+ root->setProperty("number", 1);
+ // setting number to 1 changes directly from "n2" to "n1"
+ // without any intermediate transition to ""
+ QCOMPARE(root->property("stateChangeCounter").toInt(), 2);
+}
+
void tst_qquickstates::parentChangeCorrectReversal()
{
QQmlEngine engine;