diff options
Diffstat (limited to 'src/corelib/kernel/qproperty.h')
-rw-r--r-- | src/corelib/kernel/qproperty.h | 515 |
1 files changed, 352 insertions, 163 deletions
diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h index f853eb8634..0373867a66 100644 --- a/src/corelib/kernel/qproperty.h +++ b/src/corelib/kernel/qproperty.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPROPERTY_H #define QPROPERTY_H @@ -43,26 +7,63 @@ #include <QtCore/qglobal.h> #include <QtCore/qshareddata.h> #include <QtCore/qstring.h> -#include <functional> +#include <QtCore/qbindingstorage.h> + #include <type_traits> -#include <variant> #include <QtCore/qpropertyprivate.h> -#if __has_include(<source_location>) && __cplusplus >= 202002L && !defined(Q_CLANG_QDOC) -#include <experimental/source_location> +#if __has_include(<source_location>) && __cplusplus >= 202002L && !defined(Q_QDOC) +#include <source_location> +#if defined(__cpp_lib_source_location) +#define QT_SOURCE_LOCATION_NAMESPACE std #define QT_PROPERTY_COLLECT_BINDING_LOCATION -#define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation(std::source_location::current()) -#elif __has_include(<experimental/source_location>) && __cplusplus >= 201703L && !defined(Q_CLANG_QDOC) +#if defined(Q_CC_MSVC) +/* MSVC runs into an issue with constexpr with source location (error C7595) + so use the factory function as a workaround */ +# define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation::fromStdSourceLocation(std::source_location::current()) +#else +/* some versions of gcc in turn run into + expression ‘std::source_location::current()’ is not a constant expression + so don't use the workaround there */ +# define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation(std::source_location::current()) +#endif +#endif +#endif + +#if __has_include(<experimental/source_location>) && !defined(Q_QDOC) #include <experimental/source_location> +#if !defined(QT_PROPERTY_COLLECT_BINDING_LOCATION) +#if defined(__cpp_lib_experimental_source_location) +#define QT_SOURCE_LOCATION_NAMESPACE std::experimental #define QT_PROPERTY_COLLECT_BINDING_LOCATION #define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation(std::experimental::source_location::current()) -#else +#endif // defined(__cpp_lib_experimental_source_location) +#endif +#endif + +#if !defined(QT_PROPERTY_COLLECT_BINDING_LOCATION) #define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation() #endif QT_BEGIN_NAMESPACE +namespace Qt { +Q_CORE_EXPORT void beginPropertyUpdateGroup(); +Q_CORE_EXPORT void endPropertyUpdateGroup(); +} + +class QScopedPropertyUpdateGroup +{ + Q_DISABLE_COPY_MOVE(QScopedPropertyUpdateGroup) +public: + Q_NODISCARD_CTOR + QScopedPropertyUpdateGroup() + { Qt::beginPropertyUpdateGroup(); } + ~QScopedPropertyUpdateGroup() noexcept(false) + { Qt::endPropertyUpdateGroup(); } +}; + template <typename T> class QPropertyData : public QUntypedPropertyData { @@ -89,6 +90,7 @@ public: void setValueBypassingBindings(rvalue_ref v) { val = std::move(v); } }; +// ### Qt 7: un-export struct Q_CORE_EXPORT QPropertyBindingSourceLocation { const char *fileName = nullptr; @@ -96,8 +98,23 @@ struct Q_CORE_EXPORT QPropertyBindingSourceLocation quint32 line = 0; quint32 column = 0; QPropertyBindingSourceLocation() = default; -#ifdef QT_PROPERTY_COLLECT_BINDING_LOCATION - QPropertyBindingSourceLocation(const std::experimental::source_location &cppLocation) +#ifdef __cpp_lib_source_location + constexpr QPropertyBindingSourceLocation(const std::source_location &cppLocation) + { + fileName = cppLocation.file_name(); + functionName = cppLocation.function_name(); + line = cppLocation.line(); + column = cppLocation.column(); + } + QT_POST_CXX17_API_IN_EXPORTED_CLASS + static consteval QPropertyBindingSourceLocation + fromStdSourceLocation(const std::source_location &cppLocation) + { + return cppLocation; + } +#endif +#ifdef __cpp_lib_experimental_source_location + constexpr QPropertyBindingSourceLocation(const std::experimental::source_location &cppLocation) { fileName = cppLocation.file_name(); functionName = cppLocation.function_name(); @@ -210,13 +227,16 @@ public: enum ObserverTag { ObserverNotifiesBinding, // observer was installed to notify bindings that obsverved property changed ObserverNotifiesChangeHandler, // observer is a change handler, which runs on every change - ObserverNotifiesAlias, // used for QPropertyAlias - ObserverIsPlaceholder // the observer before this one is currently evaluated in QPropertyObserver::notifyObservers. + ObserverIsPlaceholder, // the observer before this one is currently evaluated in QPropertyObserver::notifyObservers. +#if QT_DEPRECATED_SINCE(6, 6) + ObserverIsAlias QT_DEPRECATED_VERSION_X_6_6("Use QProperty and add a binding to the target.") +#endif }; protected: using ChangeHandler = void (*)(QPropertyObserver*, QUntypedPropertyData *); private: + friend struct QPropertyDelayedNotifications; friend struct QPropertyObserverNodeProtector; friend class QPropertyObserver; friend struct QPropertyObserverPointer; @@ -229,9 +249,9 @@ private: QtPrivate::QTagPreservingPointerToPointer<QPropertyObserver, ObserverTag> prev; union { - QPropertyBindingPrivate *bindingToMarkDirty = nullptr; + QPropertyBindingPrivate *binding = nullptr; ChangeHandler changeHandler; - QUntypedPropertyData *aliasedPropertyData; + QUntypedPropertyData *aliasData; }; }; @@ -243,18 +263,21 @@ public: QPropertyObserver &operator=(QPropertyObserver &&other) noexcept; ~QPropertyObserver(); - template<typename Property, typename = typename Property::InheritsQUntypedPropertyData> + template <typename Property, QtPrivate::IsUntypedPropertyData<Property> = true> void setSource(const Property &property) { setSource(property.bindingData()); } void setSource(const QtPrivate::QPropertyBindingData &property); protected: QPropertyObserver(ChangeHandler changeHandler); +#if QT_DEPRECATED_SINCE(6, 6) + QT_DEPRECATED_VERSION_X_6_6("This constructor was only meant for internal use. Use QProperty and add a binding to the target.") QPropertyObserver(QUntypedPropertyData *aliasedPropertyPtr); +#endif QUntypedPropertyData *aliasedProperty() const { - return aliasedPropertyData; + return aliasData; } private: @@ -265,10 +288,11 @@ private: }; template <typename Functor> -class [[nodiscard]] QPropertyChangeHandler : public QPropertyObserver +class QPropertyChangeHandler : public QPropertyObserver { Functor m_handler; public: + Q_NODISCARD_CTOR QPropertyChangeHandler(Functor handler) : QPropertyObserver([](QPropertyObserver *self, QUntypedPropertyData *) { auto This = static_cast<QPropertyChangeHandler<Functor>*>(self); @@ -278,7 +302,8 @@ public: { } - template<typename Property, typename = typename Property::InheritsQUntypedPropertyData> + template <typename Property, QtPrivate::IsUntypedPropertyData<Property> = true> + Q_NODISCARD_CTOR QPropertyChangeHandler(const Property &property, Functor handler) : QPropertyObserver([](QPropertyObserver *self, QUntypedPropertyData *) { auto This = static_cast<QPropertyChangeHandler<Functor>*>(self); @@ -290,6 +315,37 @@ public: } }; +class QPropertyNotifier : public QPropertyObserver +{ + std::function<void()> m_handler; +public: + Q_NODISCARD_CTOR + QPropertyNotifier() = default; + template<typename Functor> + Q_NODISCARD_CTOR + QPropertyNotifier(Functor handler) + : QPropertyObserver([](QPropertyObserver *self, QUntypedPropertyData *) { + auto This = static_cast<QPropertyNotifier *>(self); + This->m_handler(); + }) + , m_handler(handler) + { + } + + template <typename Functor, typename Property, + QtPrivate::IsUntypedPropertyData<Property> = true> + Q_NODISCARD_CTOR + QPropertyNotifier(const Property &property, Functor handler) + : QPropertyObserver([](QPropertyObserver *self, QUntypedPropertyData *) { + auto This = static_cast<QPropertyNotifier *>(self); + This->m_handler(); + }) + , m_handler(handler) + { + setSource(property); + } +}; + template <typename T> class QProperty : public QPropertyData<T> { @@ -315,7 +371,7 @@ public: explicit QProperty(const QPropertyBinding<T> &binding) : QProperty() { setBinding(binding); } -#ifndef Q_CLANG_QDOC +#ifndef Q_QDOC template <typename Functor> explicit QProperty(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION, typename std::enable_if_t<std::is_invocable_r_v<T, Functor&>> * = nullptr) @@ -329,8 +385,6 @@ public: parameter_type value() const { - if (d.hasBinding()) - d.evaluateIfDirty(this); d.registerWithCurrentlyEvaluatingBinding(); return this->val; } @@ -389,9 +443,7 @@ public: QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding) { - QPropertyBinding<T> oldBinding(d.setBinding(newBinding, this)); - notify(); - return oldBinding; + return QPropertyBinding<T>(d.setBinding(newBinding, this)); } bool setBinding(const QUntypedPropertyBinding &newBinding) @@ -402,12 +454,7 @@ public: return true; } - void markDirty() { - d.markDirty(); - notify(); - } - -#ifndef Q_CLANG_QDOC +#ifndef Q_QDOC template <typename Functor> QPropertyBinding<T> setBinding(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION, @@ -447,6 +494,13 @@ public: return onValueChanged(f); } + template<typename Functor> + QPropertyNotifier addNotifier(Functor f) + { + static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters"); + return QPropertyNotifier(*this, f); + } + const QtPrivate::QPropertyBindingData &bindingData() const { return d; } private: void notify() @@ -487,6 +541,8 @@ struct QBindableInterface MakeBinding makeBinding; SetObserver setObserver; GetMetaType metaType; + + static constexpr quintptr MetaTypeAccessorFlag = 0x1; }; template<typename Property, typename = void> @@ -556,8 +612,72 @@ public: } +namespace QtPrivate { +// used in Q(Untyped)Bindable to print warnings about various binding errors +namespace BindableWarnings { +enum Reason { InvalidInterface, NonBindableInterface, ReadOnlyInterface }; +Q_CORE_EXPORT void printUnsuitableBindableWarning(QAnyStringView prefix, Reason reason); +Q_CORE_EXPORT void printMetaTypeMismatch(QMetaType actual, QMetaType expected); +} + +namespace PropertyAdaptorSlotObjectHelpers { +Q_CORE_EXPORT void getter(const QUntypedPropertyData *d, void *value); +Q_CORE_EXPORT void setter(QUntypedPropertyData *d, const void *value); +Q_CORE_EXPORT QUntypedPropertyBinding getBinding(const QUntypedPropertyData *d); +Q_CORE_EXPORT bool bindingWrapper(QMetaType type, QUntypedPropertyData *d, + QtPrivate::QPropertyBindingFunction binding, + QUntypedPropertyData *temp, void *value); +Q_CORE_EXPORT QUntypedPropertyBinding setBinding(QUntypedPropertyData *d, + const QUntypedPropertyBinding &binding, + QPropertyBindingWrapper wrapper); +Q_CORE_EXPORT void setObserver(const QUntypedPropertyData *d, QPropertyObserver *observer); + +template<typename T> +bool bindingWrapper(QMetaType type, QUntypedPropertyData *d, + QtPrivate::QPropertyBindingFunction binding) +{ + struct Data : QPropertyData<T> + { + void *data() { return &this->val; } + } temp; + return bindingWrapper(type, d, binding, &temp, temp.data()); +} + +template<typename T> +QUntypedPropertyBinding setBinding(QUntypedPropertyData *d, const QUntypedPropertyBinding &binding) +{ + return setBinding(d, binding, &bindingWrapper<T>); +} + +template<typename T> +QUntypedPropertyBinding makeBinding(const QUntypedPropertyData *d, + const QPropertyBindingSourceLocation &location) +{ + return Qt::makePropertyBinding( + [d]() -> T { + T r; + getter(d, &r); + return r; + }, + location); +} + +template<class T> +inline constexpr QBindableInterface iface = { + &getter, + &setter, + &getBinding, + &setBinding<T>, + &makeBinding<T>, + &setObserver, + &QMetaType::fromType<T>, +}; +} +} + class QUntypedBindable { + friend struct QUntypedBindablePrivate; // allows access to internal data protected: QUntypedPropertyData *data = nullptr; const QtPrivate::QBindableInterface *iface = nullptr; @@ -565,6 +685,9 @@ protected: : data(d), iface(i) {} + Q_CORE_EXPORT explicit QUntypedBindable(QObject* obj, const QMetaProperty &property, const QtPrivate::QBindableInterface *i); + Q_CORE_EXPORT explicit QUntypedBindable(QObject* obj, const char* property, const QtPrivate::QBindableInterface *i); + public: constexpr QUntypedBindable() = default; template<typename Property> @@ -577,18 +700,39 @@ public: bool isBindable() const { return iface && iface->getBinding; } bool isReadOnly() const { return !(iface && iface->setBinding && iface->setObserver); } - QUntypedPropertyBinding makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) + QUntypedPropertyBinding makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) const { return iface ? iface->makeBinding(data, location) : QUntypedPropertyBinding(); } - void observe(QPropertyObserver *observer) + + QUntypedPropertyBinding takeBinding() + { + if (!iface) + return QUntypedPropertyBinding {}; + // We do not have a dedicated takeBinding function pointer in the interface + // therefore we synthesize takeBinding by retrieving the binding with binding + // and calling setBinding with a default constructed QUntypedPropertyBinding + // afterwards. + if (!(iface->getBinding && iface->setBinding)) + return QUntypedPropertyBinding {}; + QUntypedPropertyBinding binding = iface->getBinding(data); + iface->setBinding(data, QUntypedPropertyBinding{}); + return binding; + } + + void observe(QPropertyObserver *observer) const { if (iface) iface->setObserver(data, observer); +#ifndef QT_NO_DEBUG + else + QtPrivate::BindableWarnings::printUnsuitableBindableWarning("observe:", + QtPrivate::BindableWarnings::InvalidInterface); +#endif } template<typename Functor> - QPropertyChangeHandler<Functor> onValueChanged(Functor f) + QPropertyChangeHandler<Functor> onValueChanged(Functor f) const { QPropertyChangeHandler<Functor> handler(f); observe(&handler); @@ -596,24 +740,47 @@ public: } template<typename Functor> - QPropertyChangeHandler<Functor> subscribe(Functor f) + QPropertyChangeHandler<Functor> subscribe(Functor f) const { f(); return onValueChanged(f); } + template<typename Functor> + QPropertyNotifier addNotifier(Functor f) + { + QPropertyNotifier handler(f); + observe(&handler); + return handler; + } + QUntypedPropertyBinding binding() const { - if (!iface->getBinding) + if (!isBindable()) { +#ifndef QT_NO_DEBUG + QtPrivate::BindableWarnings::printUnsuitableBindableWarning("binding: ", + QtPrivate::BindableWarnings::NonBindableInterface); +#endif return QUntypedPropertyBinding(); + } return iface->getBinding(data); } bool setBinding(const QUntypedPropertyBinding &binding) { - if (!iface->setBinding) + if (isReadOnly()) { +#ifndef QT_NO_DEBUG + const auto errorType = iface ? QtPrivate::BindableWarnings::ReadOnlyInterface : + QtPrivate::BindableWarnings::InvalidInterface; + QtPrivate::BindableWarnings::printUnsuitableBindableWarning("setBinding: Could not set binding via bindable interface.", errorType); +#endif return false; - if (!binding.isNull() && binding.valueMetaType() != iface->metaType()) + } + if (!binding.isNull() && binding.valueMetaType() != metaType()) { +#ifndef QT_NO_DEBUG + QtPrivate::BindableWarnings::printMetaTypeMismatch(metaType(), binding.valueMetaType()); +#endif return false; + } iface->setBinding(data, binding); return true; } @@ -622,6 +789,21 @@ public: return !binding().isNull(); } + QMetaType metaType() const + { + if (!(iface && data)) + return QMetaType(); + if (iface->metaType) + return iface->metaType(); + // ### Qt 7: Change the metatype function to take data as its argument + // special casing for QML's proxy bindable: allow multiplexing in the getter + // function to retrieve the metatype from data + Q_ASSERT(iface->getter); + QMetaType result; + iface->getter(data, reinterpret_cast<void *>(quintptr(&result) | QtPrivate::QBindableInterface::MetaTypeAccessorFlag)); + return result; + } + }; template<typename T> @@ -636,13 +818,19 @@ public: using QUntypedBindable::QUntypedBindable; explicit QBindable(const QUntypedBindable &b) : QUntypedBindable(b) { - if (iface && iface->metaType() != QMetaType::fromType<T>()) { + if (iface && metaType() != QMetaType::fromType<T>()) { data = nullptr; iface = nullptr; } } - QPropertyBinding<T> makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) + explicit QBindable(QObject *obj, const QMetaProperty &property) + : QUntypedBindable(obj, property, &QtPrivate::PropertyAdaptorSlotObjectHelpers::iface<T>) {} + + explicit QBindable(QObject *obj, const char *property) + : QUntypedBindable(obj, property, &QtPrivate::PropertyAdaptorSlotObjectHelpers::iface<T>) {} + + QPropertyBinding<T> makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) const { return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::makeBinding(location)); } @@ -650,13 +838,28 @@ public: { return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::binding()); } + + QPropertyBinding<T> takeBinding() + { + return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::takeBinding()); + } + using QUntypedBindable::setBinding; QPropertyBinding<T> setBinding(const QPropertyBinding<T> &binding) { - Q_ASSERT(!iface || binding.isNull() || binding.valueMetaType() == iface->metaType()); - return (iface && iface->setBinding) ? static_cast<QPropertyBinding<T> &&>(iface->setBinding(data, binding)) : QPropertyBinding<T>(); + Q_ASSERT(!iface || binding.isNull() || binding.valueMetaType() == metaType()); + + if (iface && iface->setBinding) + return static_cast<QPropertyBinding<T> &&>(iface->setBinding(data, binding)); +#ifndef QT_NO_DEBUG + if (!iface) + QtPrivate::BindableWarnings::printUnsuitableBindableWarning("setBinding", QtPrivate::BindableWarnings::InvalidInterface); + else + QtPrivate::BindableWarnings::printUnsuitableBindableWarning("setBinding: Could not set binding via bindable interface.", QtPrivate::BindableWarnings::ReadOnlyInterface); +#endif + return QPropertyBinding<T>(); } -#ifndef Q_CLANG_QDOC +#ifndef Q_QDOC template <typename Functor> QPropertyBinding<T> setBinding(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION, @@ -686,13 +889,16 @@ public: } }; +#if QT_DEPRECATED_SINCE(6, 6) template<typename T> -class QPropertyAlias : public QPropertyObserver +class QT_DEPRECATED_VERSION_X_6_6("Class was only meant for internal use, use a QProperty and add a binding to the target") +QPropertyAlias : public QPropertyObserver { Q_DISABLE_COPY_MOVE(QPropertyAlias) const QtPrivate::QBindableInterface *iface = nullptr; public: + QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED QPropertyAlias(QProperty<T> *property) : QPropertyObserver(property), iface(&QtPrivate::QBindableInterfaceForProperty<QProperty<T>>::iface) @@ -701,7 +907,7 @@ public: iface->setObserver(aliasedProperty(), this); } - template<typename Property, typename = typename Property::InheritsQUntypedPropertyData> + template <typename Property, QtPrivate::IsUntypedPropertyData<Property> = true> QPropertyAlias(Property *property) : QPropertyObserver(property), iface(&QtPrivate::QBindableInterfaceForProperty<Property>::iface) @@ -758,7 +964,7 @@ public: return QBindable<T>(aliasedProperty(), iface).setBinding(newBinding); } -#ifndef Q_CLANG_QDOC +#ifndef Q_QDOC template <typename Functor> QPropertyBinding<T> setBinding(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION, @@ -798,68 +1004,26 @@ public: return QBindable<T>(aliasedProperty(), iface).subscribe(f); } - bool isValid() const + template<typename Functor> + QPropertyNotifier addNotifier(Functor f) { - return aliasedProperty() != nullptr; + return QBindable<T>(aliasedProperty(), iface).addNotifier(f); } -}; - -namespace QtPrivate { - -struct BindingEvaluationState; -struct CompatPropertySafePoint; -} - -struct QBindingStatus -{ - QtPrivate::BindingEvaluationState *currentlyEvaluatingBinding = nullptr; - QtPrivate::CompatPropertySafePoint *currentCompatProperty = nullptr; -}; - -struct QBindingStorageData; -class Q_CORE_EXPORT QBindingStorage -{ - mutable QBindingStorageData *d = nullptr; - QBindingStatus *bindingStatus = nullptr; - - template<typename Class, typename T, auto Offset, auto Setter> - friend class QObjectCompatProperty; -public: - QBindingStorage(); - ~QBindingStorage(); - - bool isEmpty() { return !d; } - void maybeUpdateBindingAndRegister(const QUntypedPropertyData *data) const - { - if (!d && !bindingStatus->currentlyEvaluatingBinding) - return; - maybeUpdateBindingAndRegister_helper(data); - } - QtPrivate::QPropertyBindingData *bindingData(const QUntypedPropertyData *data) const - { - if (!d) - return nullptr; - return bindingData_helper(data); - } - QtPrivate::QPropertyBindingData *bindingData(QUntypedPropertyData *data, bool create) + bool isValid() const { - if (!d && !create) - return nullptr; - return bindingData_helper(data, create); + return aliasedProperty() != nullptr; } -private: - void maybeUpdateBindingAndRegister_helper(const QUntypedPropertyData *data) const; - QtPrivate::QPropertyBindingData *bindingData_helper(const QUntypedPropertyData *data) const; - QtPrivate::QPropertyBindingData *bindingData_helper(QUntypedPropertyData *data, bool create); + QT_WARNING_POP }; - +#endif // QT_DEPRECATED_SINCE(6, 6) template<typename Class, typename T, auto Offset, auto Signal = nullptr> class QObjectBindableProperty : public QPropertyData<T> { using ThisType = QObjectBindableProperty<Class, T, Offset, Signal>; static bool constexpr HasSignal = !std::is_same_v<decltype(Signal), std::nullptr_t>; + using SignalTakesValue = std::is_invocable<decltype(Signal), Class, T>; Class *owner() { char *that = reinterpret_cast<char *>(this); @@ -873,8 +1037,12 @@ class QObjectBindableProperty : public QPropertyData<T> static void signalCallBack(QUntypedPropertyData *o) { QObjectBindableProperty *that = static_cast<QObjectBindableProperty *>(o); - if constexpr (HasSignal) - (that->owner()->*Signal)(); + if constexpr (HasSignal) { + if constexpr (SignalTakesValue::value) + (that->owner()->*Signal)(that->valueBypassingBindings()); + else + (that->owner()->*Signal)(); + } } public: using value_type = typename QPropertyData<T>::value_type; @@ -888,7 +1056,7 @@ public: explicit QObjectBindableProperty(const QPropertyBinding<T> &binding) : QObjectBindableProperty() { setBinding(binding); } -#ifndef Q_CLANG_QDOC +#ifndef Q_QDOC template <typename Functor> explicit QObjectBindableProperty(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION, typename std::enable_if_t<std::is_invocable_r_v<T, Functor&>> * = nullptr) @@ -901,7 +1069,7 @@ public: parameter_type value() const { - qGetBindingStorage(owner())->maybeUpdateBindingAndRegister(this); + qGetBindingStorage(owner())->registerDependency(this); return this->val; } @@ -938,6 +1106,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); @@ -965,7 +1138,6 @@ public: { QtPrivate::QPropertyBindingData *bd = qGetBindingStorage(owner())->bindingData(this, true); QUntypedPropertyBinding oldBinding(bd->setBinding(newBinding, this, HasSignal ? &signalCallBack : nullptr)); - notify(bd); return static_cast<QPropertyBinding<T> &>(oldBinding); } @@ -977,7 +1149,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, @@ -996,15 +1168,6 @@ public: return bd && bd->binding() != nullptr; } - void markDirty() { - QBindingStorage *storage = qGetBindingStorage(owner()); - auto bd = storage->bindingData(this, /*create=*/false); - if (bd) { // if we have no BindingData, nobody can listen anyway - bd->markDirty(); - notify(bd); - } - } - QPropertyBinding<T> binding() const { auto *bd = qGetBindingStorage(owner())->bindingData(this); @@ -1031,6 +1194,13 @@ public: return onValueChanged(f); } + template<typename Functor> + QPropertyNotifier addNotifier(Functor f) + { + static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters"); + return QPropertyNotifier(*this, f); + } + const QtPrivate::QPropertyBindingData &bindingData() const { auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner())); @@ -1040,43 +1210,53 @@ private: void notify(const QtPrivate::QPropertyBindingData *binding) { if (binding) - binding->notifyObservers(this); - if constexpr (HasSignal) - (owner()->*Signal)(); + binding->notifyObservers(this, qGetBindingStorage(owner())); + if constexpr (HasSignal) { + if constexpr (SignalTakesValue::value) + (owner()->*Signal)(this->valueBypassingBindings()); + else + (owner()->*Signal)(); + } } }; -#define Q_OBJECT_BINDABLE_PROPERTY3(Class, Type, name) \ +#define QT_OBJECT_BINDABLE_PROPERTY_3(Class, Type, name) \ static constexpr size_t _qt_property_##name##_offset() { \ + QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \ return offsetof(Class, name); \ + QT_WARNING_POP \ } \ QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, nullptr> name; -#define Q_OBJECT_BINDABLE_PROPERTY4(Class, Type, name, Signal) \ +#define QT_OBJECT_BINDABLE_PROPERTY_4(Class, Type, name, Signal) \ static constexpr size_t _qt_property_##name##_offset() { \ + QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \ return offsetof(Class, name); \ + QT_WARNING_POP \ } \ QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, Signal> name; #define Q_OBJECT_BINDABLE_PROPERTY(...) \ QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \ - QT_OVERLOADED_MACRO(Q_OBJECT_BINDABLE_PROPERTY, __VA_ARGS__) \ + QT_OVERLOADED_MACRO(QT_OBJECT_BINDABLE_PROPERTY, __VA_ARGS__) \ QT_WARNING_POP -#define Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS4(Class, Type, name, value) \ +#define QT_OBJECT_BINDABLE_PROPERTY_WITH_ARGS_4(Class, Type, name, value) \ static constexpr size_t _qt_property_##name##_offset() \ { \ - QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF return offsetof(Class, name); \ + QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \ + return offsetof(Class, name); \ QT_WARNING_POP \ } \ QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, nullptr> name = \ QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, nullptr>( \ value); -#define Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS5(Class, Type, name, value, Signal) \ +#define QT_OBJECT_BINDABLE_PROPERTY_WITH_ARGS_5(Class, Type, name, value, Signal) \ static constexpr size_t _qt_property_##name##_offset() \ { \ - QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF return offsetof(Class, name); \ + QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \ + return offsetof(Class, name); \ QT_WARNING_POP \ } \ QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, Signal> name = \ @@ -1084,7 +1264,9 @@ private: value); #define Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(...) \ - QT_OVERLOADED_MACRO(Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS, __VA_ARGS__) + QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \ + QT_OVERLOADED_MACRO(QT_OBJECT_BINDABLE_PROPERTY_WITH_ARGS, __VA_ARGS__) \ + QT_WARNING_POP template<typename Class, typename T, auto Offset, auto Getter> class QObjectComputedProperty : public QUntypedPropertyData @@ -1108,7 +1290,7 @@ public: parameter_type value() const { - qGetBindingStorage(owner())->maybeUpdateBindingAndRegister(this); + qGetBindingStorage(owner())->registerDependency(this); return (owner()->*Getter)(); } @@ -1148,21 +1330,26 @@ public: return onValueChanged(f); } + template<typename Functor> + QPropertyNotifier addNotifier(Functor f) + { + static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters"); + return QPropertyNotifier(*this, f); + } + QtPrivate::QPropertyBindingData &bindingData() const { auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner())); return *storage->bindingData(const_cast<QObjectComputedProperty *>(this), true); } - void markDirty() { + void notify() { // computed property can't store a binding, so there's nothing to mark auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner())); auto bd = storage->bindingData(const_cast<QObjectComputedProperty *>(this), false); if (bd) - bindingData().notifyObservers(this); + bd->notifyObservers(this, qGetBindingStorage(owner())); } - -private: }; #define Q_OBJECT_COMPUTED_PROPERTY(Class, Type, name, ...) \ @@ -1173,6 +1360,8 @@ private: } \ QObjectComputedProperty<Class, Type, Class::_qt_property_##name##_offset, __VA_ARGS__> name; +#undef QT_SOURCE_LOCATION_NAMESPACE + QT_END_NAMESPACE #endif // QPROPERTY_H |