From e835bccb1e53c2eeb20b5f95a2c0ea4beb547b7c Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Sun, 7 Mar 2021 20:26:56 +0100 Subject: [PATCH] QPropertyBinding: Add sticky mode A sticky QPropertyBinding is a binding that does not get removed when a write occurs. This is used in the QML engine to implement support for the QQmlPropertyData::DontRemoveBinding flag. Task-number: QTBUG-91689 Change-Id: Ib575b49abe634215318ccc7ba46212cc21eb4dad Reviewed-by: Andrei Golubev Reviewed-by: Ulf Hermann --- src/corelib/kernel/qproperty.cpp | 3 +++ src/corelib/kernel/qproperty_p.h | 8 ++++++ .../kernel/qproperty/tst_qproperty.cpp | 25 +++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp index a5354532d2e..7c8477e31a9 100644 --- a/src/corelib/kernel/qproperty.cpp +++ b/src/corelib/kernel/qproperty.cpp @@ -508,6 +508,9 @@ void QPropertyBindingData::removeBinding_helper() auto *existingBinding = d.binding(); Q_ASSERT(existingBinding); + if (existingBinding->isSticky()) { + return; + } auto observer = existingBinding->takeObservers(); d_ref() = 0; diff --git a/src/corelib/kernel/qproperty_p.h b/src/corelib/kernel/qproperty_p.h index 58a000e9bf0..6a074efebbe 100644 --- a/src/corelib/kernel/qproperty_p.h +++ b/src/corelib/kernel/qproperty_p.h @@ -184,6 +184,11 @@ private: bool hasBindingWrapper:1; // used to detect binding loops for eagerly evaluated properties bool isQQmlPropertyBinding:1; + /* a sticky binding does not get removed in removeBinding + this is used to support QQmlPropertyData::DontRemoveBinding + in qtdeclarative + */ + bool m_sticky:1; const QtPrivate::BindingFunctionVTable *vtable; @@ -234,11 +239,14 @@ public: size_t dependencyObserverCount = 0; bool isUpdating() {return updating;} + void setSticky(bool keep = true) {m_sticky = keep;} + bool isSticky() {return m_sticky;} QPropertyBindingPrivate(QMetaType metaType, const QtPrivate::BindingFunctionVTable *vtable, const QPropertyBindingSourceLocation &location, bool isQQmlPropertyBinding=false) : hasBindingWrapper(false) , isQQmlPropertyBinding(isQQmlPropertyBinding) + , m_sticky(false) , vtable(vtable) , location(location) , metaType(metaType) diff --git a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp index 0b05353bf08..c4032764643 100644 --- a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp +++ b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp @@ -56,6 +56,7 @@ private slots: void avoidDependencyAllocationAfterFirstEval(); void boolProperty(); void takeBinding(); + void stickyBinding(); void replaceBinding(); void changeHandler(); void propertyChangeHandlerApi(); @@ -307,6 +308,30 @@ void tst_QProperty::takeBinding() QVERIFY(!existingBinding.isNull()); } +void tst_QProperty::stickyBinding() +{ + QProperty prop; + QProperty prop2 {2}; + prop.setBinding([&](){ return prop2.value(); }); + QCOMPARE(prop.value(), 2); + auto privBinding = QPropertyBindingPrivate::get(prop.binding()); + // If we make a binding sticky, + privBinding->setSticky(); + // then writing to the property does not remove it + prop = 1; + QVERIFY(prop.hasBinding()); + // but the value still changes. + QCOMPARE(prop.value(), 1); + // The binding continues to work normally. + prop2 = 3; + QCOMPARE(prop.value(), 3); + // If we remove the stickiness + privBinding->setSticky(false); + // the binding goes away on the next write + prop = 42; + QVERIFY(!prop.hasBinding()); +} + void tst_QProperty::replaceBinding() { QProperty first(100);