diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp index 7c8477e31a9..6fe32e842ad 100644 --- a/src/corelib/kernel/qproperty.cpp +++ b/src/corelib/kernel/qproperty.cpp @@ -1567,7 +1567,6 @@ QString QPropertyBindingError::description() const owner is notified via the Callback function. */ - /*! \fn template template QObjectBindableProperty::QObjectBindableProperty(Functor &&f) @@ -1600,14 +1599,18 @@ QString QPropertyBindingError::description() const */ /*! - \fn template void QObjectBindableProperty::markDirty() + \fn template void QObjectBindableProperty::notify() - 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. + Programmatically signals a change of the property. Any binding which depend on it will + be notified, and if the property has a signal, it will be emitted. - \sa QProperty::markDirty() + This can be useful in combination with setValueBypassingBindings to defer signalling the change + until a class invariant has been restored. + + \note If this property has a binding (i.e. hasBinding() returns true), that binding is not reevaluated when + notify() is called. Any binding depending on this property is still reevaluated as usual. + + \sa Qt::beginProperytUpdateGroup(), setValueBypassingBindings() */ /*! diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h index a6cb7ada482..6cf8c6a34e0 100644 --- a/src/corelib/kernel/qproperty.h +++ b/src/corelib/kernel/qproperty.h @@ -962,6 +962,11 @@ public: notify(bd); } + void notify() { + auto *bd = qGetBindingStorage(owner())->bindingData(this); + notify(bd); + } + void setValue(rvalue_ref t) { auto *bd = qGetBindingStorage(owner())->bindingData(this); diff --git a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp index c4032764643..5197be3912e 100644 --- a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp +++ b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp @@ -82,6 +82,7 @@ private slots: void bindingValueReplacement(); void quntypedBindableApi(); void readonlyConstQBindable(); + void qobjectBindableManualNotify(); void testNewStuff(); void qobjectObservers(); @@ -1137,6 +1138,50 @@ public: Q_OBJECT_COMPAT_PROPERTY(MyQObject, int, compatData, &MyQObject::setCompat) }; + +void tst_QProperty::qobjectBindableManualNotify() +{ + // Given an object of type MyQObject, + MyQObject object; + // track its foo property's change count + auto bindable = object.bindableFoo(); + int fooChangeCount = 0; + auto changeHandler = bindable.onValueChanged([&](){++fooChangeCount;}); + // and how many changed signals it emits. + QSignalSpy fooChangedSpy(&object, &MyQObject::fooChanged); + + // If we bypass the bindings system, + object.fooData.setValueBypassingBindings(42); + // there is no change. + QCOMPARE(fooChangeCount, 0); + QCOMPARE(fooChangedSpy.count(), 0); + // Once we notify manually + object.fooData.notify(); + // observers are notified and the signal arrives. + QCOMPARE(fooChangeCount, 1); + QCOMPARE(fooChangedSpy.count(), 1); + + // If we set a binding + int i = 1; + object.fooData.setBinding([&](){return i;}); + // then the value changes + QCOMPARE(object.foo(), 1); + // and the change and signal count are incremented. + QCOMPARE(fooChangeCount, 2); + QCOMPARE(fooChangedSpy.count(), 2); + // Changing a non-property won't trigger any notification. + i = 2; + QCOMPARE(fooChangeCount, 2); + QCOMPARE(fooChangedSpy.count(), 2); + // Manually triggering the notification + object.fooData.notify(); + // increments the change count + QCOMPARE(fooChangeCount, 3); + QCOMPARE(fooChangedSpy.count(), 3); + // but doesn't actually cause a binding reevaluation. + QCOMPARE(object.foo(), 1); +} + void tst_QProperty::testNewStuff() { MyQObject testReadOnly;