diff options
Diffstat (limited to 'src/corelib/kernel/qproperty.cpp')
-rw-r--r-- | src/corelib/kernel/qproperty.cpp | 107 |
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 |