aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexey Edelev <alexey.edelev@qt.io>2021-01-12 16:37:09 +0100
committerAlexey Edelev <alexey.edelev@qt.io>2021-01-14 14:52:47 +0100
commit96763dbb105fde20431a264789ac27abfdab841c (patch)
treefdad05f0e4529138cd25ab1cb6fef8745546e5eb
parent127c79fb7fda16b9a48ce8c425d1700d1aa7502d (diff)
Do not revert properties of deleted objects
If state contains revert action of properties of deleted objects, we should avoid adding them to apply list Fixes: QTBUG-85106 Pick-to: 5.15 Change-Id: Iff57eb9958a054476096f6d951ab7390277a2b39 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r--src/quick/util/qquickstate.cpp5
-rw-r--r--tests/auto/quick/qquickstates/data/revertNullObjectBinding.qml48
-rw-r--r--tests/auto/quick/qquickstates/tst_qquickstates.cpp68
3 files changed, 121 insertions, 0 deletions
diff --git a/src/quick/util/qquickstate.cpp b/src/quick/util/qquickstate.cpp
index 71ab1f4d62..6a72754bde 100644
--- a/src/quick/util/qquickstate.cpp
+++ b/src/quick/util/qquickstate.cpp
@@ -635,6 +635,11 @@ void QQuickState::apply(QQuickTransition *trans, QQuickState *revert)
}
}
if (!found) {
+ // If revert list contains bindings assigned to deleted objects, we need to
+ // prevent reverting properties of those objects.
+ if (d->revertList.at(ii).binding() && !d->revertList.at(ii).property().object()) {
+ continue;
+ }
QVariant cur = d->revertList.at(ii).property().read();
QQmlPropertyPrivate::removeBinding(d->revertList.at(ii).property());
diff --git a/tests/auto/quick/qquickstates/data/revertNullObjectBinding.qml b/tests/auto/quick/qquickstates/data/revertNullObjectBinding.qml
new file mode 100644
index 0000000000..dee82f52ed
--- /dev/null
+++ b/tests/auto/quick/qquickstates/data/revertNullObjectBinding.qml
@@ -0,0 +1,48 @@
+import QtQuick 2.12
+import Qt.test 1.0
+
+Item {
+ id: root
+ readonly property int someProp: 1234
+
+ property bool state1Active: false
+ property bool state2Active: false
+ StateGroup {
+ states: [
+ State {
+ id: state1
+ name: "state1"
+ when: state1Active
+ changes: [
+ PropertyChanges {
+ objectName: "propertyChanges1"
+ target: ContainingObj.obj
+ prop: root.someProp
+ }
+ ]
+ }
+ ]}
+ StateGroup {
+ states: [
+ State {
+ id: state2
+ name: "state2"
+ when: state2Active
+ changes: [
+ PropertyChanges {
+ objectName: "propertyChanges2"
+ target: ContainingObj.obj
+ prop: 11111
+ }
+ ]
+ }
+ ]
+ }
+
+ Component.onCompleted: {
+ state1Active = true;
+ state2Active = true;
+
+ ContainingObj.reset()
+ }
+}
diff --git a/tests/auto/quick/qquickstates/tst_qquickstates.cpp b/tests/auto/quick/qquickstates/tst_qquickstates.cpp
index e8ea6b5f1a..a3e0e9ef06 100644
--- a/tests/auto/quick/qquickstates/tst_qquickstates.cpp
+++ b/tests/auto/quick/qquickstates/tst_qquickstates.cpp
@@ -79,6 +79,55 @@ private:
QML_DECLARE_TYPE(MyRect)
QML_DECLARE_TYPEINFO(MyRect, QML_HAS_ATTACHED_PROPERTIES)
+class RemovableObj : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int prop READ prop WRITE setProp NOTIFY propChanged)
+
+public:
+ RemovableObj(QObject *parent) : QObject(parent), m_prop(4321) { }
+ int prop() const { return m_prop; }
+
+public slots:
+ void setProp(int prop)
+ {
+ if (m_prop == prop)
+ return;
+
+ m_prop = prop;
+ emit propChanged(m_prop);
+ }
+
+signals:
+ void propChanged(int prop);
+
+private:
+ int m_prop;
+};
+
+class ContainingObj : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(RemovableObj *obj READ obj NOTIFY objChanged)
+ RemovableObj *m_obj;
+
+public:
+ ContainingObj() : m_obj(new RemovableObj(this)) { }
+ RemovableObj *obj() const { return m_obj; }
+
+ Q_INVOKABLE void reset()
+ {
+ if (m_obj) {
+ m_obj->deleteLater();
+ }
+
+ m_obj = new RemovableObj(this);
+ emit objChanged();
+ }
+signals:
+ void objChanged();
+};
+
class tst_qquickstates : public QQmlDataTest
{
Q_OBJECT
@@ -140,12 +189,20 @@ private slots:
void duplicateStateName();
void trivialWhen();
void parentChangeCorrectReversal();
+ void revertNullObjectBinding();
};
void tst_qquickstates::initTestCase()
{
QQmlDataTest::initTestCase();
qmlRegisterType<MyRect>("Qt.test", 1, 0, "MyRectangle");
+ qmlRegisterSingletonType<ContainingObj>(
+ "Qt.test", 1, 0, "ContainingObj", [](QQmlEngine *engine, QJSEngine *) {
+ static ContainingObj instance;
+ engine->setObjectOwnership(&instance, QQmlEngine::CppOwnership);
+ return &instance;
+ });
+ qmlRegisterUncreatableType<RemovableObj>("Qt.test", 1, 0, "RemovableObj", "Uncreatable");
}
QByteArray tst_qquickstates::fullDataPath(const QString &path) const
@@ -1692,6 +1749,17 @@ void tst_qquickstates::parentChangeCorrectReversal()
QCOMPARE(oldX, stayingRectX.read().toDouble());
}
+void tst_qquickstates::revertNullObjectBinding()
+{
+ QQmlEngine engine;
+
+ QQmlComponent c(&engine, testFileUrl("revertNullObjectBinding.qml"));
+ QScopedPointer<QObject> root { c.create() };
+ QVERIFY(root);
+ QTest::qWait(10);
+ QQmlProperty state2Active(root.get(), "state2Active");
+ state2Active.write(false);
+}
QTEST_MAIN(tst_qquickstates)