diff options
Diffstat (limited to 'src/corelib/kernel/qproperty_p.h')
-rw-r--r-- | src/corelib/kernel/qproperty_p.h | 167 |
1 files changed, 129 insertions, 38 deletions
diff --git a/src/corelib/kernel/qproperty_p.h b/src/corelib/kernel/qproperty_p.h index aa5c05a9e9..8ae6664a2b 100644 --- a/src/corelib/kernel/qproperty_p.h +++ b/src/corelib/kernel/qproperty_p.h @@ -18,8 +18,10 @@ #include <private/qglobal_p.h> #include <qproperty.h> +#include <qmetaobject.h> #include <qscopedpointer.h> #include <qscopedvaluerollback.h> +#include <qvariant.h> #include <vector> #include <QtCore/QVarLengthArray> @@ -81,6 +83,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; @@ -91,11 +94,12 @@ struct QPropertyBindingDataPointer } }; -struct [[nodiscard]] QPropertyObserverNodeProtector +struct QPropertyObserverNodeProtector { Q_DISABLE_COPY_MOVE(QPropertyObserverNodeProtector) QPropertyObserverBase m_placeHolder; + Q_NODISCARD_CTOR QPropertyObserverNodeProtector(QPropertyObserver *observer) { // insert m_placeholder after observer into the linked list @@ -121,25 +125,37 @@ struct QPropertyObserverPointer void unlink() { unlink_common(); +#if QT_DEPRECATED_SINCE(6, 6) + QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED if (ptr->next.tag() == QPropertyObserver::ObserverIsAlias) ptr->aliasData = nullptr; + QT_WARNING_POP +#endif } void unlink_fast() { +#if QT_DEPRECATED_SINCE(6, 6) + QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsAlias); + QT_WARNING_POP +#endif unlink_common(); } - void setBindingToNotify(QPropertyBindingPrivate *binding); + void setBindingToNotify(QPropertyBindingPrivate *binding) + { + Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsPlaceholder); + ptr->binding = binding; + ptr->next.setTag(QPropertyObserver::ObserverNotifiesBinding); + } + 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 @@ -156,7 +172,7 @@ struct QPropertyObserverPointer { Q_ASSERT(ptr->next.tag() == QPropertyObserver::ObserverNotifiesBinding); return ptr->binding; - }; + } private: void unlink_common() @@ -190,6 +206,7 @@ struct BindingEvaluationState QPropertyBindingPrivate *binding; BindingEvaluationState *previousState = nullptr; BindingEvaluationState **currentState = nullptr; + QVarLengthArray<const QPropertyBindingData *, 8> alreadyCaptureProperties; }; /*! @@ -214,6 +231,33 @@ struct CompatPropertySafePoint QtPrivate::BindingEvaluationState *bindingState = nullptr; }; +/*! + * \internal + * While the regular QProperty notification for a compat property runs we + * don't want to have any currentCompatProperty set. This would be a _different_ + * one than the one we are current evaluating. Therefore it's misleading and + * prevents the registering of actual dependencies. + */ +struct CurrentCompatPropertyThief +{ + Q_DISABLE_COPY_MOVE(CurrentCompatPropertyThief) +public: + CurrentCompatPropertyThief(QBindingStatus *status) + : status(&status->currentCompatProperty) + , stolen(std::exchange(status->currentCompatProperty, nullptr)) + { + } + + ~CurrentCompatPropertyThief() + { + *status = stolen; + } + +private: + CompatPropertySafePoint **status = nullptr; + CompatPropertySafePoint *stolen = nullptr; +}; + } class Q_CORE_EXPORT QPropertyBindingPrivate : public QtPrivate::RefCounted @@ -362,16 +406,8 @@ public: bool evaluateRecursive(PendingBindingObserverList &bindingObservers, QBindingStatus *status = nullptr); - // ### TODO: remove as soon as declarative no longer needs this overload - void evaluateRecursive() - { - PendingBindingObserverList bindingObservers; - evaluateRecursive(bindingObservers); - } - bool Q_ALWAYS_INLINE evaluateRecursive_inline(PendingBindingObserverList &bindingObservers, QBindingStatus *status); - void notifyRecursive(); void notifyNonRecursive(const PendingBindingObserverList &bindingObservers); enum NotificationState : bool { Delayed, Sent }; NotificationState notifyNonRecursive(); @@ -423,9 +459,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 @@ -443,6 +479,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; @@ -479,13 +526,16 @@ class QObjectCompatProperty : public QPropertyData<T> static bool bindingWrapper(QMetaType type, QUntypedPropertyData *dataPtr, QtPrivate::QPropertyBindingFunction binding) { auto *thisData = static_cast<ThisType *>(dataPtr); + QBindingStorage *storage = qGetBindingStorage(thisData->owner()); QPropertyData<T> copy; - binding.vtable->call(type, ©, binding.functor); - if constexpr (QTypeTraits::has_operator_equal_v<T>) - if (copy.valueBypassingBindings() == thisData->valueBypassingBindings()) - return false; + { + QtPrivate::CurrentCompatPropertyThief thief(storage->bindingStatus); + binding.vtable->call(type, ©, binding.functor); + if constexpr (QTypeTraits::has_operator_equal_v<T>) + if (copy.valueBypassingBindings() == thisData->valueBypassingBindings()) + return false; + } // ensure value and setValue know we're currently evaluating our binding - QBindingStorage *storage = qGetBindingStorage(thisData->owner()); QtPrivate::CompatPropertySafePoint guardThis(storage->bindingStatus, thisData); (thisData->owner()->*Setter)(copy.valueBypassingBindings()); return true; @@ -577,7 +627,7 @@ public: return true; } -#ifndef Q_CLANG_QDOC +#ifndef Q_QDOC template <typename Functor> QPropertyBinding<T> setBinding(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION, @@ -621,7 +671,7 @@ public: == QtPrivate::QPropertyBindingData::Evaluated) { // evaluateBindings() can trash the observers. We need to re-fetch here. if (QPropertyObserverPointer observer = d.firstObserver()) - observer.notifyOnlyChangeHandler(this); + observer.notify(this); for (auto&& bindingObserver: bindingObservers) bindingObserver.binding()->notifyNonRecursive(); } @@ -823,7 +873,14 @@ inline bool QPropertyBindingPrivate::evaluateRecursive_inline(PendingBindingObse return true; } -template<QPropertyObserverPointer::Notify notifyPolicy> +/*! + \internal + + Walks through the list of property observers, and calls any ChangeHandler + found there. + It doesn't do anything with bindings, which are only handled in + QPropertyBindingPrivate::evaluateRecursive. + */ inline void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataPtr) { auto observer = const_cast<QPropertyObserver*>(ptr); @@ -862,31 +919,22 @@ inline void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataP break; } case QPropertyObserver::ObserverNotifiesBinding: - { - if constexpr (notifyPolicy == Notify::Everything) { - auto bindingToNotify = observer->binding; - QPropertyObserverNodeProtector protector(observer); - bindingToNotify->notifyRecursive(); - next = protector.next(); - } break; - } case QPropertyObserver::ObserverIsPlaceholder: // recursion is already properly handled somewhere else break; +#if QT_DEPRECATED_SINCE(6, 6) + QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED case QPropertyObserver::ObserverIsAlias: break; + QT_WARNING_POP +#endif default: Q_UNREACHABLE(); } observer = next; } } -inline void QPropertyObserverPointer::notifyOnlyChangeHandler(QUntypedPropertyData *propertyDataPtr) -{ - notify<Notify::OnlyChangeHandlers>(propertyDataPtr); -} - inline QPropertyObserverNodeProtector::~QPropertyObserverNodeProtector() { QPropertyObserverPointer d{static_cast<QPropertyObserver *>(&m_placeHolder)}; @@ -899,12 +947,55 @@ QBindingObserverPtr::QBindingObserverPtr(QPropertyObserver *observer) noexcept : QPropertyObserverPointer{d}.binding()->addRef(); } -QBindingObserverPtr::~QBindingObserverPtr() { if (d) QPropertyObserverPointer{d}.binding()->deref(); } +QBindingObserverPtr::~QBindingObserverPtr() +{ + if (!d) + return; + + QPropertyBindingPrivate *bindingPrivate = binding(); + if (!bindingPrivate->deref()) + QPropertyBindingPrivate::destroyAndFreeMemory(bindingPrivate); +} QPropertyBindingPrivate *QBindingObserverPtr::binding() const noexcept { return QPropertyObserverPointer{d}.binding(); } QPropertyObserver *QBindingObserverPtr::operator->() { return d; } +namespace QtPrivate { +class QPropertyAdaptorSlotObject : public QUntypedPropertyData, public QSlotObjectBase +{ + QPropertyBindingData bindingData_; + QObject *obj; + QMetaProperty metaProperty_; + +#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) + static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret); +#else + static void impl(QSlotObjectBase *this_, QObject *r, void **a, int which, bool *ret); +#endif + + QPropertyAdaptorSlotObject(QObject *o, const QMetaProperty& p); + +public: + static QPropertyAdaptorSlotObject *cast(QSlotObjectBase *ptr, int propertyIndex) + { + if (ptr->isImpl(&QPropertyAdaptorSlotObject::impl)) { + auto p = static_cast<QPropertyAdaptorSlotObject *>(ptr); + if (p->metaProperty_.propertyIndex() == propertyIndex) + return p; + } + return nullptr; + } + + inline const QPropertyBindingData &bindingData() const { return bindingData_; } + inline QPropertyBindingData &bindingData() { return bindingData_; } + inline QObject *object() const { return obj; } + inline const QMetaProperty &metaProperty() const { return metaProperty_; } + + friend class QT_PREPEND_NAMESPACE(QUntypedBindable); +}; +} + QT_END_NAMESPACE #endif // QPROPERTY_P_H |