diff options
Diffstat (limited to 'src/corelib/kernel/qproperty_p.h')
-rw-r--r-- | src/corelib/kernel/qproperty_p.h | 113 |
1 files changed, 99 insertions, 14 deletions
diff --git a/src/corelib/kernel/qproperty_p.h b/src/corelib/kernel/qproperty_p.h index a569c172c5..8e747b4f64 100644 --- a/src/corelib/kernel/qproperty_p.h +++ b/src/corelib/kernel/qproperty_p.h @@ -21,6 +21,7 @@ #include <qscopedpointer.h> #include <qscopedvaluerollback.h> #include <vector> +#include <QtCore/QVarLengthArray> QT_BEGIN_NAMESPACE @@ -29,6 +30,34 @@ namespace QtPrivate { struct QBindingStatusAccessToken {}; } + +/*! + \internal + Similar to \c QPropertyBindingPrivatePtr, but stores a + \c QPropertyObserver * linking to the QPropertyBindingPrivate* + instead of the QPropertyBindingPrivate* itself + */ +struct QBindingObserverPtr +{ +private: + QPropertyObserver *d = nullptr; +public: + QBindingObserverPtr() = default; + Q_DISABLE_COPY(QBindingObserverPtr); + void swap(QBindingObserverPtr &other) noexcept + { qt_ptr_swap(d, other.d); } + QBindingObserverPtr(QBindingObserverPtr &&other) : d(std::exchange(other.d, nullptr)) {} + QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QBindingObserverPtr); + + + inline QBindingObserverPtr(QPropertyObserver *observer); + inline ~QBindingObserverPtr(); + inline QPropertyBindingPrivate *binding() const; + inline QPropertyObserver *operator ->(); +}; + +using PendingBindingObserverList = QVarLengthArray<QBindingObserverPtr>; + // Keep all classes related to QProperty in one compilation unit. Performance of this code is crucial and // we need to allow the compiler to inline where it makes sense. @@ -52,6 +81,7 @@ struct QPropertyBindingDataPointer void Q_ALWAYS_INLINE addObserver(QPropertyObserver *observer); inline void setFirstObserver(QPropertyObserver *observer); inline QPropertyObserverPointer firstObserver() const; + static QPropertyProxyBindingData *proxyData(QtPrivate::QPropertyBindingData *ptr); inline int observerCount() const; @@ -106,19 +136,29 @@ struct QPropertyObserverPointer void setBindingToNotify_unsafe(QPropertyBindingPrivate *binding); void setChangeHandler(QPropertyObserver::ChangeHandler changeHandler); + enum class Notify {Everything, OnlyChangeHandlers}; + + template<Notify notifyPolicy = Notify::Everything> void notify(QUntypedPropertyData *propertyDataPtr); + void notifyOnlyChangeHandler(QUntypedPropertyData *propertyDataPtr); #ifndef QT_NO_DEBUG void noSelfDependencies(QPropertyBindingPrivate *binding); #else void noSelfDependencies(QPropertyBindingPrivate *) {} #endif - void evaluateBindings(QBindingStatus *status); + void evaluateBindings(PendingBindingObserverList &bindingObservers, QBindingStatus *status); void observeProperty(QPropertyBindingDataPointer property); explicit operator bool() const { return ptr != nullptr; } QPropertyObserverPointer nextObserver() const { return {ptr->next.data()}; } + QPropertyBindingPrivate *binding() const + { + Q_ASSERT(ptr->next.tag() == QPropertyObserver::ObserverNotifiesBinding); + return ptr->binding; + }; + private: void unlink_common() { @@ -321,10 +361,21 @@ public: void unlinkAndDeref(); - void evaluateRecursive(QBindingStatus *status = nullptr); - void Q_ALWAYS_INLINE evaluateRecursive_inline(QBindingStatus *status); + void evaluateRecursive(PendingBindingObserverList &bindingObservers, QBindingStatus *status = nullptr); + + // ### TODO: remove as soon as declarative no longer needs this overload + void evaluateRecursive() + { + PendingBindingObserverList bindingObservers; + evaluateRecursive(bindingObservers); + } + + void Q_ALWAYS_INLINE evaluateRecursive_inline(PendingBindingObserverList &bindingObservers, QBindingStatus *status); void notifyRecursive(); + void notifyNonRecursive(const PendingBindingObserverList &bindingObservers); + enum NotificationState : bool { Delayed, Sent }; + NotificationState notifyNonRecursive(); static QPropertyBindingPrivate *get(const QUntypedPropertyBinding &binding) { return static_cast<QPropertyBindingPrivate *>(binding.d.data()); } @@ -373,9 +424,9 @@ inline void QPropertyBindingDataPointer::fixupAfterMove(QtPrivate::QPropertyBind { auto &d = ptr->d_ref(); if (ptr->isNotificationDelayed()) { - QPropertyProxyBindingData *proxyData - = reinterpret_cast<QPropertyProxyBindingData*>(d & ~QtPrivate::QPropertyBindingData::BindingBit); - proxyData->originalBindingData = ptr; + QPropertyProxyBindingData *proxy = ptr->proxyData(); + Q_ASSERT(proxy); + proxy->originalBindingData = ptr; } // If QPropertyBindingData has been moved, and it has an observer // we have to adjust the firstObserver's prev pointer to point to @@ -393,6 +444,17 @@ inline QPropertyObserverPointer QPropertyBindingDataPointer::firstObserver() con return { reinterpret_cast<QPropertyObserver *>(ptr->d()) }; } +/*! + \internal + Returns the proxy data of \a ptr, or \c nullptr if \a ptr has no delayed notification + */ +inline QPropertyProxyBindingData *QPropertyBindingDataPointer::proxyData(QtPrivate::QPropertyBindingData *ptr) +{ + if (!ptr->isNotificationDelayed()) + return nullptr; + return ptr->proxyData(); +} + inline int QPropertyBindingDataPointer::observerCount() const { int count = 0; @@ -566,11 +628,14 @@ public: QPropertyBindingDataPointer d{bd}; if (QPropertyObserverPointer observer = d.firstObserver()) { if (!inBindingWrapper(storage)) { - if (bd->notifyObserver_helper(this, observer, storage) + PendingBindingObserverList bindingObservers; + if (bd->notifyObserver_helper(this, storage, observer, bindingObservers) == QtPrivate::QPropertyBindingData::Evaluated) { // evaluateBindings() can trash the observers. We need to re-fetch here. if (QPropertyObserverPointer observer = d.firstObserver()) - observer.notify(this); + observer.notifyOnlyChangeHandler(this); + for (auto&& bindingObserver: bindingObservers) + bindingObserver.binding()->notifyNonRecursive(); } } } @@ -727,7 +792,7 @@ struct QUntypedBindablePrivate } }; -inline void QPropertyBindingPrivate::evaluateRecursive_inline(QBindingStatus *status) +inline void QPropertyBindingPrivate::evaluateRecursive_inline(PendingBindingObserverList &bindingObservers, QBindingStatus *status) { if (updating) { error = QPropertyBindingError(QPropertyBindingError::BindingLoop); @@ -766,9 +831,10 @@ inline void QPropertyBindingPrivate::evaluateRecursive_inline(QBindingStatus *st return; firstObserver.noSelfDependencies(this); - firstObserver.evaluateBindings(status); + firstObserver.evaluateBindings(bindingObservers, status); } +template<QPropertyObserverPointer::Notify notifyPolicy> inline void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataPtr) { auto observer = const_cast<QPropertyObserver*>(ptr); @@ -808,10 +874,12 @@ inline void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataP } case QPropertyObserver::ObserverNotifiesBinding: { - auto bindingToNotify = observer->binding; - QPropertyObserverNodeProtector protector(observer); - bindingToNotify->notifyRecursive(); - next = protector.next(); + if constexpr (notifyPolicy == Notify::Everything) { + auto bindingToNotify = observer->binding; + QPropertyObserverNodeProtector protector(observer); + bindingToNotify->notifyRecursive(); + next = protector.next(); + } break; } case QPropertyObserver::ObserverIsPlaceholder: @@ -825,12 +893,29 @@ inline void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataP } } +inline void QPropertyObserverPointer::notifyOnlyChangeHandler(QUntypedPropertyData *propertyDataPtr) +{ + notify<Notify::OnlyChangeHandlers>(propertyDataPtr); +} + inline QPropertyObserverNodeProtector::~QPropertyObserverNodeProtector() { QPropertyObserverPointer d{static_cast<QPropertyObserver *>(&m_placeHolder)}; d.unlink_fast(); } +QBindingObserverPtr::QBindingObserverPtr(QPropertyObserver *observer) : d(observer) +{ + Q_ASSERT(d); + QPropertyObserverPointer{d}.binding()->addRef(); +} + +QBindingObserverPtr::~QBindingObserverPtr() { if (d) QPropertyObserverPointer{d}.binding()->deref(); } + +QPropertyBindingPrivate *QBindingObserverPtr::binding() const { return QPropertyObserverPointer{d}.binding(); } + +QPropertyObserver *QBindingObserverPtr::operator->() { return d; } + QT_END_NAMESPACE #endif // QPROPERTY_P_H |