aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2025-04-10 13:38:36 +0200
committerUlf Hermann <ulf.hermann@qt.io>2025-04-17 20:35:53 +0200
commit3d486bfce5480c12fcdb48abd2086a45bf98a6b9 (patch)
tree9aa8fa63d63705e7a813290b231c8dbf069dbfce
parentb81df9a31d8fa9b4f3a2e72060a13e5ec225c91d (diff)
QQmlBind: Only restore previous state if current state is still active
If some other Binding has overridden the one we're currently clearing, we should not restore the previous state. Amends commit e2fa7ab91310ea74c30e9458dfbe20d257578659 [ChangeLog][QtQml] The Binding element now only restores previous bindings or values if its own binding is still active on destruction or changes to its "when" property. If it has been overridden by another Binding element, it will not disable that one anymore. Fixes: QTBUG-134922 Change-Id: Iac2883e7649dc0d2fb2669efa847becbb886ca64 Reviewed-by: Sami Shalayel <sami.shalayel@qt.io> Reviewed-by: YaNing Lu <luyaning@uniontech.com> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> (cherry picked from commit 6a8478829747289cdcce2a6e9628b31cfd865f15) Reviewed-by: Semih Yavuz <semih.yavuz@qt.io> (cherry picked from commit 1c3bbebb23179a6f39ac63f8f31c3240793f006d) Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io>
-rw-r--r--src/qmlmeta/types/qqmlbind.cpp30
-rw-r--r--tests/auto/qml/qqmlbinding/data/delayedBindingDestruction.qml31
-rw-r--r--tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp32
3 files changed, 93 insertions, 0 deletions
diff --git a/src/qmlmeta/types/qqmlbind.cpp b/src/qmlmeta/types/qqmlbind.cpp
index 6128eb6925..85b3a94d17 100644
--- a/src/qmlmeta/types/qqmlbind.cpp
+++ b/src/qmlmeta/types/qqmlbind.cpp
@@ -13,6 +13,7 @@
#include <private/qv4persistent_p.h>
#include <private/qv4qmlcontext_p.h>
#include <private/qv4resolvedtypereference_p.h>
+#include <private/qv4runtime_p.h>
#include <QtQml/qqmlcontext.h>
#include <QtQml/qqmlengine.h>
@@ -285,6 +286,7 @@ public:
void onDelayedValueChanged(QString delayedName);
void evalDelayed();
void buildBindEntries(QQmlBind *q, QQmlComponentPrivate::DeferredState *deferredState);
+ bool isCurrent(QQmlBindEntry *entry) const;
};
void QQmlBindEntry::validate(QQmlBind *q) const
@@ -1058,6 +1060,29 @@ void QQmlBindEntry::clearPrev()
previousKind = previous.destroy(previousKind);
}
+bool QQmlBindPrivate::isCurrent(QQmlBindEntry *entry) const
+{
+ switch (entry->currentKind) {
+ case QQmlBindEntryKind::V4Value: {
+ auto propPriv = QQmlPropertyPrivate::get(entry->prop);
+ QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(propPriv->object);
+ Q_ASSERT(vmemo);
+ return QV4::RuntimeHelpers::strictEqual(
+ // fromReturnedValue is OK here because strictEqual will not allocate
+ QV4::Value::fromReturnedValue(vmemo->vmeProperty(propPriv->core.coreIndex())),
+ *entry->current.v4Value.valueRef());
+ }
+ case QQmlBindEntryKind::Variant:
+ return entry->current.variant == entry->prop.read();
+ case QQmlBindEntryKind::Binding:
+ return entry->current.binding == QQmlAnyBinding::ofProperty(entry->prop);
+ case QQmlBindEntryKind::None:
+ break;
+ }
+
+ return false;
+}
+
void QQmlBind::eval()
{
Q_D(QQmlBind);
@@ -1072,6 +1097,11 @@ void QQmlBind::eval()
continue; // if the target is already gone, we can't do anything
if (!d->when) {
+ if (!d->isCurrent(&entry)) {
+ entry.clearPrev();
+ return;
+ }
+
//restore any previous binding
switch (entry.previousKind) {
case QQmlBindEntryKind::Binding:
diff --git a/tests/auto/qml/qqmlbinding/data/delayedBindingDestruction.qml b/tests/auto/qml/qqmlbinding/data/delayedBindingDestruction.qml
new file mode 100644
index 0000000000..80cbf2dad1
--- /dev/null
+++ b/tests/auto/qml/qqmlbinding/data/delayedBindingDestruction.qml
@@ -0,0 +1,31 @@
+pragma ComponentBehavior: Bound
+import QtQml
+
+QtObject {
+ id: root
+
+ property string result
+ property string text: "foo"
+ function toggle() {
+ text = (text === "foo") ? "bar" : "foo"
+ repeater.model = [text]
+ }
+
+ property Instantiator instantiator: Instantiator {
+ id: repeater
+ model: 0
+ delegate: QtObject {
+ id: delegate
+ required property string modelData
+ property Binding binding: Binding {
+ target: root
+ property: "objectName"
+ value: delegate.modelData
+ }
+
+ property Binding binding2: Binding {
+ delegate.objectName: delegate.modelData
+ }
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
index 737d48a16b..009f5aedc8 100644
--- a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
+++ b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
@@ -6,6 +6,7 @@
#include <private/qmlutils_p.h>
#include <private/qqmlbind_p.h>
#include <private/qqmlcomponentattached_p.h>
+#include <private/qqmlinstantiator_p.h>
#include <private/qquickrectangle_p.h>
#include <QtTest/qtest.h>
@@ -46,6 +47,7 @@ private slots:
void whenEvaluatedEarlyEnough();
void propertiesAttachedToBindingItself();
void toggleEnableProperlyRemembersValues();
+ void delayedBindingDestruction();
private:
QQmlEngine engine;
@@ -674,6 +676,36 @@ void tst_qqmlbinding::toggleEnableProperlyRemembersValues()
}
}
+void tst_qqmlbinding::delayedBindingDestruction()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("delayedBindingDestruction.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(object);
+ QVERIFY(object->objectName().isEmpty());
+ QVERIFY(object->property("result").toString().isEmpty());
+
+ const auto verifyDelegate = [&](const QString &expected) {
+ QQmlInstantiator *instantiator
+ = object->property("instantiator").value<QQmlInstantiator *>();
+ QVERIFY(instantiator);
+ QCOMPARE(instantiator->object()->objectName(), expected);
+ };
+
+ QMetaObject::invokeMethod(object.data(), "toggle");
+ QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
+ QCoreApplication::processEvents();
+ QCOMPARE(object->objectName(), QLatin1String("bar"));
+ verifyDelegate(QLatin1String("bar"));
+
+ QMetaObject::invokeMethod(object.data(), "toggle");
+ QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
+ QCoreApplication::processEvents();
+ QCOMPARE(object->objectName(), QLatin1String("foo"));
+ verifyDelegate(QLatin1String("foo"));
+}
+
QTEST_MAIN(tst_qqmlbinding)
#include "tst_qqmlbinding.moc"