summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qproperty.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/kernel/qproperty.cpp')
-rw-r--r--src/corelib/kernel/qproperty.cpp107
1 files changed, 78 insertions, 29 deletions
diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp
index 22422995fe..a94e3ad989 100644
--- a/src/corelib/kernel/qproperty.cpp
+++ b/src/corelib/kernel/qproperty.cpp
@@ -118,7 +118,7 @@ struct QPropertyDelayedNotifications
Change notifications are sent later with notify (following the logic of separating
binding updates and notifications used in non-deferred updates).
*/
- void evaluateBindings(int index, QBindingStatus *status) {
+ void evaluateBindings(PendingBindingObserverList &bindingObservers, qsizetype index, QBindingStatus *status) {
auto *delayed = delayedProperties + index;
auto *bindingData = delayed->originalBindingData;
if (!bindingData)
@@ -134,7 +134,7 @@ struct QPropertyDelayedNotifications
QPropertyBindingDataPointer bindingDataPointer{bindingData};
QPropertyObserverPointer observer = bindingDataPointer.firstObserver();
if (observer)
- observer.evaluateBindings(status);
+ observer.evaluateBindings(bindingObservers, status);
}
/*!
@@ -146,19 +146,19 @@ struct QPropertyDelayedNotifications
\li sends any pending notifications.
\endlist
*/
- void notify(int index) {
+ void notify(qsizetype index) {
auto *delayed = delayedProperties + index;
- auto *bindingData = delayed->originalBindingData;
- if (!bindingData)
+ if (delayed->d_ptr & QPropertyBindingData::BindingBit)
+ return; // already handled
+ if (!delayed->originalBindingData)
return;
-
delayed->originalBindingData = nullptr;
+
+ QPropertyObserverPointer observer { reinterpret_cast<QPropertyObserver *>(delayed->d_ptr & ~QPropertyBindingData::DelayedNotificationBit) };
delayed->d_ptr = 0;
- QPropertyBindingDataPointer bindingDataPointer{bindingData};
- QPropertyObserverPointer observer = bindingDataPointer.firstObserver();
if (observer)
- observer.notify(delayed->propertyData);
+ observer.notify<QPropertyObserverPointer::Notify::OnlyChangeHandlers>(delayed->propertyData);
}
};
@@ -213,17 +213,24 @@ void Qt::endPropertyUpdateGroup()
if (--data->ref)
return;
groupUpdateData = nullptr;
+ // ensures that bindings are kept alive until endPropertyUpdateGroup concludes
+ PendingBindingObserverList bindingObservers;
// update all delayed properties
auto start = data;
while (data) {
- for (int i = 0; i < data->used; ++i)
- data->evaluateBindings(i, status);
+ for (qsizetype i = 0; i < data->used; ++i)
+ data->evaluateBindings(bindingObservers, i, status);
data = data->next;
}
- // notify all delayed properties
+ // notify all delayed notifications from binding evaluation
+ for (const QBindingObserverPtr &observer: bindingObservers) {
+ QPropertyBindingPrivate *binding = observer.binding();
+ binding->notifyNonRecursive();
+ }
+ // do the same for properties which only have observers
data = start;
while (data) {
- for (int i = 0; i < data->used; ++i)
+ for (qsizetype i = 0; i < data->used; ++i)
data->notify(i);
auto *next = data->next;
delete data;
@@ -271,11 +278,11 @@ void QPropertyBindingPrivate::unlinkAndDeref()
destroyAndFreeMemory(this);
}
-void QPropertyBindingPrivate::evaluateRecursive(QBindingStatus *status)
+void QPropertyBindingPrivate::evaluateRecursive(PendingBindingObserverList &bindingObservers, QBindingStatus *status)
{
if (!status)
status = &bindingStatus;
- return evaluateRecursive_inline(status);
+ return evaluateRecursive_inline(bindingObservers, status);
}
void QPropertyBindingPrivate::notifyRecursive()
@@ -294,6 +301,31 @@ void QPropertyBindingPrivate::notifyRecursive()
updating = false;
}
+void QPropertyBindingPrivate::notifyNonRecursive(const PendingBindingObserverList &bindingObservers)
+{
+ notifyNonRecursive();
+ for (auto &&bindingObserver: bindingObservers) {
+ bindingObserver.binding()->notifyNonRecursive();
+ }
+}
+
+QPropertyBindingPrivate::NotificationState QPropertyBindingPrivate::notifyNonRecursive()
+{
+ if (!pendingNotify)
+ return Delayed;
+ pendingNotify = false;
+ Q_ASSERT(!updating);
+ updating = true;
+ if (firstObserver) {
+ firstObserver.noSelfDependencies(this);
+ firstObserver.notifyOnlyChangeHandler(propertyDataPtr);
+ }
+ if (hasStaticObserver)
+ staticObserverCallback(propertyDataPtr);
+ updating = false;
+ return Sent;
+}
+
/*!
Constructs a null QUntypedPropertyBinding.
@@ -415,6 +447,8 @@ QMetaType QUntypedPropertyBinding::valueMetaType() const
QPropertyBindingData::~QPropertyBindingData()
{
QPropertyBindingDataPointer d{this};
+ if (isNotificationDelayed())
+ proxyData()->originalBindingData = nullptr;
for (auto observer = d.firstObserver(); observer;) {
auto next = observer.nextObserver();
observer.unlink();
@@ -461,8 +495,9 @@ QUntypedPropertyBinding QPropertyBindingData::setBinding(const QUntypedPropertyB
newBindingRaw->prependObserver(observer);
newBindingRaw->setStaticObserver(staticObserverCallback, guardCallback);
- newBindingRaw->evaluateRecursive();
- newBindingRaw->notifyRecursive();
+ PendingBindingObserverList bindingObservers;
+ newBindingRaw->evaluateRecursive(bindingObservers);
+ newBindingRaw->notifyNonRecursive(bindingObservers);
} else if (observer) {
d.setObservers(observer.ptr);
} else {
@@ -565,18 +600,31 @@ void QPropertyBindingData::notifyObservers(QUntypedPropertyData *propertyDataPtr
return;
QPropertyBindingDataPointer d{this};
+ PendingBindingObserverList bindingObservers;
if (QPropertyObserverPointer observer = d.firstObserver()) {
- if (notifyObserver_helper(propertyDataPtr, observer, storage) == Evaluated) {
- // evaluateBindings() can trash the observers. We need to re-fetch here.
+ if (notifyObserver_helper(propertyDataPtr, storage, observer, bindingObservers) == Evaluated) {
+ /* evaluateBindings() can trash the observers. We need to re-fetch here.
+ "this" might also no longer be valid in case we have a QObjectBindableProperty
+ and consequently d isn't either (this happens when binding evaluation has
+ caused the binding storage to resize.
+ If storage is nullptr, then there is no dynamically resizable storage,
+ and we cannot run into the issue.
+ */
+ if (storage)
+ d = QPropertyBindingDataPointer {storage->bindingData(propertyDataPtr)};
if (QPropertyObserverPointer observer = d.firstObserver())
- observer.notify(propertyDataPtr);
+ observer.notifyOnlyChangeHandler(propertyDataPtr);
+ for (auto &&bindingObserver: bindingObservers)
+ bindingObserver.binding()->notifyNonRecursive();
}
}
}
-QPropertyBindingData::NotificationResult QPropertyBindingData::notifyObserver_helper(
- QUntypedPropertyData *propertyDataPtr, QPropertyObserverPointer observer,
- QBindingStorage *storage) const
+QPropertyBindingData::NotificationResult QPropertyBindingData::notifyObserver_helper
+(
+ QUntypedPropertyData *propertyDataPtr, QBindingStorage *storage,
+ QPropertyObserverPointer observer,
+ PendingBindingObserverList &bindingObservers) const
{
#ifdef QT_HAS_FAST_CURRENT_THREAD_ID
QBindingStatus *status = storage ? storage->bindingStatus : nullptr;
@@ -591,7 +639,7 @@ QPropertyBindingData::NotificationResult QPropertyBindingData::notifyObserver_he
return Delayed;
}
- observer.evaluateBindings(status);
+ observer.evaluateBindings(bindingObservers, status);
return Evaluated;
}
@@ -724,7 +772,7 @@ void QPropertyObserverPointer::noSelfDependencies(QPropertyBindingPrivate *bindi
}
#endif
-void QPropertyObserverPointer::evaluateBindings(QBindingStatus *status)
+void QPropertyObserverPointer::evaluateBindings(PendingBindingObserverList &bindingObservers, QBindingStatus *status)
{
Q_ASSERT(status);
auto observer = const_cast<QPropertyObserver*>(ptr);
@@ -733,9 +781,10 @@ void QPropertyObserverPointer::evaluateBindings(QBindingStatus *status)
QPropertyObserver *next = observer->next.data();
if (QPropertyObserver::ObserverTag(observer->next.tag()) == QPropertyObserver::ObserverNotifiesBinding) {
+ bindingObservers.push_back(observer);
auto bindingToEvaluate = observer->binding;
QPropertyObserverNodeProtector protector(observer);
- bindingToEvaluate->evaluateRecursive_inline(status);
+ bindingToEvaluate->evaluateRecursive_inline(bindingObservers, status);
next = protector.next();
}
@@ -1565,13 +1614,13 @@ QString QPropertyBindingError::description() const
have changed. Whenever a bindable property used in the callback changes,
this happens automatically. If the result of the callback might change
because of a change in a value which is not a bindable property,
- it is the developer's responsibility to call markDirty
+ it is the developer's responsibility to call \c notify
on the QObjectComputedProperty object.
This will inform dependent properties about the potential change.
- Note that calling markDirty might trigger change handlers in dependent
+ Note that calling \c notify might trigger change handlers in dependent
properties, which might in turn use the object the QObjectComputedProperty
- is a member of. So markDirty must not be called when in a transitional
+ is a member of. So \c notify must not be called when in a transitional
or invalid state.
QObjectComputedProperty is not suitable for use with a computation that depends