diff options
Diffstat (limited to 'src/corelib/kernel/qproperty.h')
-rw-r--r-- | src/corelib/kernel/qproperty.h | 447 |
1 files changed, 312 insertions, 135 deletions
diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h index a6cb7ada48..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,21 +7,42 @@ #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 @@ -68,6 +53,17 @@ 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 { @@ -94,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; @@ -101,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(); @@ -215,8 +227,10 @@ 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 *); @@ -237,7 +251,7 @@ private: union { QPropertyBindingPrivate *binding = nullptr; ChangeHandler changeHandler; - QUntypedPropertyData *aliasedPropertyData; + QUntypedPropertyData *aliasData; }; }; @@ -249,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: @@ -271,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); @@ -284,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); @@ -296,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> { @@ -321,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) @@ -404,7 +454,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, @@ -444,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() @@ -484,6 +541,8 @@ struct QBindableInterface MakeBinding makeBinding; SetObserver setObserver; GetMetaType metaType; + + static constexpr quintptr MetaTypeAccessorFlag = 0x1; }; template<typename Property, typename = void> @@ -553,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; @@ -562,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> @@ -598,6 +724,11 @@ public: { if (iface) iface->setObserver(data, observer); +#ifndef QT_NO_DEBUG + else + QtPrivate::BindableWarnings::printUnsuitableBindableWarning("observe:", + QtPrivate::BindableWarnings::InvalidInterface); +#endif } template<typename Functor> @@ -615,18 +746,41 @@ public: 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; } @@ -635,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> @@ -649,12 +818,18 @@ 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; } } + 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)); @@ -672,10 +847,19 @@ public: 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, @@ -705,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) @@ -720,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) @@ -777,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, @@ -817,73 +1004,26 @@ public: return QBindable<T>(aliasedProperty(), iface).subscribe(f); } - bool isValid() const - { - return aliasedProperty() != nullptr; - } -}; - -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, auto Signal> - friend class QObjectCompatProperty; -public: - QBindingStorage(); - ~QBindingStorage(); - - bool isEmpty() { return !d; } - - void registerDependency(const QUntypedPropertyData *data) const - { - if (!bindingStatus->currentlyEvaluatingBinding) - return; - registerDependency_helper(data); - } - QtPrivate::QPropertyBindingData *bindingData(const QUntypedPropertyData *data) const + template<typename Functor> + QPropertyNotifier addNotifier(Functor f) { - if (!d) - return nullptr; - return bindingData_helper(data); + return QBindable<T>(aliasedProperty(), iface).addNotifier(f); } - // ### Qt 7: remove unused BIC shim - void maybeUpdateBindingAndRegister(const QUntypedPropertyData *data) const { registerDependency(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 registerDependency_helper(const QUntypedPropertyData *data) const; - // ### Unused, but keep for BC - 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); @@ -897,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; @@ -912,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) @@ -962,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); @@ -1000,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, @@ -1045,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())); @@ -1054,42 +1210,54 @@ 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_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_POP \ } \ QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, Signal> name = \ QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, Signal>( \ @@ -1097,7 +1265,7 @@ private: #define Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(...) \ QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \ - QT_OVERLOADED_MACRO(Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS, __VA_ARGS__) \ + QT_OVERLOADED_MACRO(QT_OBJECT_BINDABLE_PROPERTY_WITH_ARGS, __VA_ARGS__) \ QT_WARNING_POP template<typename Class, typename T, auto Offset, auto Getter> @@ -1162,6 +1330,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); + } + QtPrivate::QPropertyBindingData &bindingData() const { auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner())); @@ -1173,7 +1348,7 @@ public: auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner())); auto bd = storage->bindingData(const_cast<QObjectComputedProperty *>(this), false); if (bd) - bd->notifyObservers(this); + bd->notifyObservers(this, qGetBindingStorage(owner())); } }; @@ -1185,6 +1360,8 @@ public: } \ QObjectComputedProperty<Class, Type, Class::_qt_property_##name##_offset, __VA_ARGS__> name; +#undef QT_SOURCE_LOCATION_NAMESPACE + QT_END_NAMESPACE #endif // QPROPERTY_H |