summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/kernel/qproperty.cpp43
-rw-r--r--src/corelib/kernel/qproperty.h22
-rw-r--r--src/corelib/kernel/qproperty_p.h9
-rw-r--r--src/corelib/kernel/qpropertyprivate.h2
-rw-r--r--tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp77
5 files changed, 153 insertions, 0 deletions
diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp
index 20efbaa1aa..5dd6507e26 100644
--- a/src/corelib/kernel/qproperty.cpp
+++ b/src/corelib/kernel/qproperty.cpp
@@ -381,6 +381,13 @@ void QPropertyBindingData::notifyObservers(QUntypedPropertyData *propertyDataPtr
observer.notify(d.bindingPtr(), propertyDataPtr);
}
+void QPropertyBindingData::markDirty()
+{
+ QPropertyBindingDataPointer d{this};
+ if (auto *binding = d.bindingPtr())
+ binding->setDirty(true);
+}
+
int QPropertyBindingDataPointer::observerCount() const
{
int count = 0;
@@ -837,6 +844,31 @@ QString QPropertyBindingError::description() const
*/
/*!
+ \fn template <typename T> void QProperty<T>::markDirty()
+
+ Programatically sets the property dirty. Any binding which depends on it will
+ be notified.
+ This can be useful for properties which do not only depend on bindable properties,
+ but also on non-bindable properties or some other state.
+
+ For example, assume we have a \c Circle class, with a non-bindable \c radius property
+ and a corresponding \c radiusChanged signal. We now want to create a property for a
+ cylinders volume, based on a height \c QProperty and an instance of Circle. To ensure
+ that the volume changes, we can call setDirty in a slot connected to radiusChanged.
+ \code
+ Circle circle;
+ QProperty<double> height;
+
+ QProperty<double> volume;
+ volume.setBinding([&]() {return height * std::pi_v<double> * circle.radius() * circle.radius()};
+ QOBject::connect(&circle, &Circle::radiusChanged, [&](){volume.markDirty();});
+ \endcode
+
+ \note Binding to a QObjectBindableProperty's signal does not make sense in general. Bindings
+ across bindable properties get marked dirty automatically.
+*/
+
+/*!
\fn template <typename T> QPropertyBinding<T> QProperty<T>::setBinding(const QPropertyBinding<T> &newBinding)
Associates the value of this property with the provided \a newBinding
@@ -1036,6 +1068,17 @@ QString QPropertyBindingError::description() const
*/
/*!
+ \fn template <typename Class, typename T, auto offset, auto Callback> void QObjectBindableProperty<Class, T, offset, Callback>::markDirty()
+
+ Programatically sets the property dirty. Any binding which depend on it will
+ be notified.
+ This can be useful for properties which do not only depend on bindable properties,
+ but also on non-bindable properties or some other state.
+
+ \sa QProperty<T>::markDirty
+*/
+
+/*!
\fn template <typename Class, typename T, auto offset, auto Callback> QPropertyBinding<T> QObjectBindableProperty<Class, T, offset, Callback>::setBinding(const QPropertyBinding<T> &newBinding)
Associates the value of this property with the provided \a newBinding
diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h
index 20e7daf811..102b627d76 100644
--- a/src/corelib/kernel/qproperty.h
+++ b/src/corelib/kernel/qproperty.h
@@ -406,6 +406,11 @@ public:
return true;
}
+ void markDirty() {
+ d.markDirty();
+ notify();
+ }
+
#ifndef Q_CLANG_QDOC
template <typename Functor>
QPropertyBinding<T> setBinding(Functor &&f,
@@ -972,6 +977,15 @@ public:
return bd && bd->binding() != nullptr;
}
+ void markDirty() {
+ QBindingStorage *storage = qGetBindingStorage(owner());
+ auto bd = storage->bindingData(this, /*create=*/false);
+ if (bd) { // if we have no BindingData, nobody can listen anyway
+ bd->markDirty();
+ notify(bd);
+ }
+ }
+
QPropertyBinding<T> binding() const
{
auto *bd = qGetBindingStorage(owner())->bindingData(this);
@@ -1121,6 +1135,14 @@ public:
return *storage->bindingData(const_cast<QObjectComputedProperty *>(this), true);
}
+ void markDirty() {
+ // computed property can't store a binding, so there's nothing to mark
+ auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
+ auto bd = storage->bindingData(const_cast<QObjectComputedProperty *>(this), false);
+ if (bd)
+ bindingData().notifyObservers(this);
+ }
+
private:
};
diff --git a/src/corelib/kernel/qproperty_p.h b/src/corelib/kernel/qproperty_p.h
index f7792c1b5a..3b874ec27b 100644
--- a/src/corelib/kernel/qproperty_p.h
+++ b/src/corelib/kernel/qproperty_p.h
@@ -498,6 +498,15 @@ public:
return bd && bd->binding() != nullptr;
}
+ void markDirty() {
+ QBindingStorage *storage = qGetBindingStorage(owner());
+ auto *bd = storage->bindingData(this, false);
+ if (bd) {
+ bd->markDirty();
+ notify(bd);
+ }
+ }
+
QPropertyBinding<T> binding() const
{
auto *bd = qGetBindingStorage(owner())->bindingData(this);
diff --git a/src/corelib/kernel/qpropertyprivate.h b/src/corelib/kernel/qpropertyprivate.h
index f4b81ea40b..c53e15c2ec 100644
--- a/src/corelib/kernel/qpropertyprivate.h
+++ b/src/corelib/kernel/qpropertyprivate.h
@@ -248,6 +248,8 @@ public:
}
void evaluateIfDirty(const QUntypedPropertyData *property) const;
+ void markDirty();
+
void removeBinding()
{
if (hasBinding())
diff --git a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp
index 2ff32d0d1a..4559587afd 100644
--- a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp
+++ b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp
@@ -91,6 +91,7 @@ private slots:
void noFakeDependencies();
void bindablePropertyWithInitialization();
+ void markDirty();
};
void tst_QProperty::functorBinding()
@@ -1480,6 +1481,82 @@ void tst_QProperty::bindablePropertyWithInitialization()
QCOMPARE(tester.prop3().anotherValue, 20);
}
+class MarkDirtyTester : public QObject
+{
+ Q_OBJECT
+public:
+ Q_PROPERTY(int value1 READ value1 WRITE setValue1 BINDABLE bindableValue1)
+ Q_PROPERTY(int value2 READ value2 WRITE setValue1 BINDABLE bindableValue2)
+ Q_PROPERTY(int computed READ computed BINDABLE bindableComputed)
+
+ inline static int staticValue = 0;
+
+ int value1() const {return m_value1;}
+ void setValue1(int val) {m_value1 = val;}
+ QBindable<int> bindableValue1() {return { &m_value1 };}
+
+ int value2() const {return m_value2;}
+ void setValue2(int val) {m_value2 = val;}
+ QBindable<int> bindableValue2() {return { &m_value2 };}
+
+ int computed() const { return staticValue + m_value1; }
+ QBindable<int> bindableComputed() {return {&m_computed};}
+
+ void incrementStaticValue() {
+ ++staticValue;
+ m_computed.markDirty();
+ }
+
+ void markValue1Dirty() {
+ m_value1.markDirty();
+ }
+
+ void markValue2Dirty() {
+ m_value2.markDirty();
+ }
+private:
+ Q_OBJECT_BINDABLE_PROPERTY(MarkDirtyTester, int, m_value1, nullptr)
+ Q_OBJECT_COMPAT_PROPERTY(MarkDirtyTester, int, m_value2, &MarkDirtyTester::setValue2)
+ Q_OBJECT_COMPUTED_PROPERTY(MarkDirtyTester, int, m_computed, &MarkDirtyTester::computed)
+};
+
+void tst_QProperty::markDirty()
+{
+ {
+ QProperty<int> testProperty;
+ int changeCounter = 0;
+ auto handler = testProperty.onValueChanged([&](){++changeCounter;});
+ testProperty.markDirty();
+ QCOMPARE(changeCounter, 1);
+ }
+ {
+ MarkDirtyTester dirtyTester;
+ int computedChangeCounter = 0;
+ int value1ChangeCounter = 0;
+ auto handler = dirtyTester.bindableComputed().onValueChanged([&](){
+ computedChangeCounter++;
+ });
+ auto handler2 = dirtyTester.bindableValue1().onValueChanged([&](){
+ value1ChangeCounter++;
+ });
+ dirtyTester.incrementStaticValue();
+ QCOMPARE(computedChangeCounter, 1);
+ QCOMPARE(dirtyTester.computed(), 1);
+ dirtyTester.markValue1Dirty();
+ QCOMPARE(value1ChangeCounter, 1);
+ QCOMPARE(computedChangeCounter, 1);
+ }
+ {
+ MarkDirtyTester dirtyTester;
+ int changeCounter = 0;
+ auto handler = dirtyTester.bindableValue2().onValueChanged([&](){
+ changeCounter++;
+ });
+ dirtyTester.markValue2Dirty();
+ QCOMPARE(changeCounter, 1);
+ }
+}
+
QTEST_MAIN(tst_QProperty);
#include "tst_qproperty.moc"