diff options
| author | Ulf Hermann <ulf.hermann@qt.io> | 2025-04-10 13:38:36 +0200 |
|---|---|---|
| committer | Ulf Hermann <ulf.hermann@qt.io> | 2025-04-17 20:35:53 +0200 |
| commit | 3d486bfce5480c12fcdb48abd2086a45bf98a6b9 (patch) | |
| tree | 9aa8fa63d63705e7a813290b231c8dbf069dbfce | |
| parent | b81df9a31d8fa9b4f3a2e72060a13e5ec225c91d (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.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 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" |
