aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabian Kosmale <fabian.kosmale@qt.io>2022-02-07 15:48:30 +0100
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2022-02-08 18:35:08 +0000
commitcb4594718a709c50cc8da0237cef6cd0567e5295 (patch)
tree99cadb3d788fe98dac016a7fb6fa202748c4e8cb
parentcd60a5182e273d99ef66b77a555cea2571d034a9 (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: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-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 d664b67485..781d2cf1fb 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) {
qCDebug(lcStates) << "Setting auto state due to expression";
if (currentState != state->name()) {
q->setState(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 176a377ed4..2397cf4f06 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();
@@ -1715,6 +1716,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;