aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <[email protected]>2025-04-10 13:38:36 +0200
committerUlf Hermann <[email protected]>2025-04-15 17:27:08 +0000
commit6a8478829747289cdcce2a6e9628b31cfd865f15 (patch)
tree258e01574781006e154f35ef0fe35223582a3aea
parentb15ab35bfa66663b050dc17a8ed4b032addb0e8f (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. Pick-to: 6.9 6.8 Fixes: QTBUG-134922 Change-Id: Iac2883e7649dc0d2fb2669efa847becbb886ca64 Reviewed-by: Sami Shalayel <[email protected]> Reviewed-by: YaNing Lu <[email protected]> Reviewed-by: Fabian Kosmale <[email protected]>
-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 e73a141b70..e5e949f441 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>
@@ -317,6 +318,7 @@ public:
void buildBindEntries(QQmlBind *q, QQmlComponentPrivate::DeferredState *deferredState);
void preEvalEntry(QQmlBindEntry *entry);
void postEvalEntry(QQmlBindEntry *entry);
+ bool isCurrent(QQmlBindEntry *entry) const;
};
static void warnIgnoredProperties(QQmlBind *q)
@@ -1223,6 +1225,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 QQmlBindPrivate::preEvalEntry(QQmlBindEntry *entry)
{
if (!entry->prop.isValid() || (entry->currentKind == QQmlBindEntryKind::None))
@@ -1231,6 +1256,11 @@ void QQmlBindPrivate::preEvalEntry(QQmlBindEntry *entry)
return; // if the target is already gone, we can't do anything
if (!when) {
+ if (!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 20a8c409a5..42f5cfc1dc 100644
--- a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
+++ b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
@@ -7,6 +7,7 @@
#include <private/qqmlanybinding_p.h>
#include <private/qqmlbind_p.h>
#include <private/qqmlcomponentattached_p.h>
+#include <private/qqmlinstantiator_p.h>
#include <private/qqmlpropertytopropertybinding_p.h>
#include <private/qquickrectangle_p.h>
@@ -51,6 +52,7 @@ private slots:
void toggleEnableProperlyRemembersValues();
void qQmlPropertyToPropertyBinding();
void qQmlPropertyToPropertyBindingReverse();
+ void delayedBindingDestruction();
private:
QQmlEngine engine;
@@ -824,6 +826,36 @@ void tst_qqmlbinding::qQmlPropertyToPropertyBindingReverse()
QCOMPARE(target->b(), QRectF(55, 66, 77, 88));
}
+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"