diff options
author | Ulf Hermann <[email protected]> | 2025-04-10 13:38:36 +0200 |
---|---|---|
committer | Ulf Hermann <[email protected]> | 2025-04-15 17:27:08 +0000 |
commit | 6a8478829747289cdcce2a6e9628b31cfd865f15 (patch) | |
tree | 258e01574781006e154f35ef0fe35223582a3aea | |
parent | b15ab35bfa66663b050dc17a8ed4b032addb0e8f (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.cpp | 30 | ||||
-rw-r--r-- | tests/auto/qml/qqmlbinding/data/delayedBindingDestruction.qml | 31 | ||||
-rw-r--r-- | tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp | 32 |
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" |