diff options
Diffstat (limited to 'src/corelib/kernel/qproperty.h')
-rw-r--r-- | src/corelib/kernel/qproperty.h | 337 |
1 files changed, 221 insertions, 116 deletions
diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h index f5513be73f..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 <QtCore/qbindingstorage.h> + #include <type_traits> #include <QtCore/qpropertyprivate.h> -#if __has_include(<source_location>) && __cplusplus >= 202002L && !defined(Q_CLANG_QDOC) +#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 QT_SOURCE_LOCATION_NAMESPACE::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() @@ -562,6 +619,60 @@ 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 @@ -574,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> @@ -632,6 +746,14 @@ public: return onValueChanged(f); } + template<typename Functor> + QPropertyNotifier addNotifier(Functor f) + { + QPropertyNotifier handler(f); + observe(&handler); + return handler; + } + QUntypedPropertyBinding binding() const { if (!isBindable()) { @@ -702,6 +824,12 @@ public: } } + 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)); @@ -731,7 +859,7 @@ public: #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, @@ -761,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) @@ -776,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) @@ -833,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, @@ -873,67 +1004,19 @@ 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> @@ -973,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) @@ -1066,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, @@ -1111,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())); @@ -1120,7 +1210,7 @@ private: void notify(const QtPrivate::QPropertyBindingData *binding) { if (binding) - binding->notifyObservers(this); + binding->notifyObservers(this, qGetBindingStorage(owner())); if constexpr (HasSignal) { if constexpr (SignalTakesValue::value) (owner()->*Signal)(this->valueBypassingBindings()); @@ -1132,13 +1222,17 @@ private: #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 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; @@ -1150,7 +1244,9 @@ private: #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>( \ @@ -1159,7 +1255,9 @@ private: #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>( \ @@ -1232,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())); @@ -1243,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())); } }; |