/**************************************************************************** ** ** 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$ ** ****************************************************************************/ #ifndef QPROPERTY_H #define QPROPERTY_H #include #include #include #include #include #include #include #if __has_include() && __cplusplus >= 202002L && !defined(Q_CLANG_QDOC) #include #define QT_PROPERTY_COLLECT_BINDING_LOCATION #define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation(std::source_location::current()) #elif __has_include() && __cplusplus >= 201703L && !defined(Q_CLANG_QDOC) #include #define QT_PROPERTY_COLLECT_BINDING_LOCATION #define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation(std::experimental::source_location::current()) #else #define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation() #endif QT_BEGIN_NAMESPACE template class QPropertyData : public QUntypedPropertyData { protected: mutable T val = T(); private: class DisableRValueRefs {}; protected: static constexpr bool UseReferences = !(std::is_arithmetic_v || std::is_enum_v || std::is_pointer_v); public: using value_type = T; using parameter_type = std::conditional_t; using rvalue_ref = typename std::conditional_t; using arrow_operator_result = std::conditional_t, const T &, std::conditional_t, const T &, void>>; QPropertyData() = default; QPropertyData(parameter_type t) : val(t) {} QPropertyData(rvalue_ref t) : val(std::move(t)) {} ~QPropertyData() = default; parameter_type valueBypassingBindings() const { return val; } void setValueBypassingBindings(parameter_type v) { val = v; } void setValueBypassingBindings(rvalue_ref v) { val = std::move(v); } }; struct Q_CORE_EXPORT QPropertyBindingSourceLocation { const char *fileName = nullptr; const char *functionName = nullptr; quint32 line = 0; quint32 column = 0; QPropertyBindingSourceLocation() = default; #ifdef QT_PROPERTY_COLLECT_BINDING_LOCATION QPropertyBindingSourceLocation(const std::experimental::source_location &cppLocation) { fileName = cppLocation.file_name(); functionName = cppLocation.function_name(); line = cppLocation.line(); column = cppLocation.column(); } #endif }; template class QPropertyChangeHandler; class QPropertyBindingErrorPrivate; class Q_CORE_EXPORT QPropertyBindingError { public: enum Type { NoError, BindingLoop, EvaluationError, UnknownError }; QPropertyBindingError(); QPropertyBindingError(Type type, const QString &description = QString()); QPropertyBindingError(const QPropertyBindingError &other); QPropertyBindingError &operator=(const QPropertyBindingError &other); QPropertyBindingError(QPropertyBindingError &&other); QPropertyBindingError &operator=(QPropertyBindingError &&other); ~QPropertyBindingError(); bool hasError() const { return d.get() != nullptr; } Type type() const; QString description() const; private: QSharedDataPointer d; }; class Q_CORE_EXPORT QUntypedPropertyBinding { public: // writes binding result into dataPtr using BindingFunctionVTable = QtPrivate::BindingFunctionVTable; QUntypedPropertyBinding(); QUntypedPropertyBinding(QMetaType metaType, const BindingFunctionVTable *vtable, void *function, const QPropertyBindingSourceLocation &location); template QUntypedPropertyBinding(QMetaType metaType, Functor &&f, const QPropertyBindingSourceLocation &location) : QUntypedPropertyBinding(metaType, &QtPrivate::bindingFunctionVTable>, &f, location) {} QUntypedPropertyBinding(QUntypedPropertyBinding &&other); QUntypedPropertyBinding(const QUntypedPropertyBinding &other); QUntypedPropertyBinding &operator=(const QUntypedPropertyBinding &other); QUntypedPropertyBinding &operator=(QUntypedPropertyBinding &&other); ~QUntypedPropertyBinding(); bool isNull() const; QPropertyBindingError error() const; QMetaType valueMetaType() const; explicit QUntypedPropertyBinding(QPropertyBindingPrivate *priv); private: friend class QtPrivate::QPropertyBindingData; friend class QPropertyBindingPrivate; template friend class QPropertyBinding; QPropertyBindingPrivatePtr d; }; template class QPropertyBinding : public QUntypedPropertyBinding { public: QPropertyBinding() = default; template QPropertyBinding(Functor &&f, const QPropertyBindingSourceLocation &location) : QUntypedPropertyBinding(QMetaType::fromType(), &QtPrivate::bindingFunctionVTable, PropertyType>, &f, location) {} template QPropertyBinding(const Property &property) : QUntypedPropertyBinding(property.bindingData().binding()) {} // Internal explicit QPropertyBinding(const QUntypedPropertyBinding &binding) : QUntypedPropertyBinding(binding) {} }; namespace Qt { template auto makePropertyBinding(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION, std::enable_if_t> * = nullptr) { return QPropertyBinding>(std::forward(f), location); } } struct QPropertyObserverPrivate; struct QPropertyObserverPointer; class Q_CORE_EXPORT QPropertyObserver { public: // Internal 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 ActivelyExecuting // the observer is currently evaluated in QPropertyObserver::notifyObservers or its // placeholder. We only can store 4 different values, therefore those two conflate }; constexpr QPropertyObserver() = default; QPropertyObserver(QPropertyObserver &&other) noexcept; QPropertyObserver &operator=(QPropertyObserver &&other) noexcept; ~QPropertyObserver(); template void setSource(const Property &property) { setSource(property.bindingData()); } void setSource(const QtPrivate::QPropertyBindingData &property); protected: using ChangeHandler = void (*)(QPropertyObserver*, QUntypedPropertyData *); QPropertyObserver(ChangeHandler changeHandler); QPropertyObserver(QUntypedPropertyData *aliasedPropertyPtr); QUntypedPropertyData *aliasedProperty() const { return aliasedPropertyData; } private: QTaggedPointer next; // prev is a pointer to the "next" element within the previous node, or to the "firstObserverPtr" if it is the // first node. QtPrivate::QTagPreservingPointerToPointer prev; union { QPropertyBindingPrivate *bindingToMarkDirty = nullptr; ChangeHandler changeHandler; QUntypedPropertyData *aliasedPropertyData; QPropertyObserver **nodeState; }; QPropertyObserver(const QPropertyObserver &) = delete; QPropertyObserver &operator=(const QPropertyObserver &) = delete; friend struct QPropertyObserverPointer; friend struct QPropertyBindingDataPointer; friend class QPropertyBindingPrivate; template friend struct QPropertyObserverNodeProtector; }; template class [[nodiscard]] QPropertyChangeHandler : public QPropertyObserver { Functor m_handler; public: QPropertyChangeHandler(Functor handler) : QPropertyObserver([](QPropertyObserver *self, QUntypedPropertyData *) { auto This = static_cast*>(self); This->m_handler(); }) , m_handler(handler) { } template QPropertyChangeHandler(const Property &property, Functor handler) : QPropertyObserver([](QPropertyObserver *self, QUntypedPropertyData *) { auto This = static_cast*>(self); This->m_handler(); }) , m_handler(handler) { setSource(property); } }; template class QProperty : public QPropertyData { QtPrivate::QPropertyBindingData d; bool is_equal(const T &v) { if constexpr (QTypeTraits::has_operator_equal_v) { if (v == this->val) return true; } return false; } public: using value_type = typename QPropertyData::value_type; using parameter_type = typename QPropertyData::parameter_type; using rvalue_ref = typename QPropertyData::rvalue_ref; using arrow_operator_result = typename QPropertyData::arrow_operator_result; QProperty() = default; explicit QProperty(parameter_type initialValue) : QPropertyData(initialValue) {} explicit QProperty(rvalue_ref initialValue) : QPropertyData(std::move(initialValue)) {} explicit QProperty(const QPropertyBinding &binding) : QProperty() { setBinding(binding); } #ifndef Q_CLANG_QDOC template explicit QProperty(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION, typename std::enable_if_t> * = nullptr) : QProperty(QPropertyBinding(std::forward(f), location)) {} #else template explicit QProperty(Functor &&f); #endif ~QProperty() = default; parameter_type value() const { if (d.hasBinding()) d.evaluateIfDirty(this); d.registerWithCurrentlyEvaluatingBinding(); return this->val; } arrow_operator_result operator->() const { if constexpr (QTypeTraits::is_dereferenceable_v) { return value(); } else if constexpr (std::is_pointer_v) { value(); return this->val; } else { return; } } parameter_type operator*() const { return value(); } operator parameter_type() const { return value(); } void setValue(rvalue_ref newValue) { d.removeBinding(); if (is_equal(newValue)) return; this->val = std::move(newValue); notify(); } void setValue(parameter_type newValue) { d.removeBinding(); if (is_equal(newValue)) return; this->val = newValue; notify(); } QProperty &operator=(rvalue_ref newValue) { setValue(std::move(newValue)); return *this; } QProperty &operator=(parameter_type newValue) { setValue(newValue); return *this; } QPropertyBinding setBinding(const QPropertyBinding &newBinding) { QPropertyBinding oldBinding(d.setBinding(newBinding, this)); notify(); return oldBinding; } bool setBinding(const QUntypedPropertyBinding &newBinding) { if (!newBinding.isNull() && newBinding.valueMetaType().id() != qMetaTypeId()) return false; setBinding(static_cast &>(newBinding)); return true; } #ifndef Q_CLANG_QDOC template QPropertyBinding setBinding(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION, std::enable_if_t> * = nullptr) { return setBinding(Qt::makePropertyBinding(std::forward(f), location)); } #else template QPropertyBinding setBinding(Functor f); #endif bool hasBinding() const { return d.hasBinding(); } QPropertyBinding binding() const { return QPropertyBinding(*this); } QPropertyBinding takeBinding() { return QPropertyBinding(d.setBinding(QUntypedPropertyBinding(), this)); } template QPropertyChangeHandler onValueChanged(Functor f) { static_assert(std::is_invocable_v, "Functor callback must be callable without any parameters"); return QPropertyChangeHandler(*this, f); } template QPropertyChangeHandler subscribe(Functor f) { static_assert(std::is_invocable_v, "Functor callback must be callable without any parameters"); f(); return onValueChanged(f); } const QtPrivate::QPropertyBindingData &bindingData() const { return d; } private: void notify() { d.notifyObservers(this); } Q_DISABLE_COPY_MOVE(QProperty) }; namespace Qt { template QPropertyBinding makePropertyBinding(const QProperty &otherProperty, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) { return Qt::makePropertyBinding([&otherProperty]() -> PropertyType { return otherProperty; }, location); } } namespace QtPrivate { struct QBindableInterface { using Getter = void (*)(const QUntypedPropertyData *d, void *value); using Setter = void (*)(QUntypedPropertyData *d, const void *value); using BindingGetter = QUntypedPropertyBinding (*)(const QUntypedPropertyData *d); using BindingSetter = QUntypedPropertyBinding (*)(QUntypedPropertyData *d, const QUntypedPropertyBinding &binding); using MakeBinding = QUntypedPropertyBinding (*)(const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location); using SetObserver = void (*)(const QUntypedPropertyData *d, QPropertyObserver *observer); using GetMetaType = QMetaType (*)(); Getter getter; Setter setter; BindingGetter getBinding; BindingSetter setBinding; MakeBinding makeBinding; SetObserver setObserver; GetMetaType metaType; }; template class QBindableInterfaceForProperty { using T = typename Property::value_type; public: // interface for read-only properties. Those do not have a binding()/setBinding() method, but one can // install observers on them. static constexpr QBindableInterface iface = { [](const QUntypedPropertyData *d, void *value) -> void { *static_cast(value) = static_cast(d)->value(); }, nullptr, nullptr, nullptr, [](const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding { return Qt::makePropertyBinding([d]() -> T { return static_cast(d)->value(); }, location); }, [](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void { observer->setSource(static_cast(d)->bindingData()); }, []() { return QMetaType::fromType(); } }; }; template class QBindableInterfaceForProperty().binding())>> { using T = typename Property::value_type; public: static constexpr QBindableInterface iface = { [](const QUntypedPropertyData *d, void *value) -> void { *static_cast(value) = static_cast(d)->value(); }, [](QUntypedPropertyData *d, const void *value) -> void { static_cast(d)->setValue(*static_cast(value)); }, [](const QUntypedPropertyData *d) -> QUntypedPropertyBinding { return static_cast(d)->binding(); }, [](QUntypedPropertyData *d, const QUntypedPropertyBinding &binding) -> QUntypedPropertyBinding { return static_cast(d)->setBinding(static_cast &>(binding)); }, [](const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding { return Qt::makePropertyBinding([d]() -> T { return static_cast(d)->value(); }, location); }, [](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void { observer->setSource(static_cast(d)->bindingData()); }, []() { return QMetaType::fromType(); } }; }; } class QUntypedBindable { protected: QUntypedPropertyData *data = nullptr; const QtPrivate::QBindableInterface *iface = nullptr; constexpr QUntypedBindable(QUntypedPropertyData *d, const QtPrivate::QBindableInterface *i) : data(d), iface(i) {} public: constexpr QUntypedBindable() = default; template QUntypedBindable(Property *p) : data(const_cast *>(p)), iface(&QtPrivate::QBindableInterfaceForProperty::iface) { Q_ASSERT(data && iface); } bool isValid() const { return data != nullptr; } bool isBindable() const { return iface && iface->getBinding; } QUntypedPropertyBinding makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) { return iface ? iface->makeBinding(data, location) : QUntypedPropertyBinding(); } void observe(QPropertyObserver *observer) { if (iface) iface->setObserver(data, observer); } template QPropertyChangeHandler onValueChanged(Functor f) { QPropertyChangeHandler handler(f); observe(&handler); return handler; } template QPropertyChangeHandler subscribe(Functor f) { f(); return onValueChanged(f); } QUntypedPropertyBinding binding() const { if (!iface->getBinding) return QUntypedPropertyBinding(); return iface->getBinding(data); } bool setBinding(const QUntypedPropertyBinding &binding) { if (!iface->setBinding) return false; if (!binding.isNull() && binding.valueMetaType() != iface->metaType()) return false; iface->setBinding(data, binding); return true; } bool hasBinding() const { return !binding().isNull(); } }; template class QBindable : public QUntypedBindable { template friend class QPropertyAlias; constexpr QBindable(QUntypedPropertyData *d, const QtPrivate::QBindableInterface *i) : QUntypedBindable(d, i) {} public: using QUntypedBindable::QUntypedBindable; explicit QBindable(const QUntypedBindable &b) : QUntypedBindable(b) { if (iface && iface->metaType() != QMetaType::fromType()) { data = nullptr; iface = nullptr; } } QPropertyBinding makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) { return static_cast &&>(QUntypedBindable::makeBinding(location)); } QPropertyBinding binding() const { return static_cast &&>(QUntypedBindable::binding()); } using QUntypedBindable::setBinding; QPropertyBinding setBinding(const QPropertyBinding &binding) { Q_ASSERT(!iface || binding.isNull() || binding.valueMetaType() == iface->metaType()); return iface ? static_cast &&>(iface->setBinding(data, binding)) : QPropertyBinding(); } #ifndef Q_CLANG_QDOC template QPropertyBinding setBinding(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION, std::enable_if_t> * = nullptr) { return setBinding(Qt::makePropertyBinding(std::forward(f), location)); } #else template QPropertyBinding setBinding(Functor f); #endif T value() const { if (iface) { T result; iface->getter(data, &result); return result; } return T{}; } void setValue(const T &value) { if (iface) iface->setter(data, &value); } }; template class QPropertyAlias : public QPropertyObserver { Q_DISABLE_COPY_MOVE(QPropertyAlias) const QtPrivate::QBindableInterface *iface = nullptr; public: QPropertyAlias(QProperty *property) : QPropertyObserver(property), iface(&QtPrivate::QBindableInterfaceForProperty>::iface) { if (iface) iface->setObserver(aliasedProperty(), this); } template QPropertyAlias(Property *property) : QPropertyObserver(property), iface(&QtPrivate::QBindableInterfaceForProperty::iface) { if (iface) iface->setObserver(aliasedProperty(), this); } QPropertyAlias(QPropertyAlias *alias) : QPropertyObserver(alias->aliasedProperty()), iface(alias->iface) { if (iface) iface->setObserver(aliasedProperty(), this); } QPropertyAlias(const QBindable &property) : QPropertyObserver(property.data), iface(property.iface) { if (iface) iface->setObserver(aliasedProperty(), this); } T value() const { T t = T(); if (auto *p = aliasedProperty()) iface->getter(p, &t); return t; } operator T() const { return value(); } void setValue(const T &newValue) { if (auto *p = aliasedProperty()) iface->setter(p, &newValue); } QPropertyAlias &operator=(const T &newValue) { setValue(newValue); return *this; } QPropertyBinding setBinding(const QPropertyBinding &newBinding) { return QBindable(aliasedProperty(), iface).setBinding(newBinding); } bool setBinding(const QUntypedPropertyBinding &newBinding) { return QBindable(aliasedProperty(), iface).setBinding(newBinding); } #ifndef Q_CLANG_QDOC template QPropertyBinding setBinding(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION, std::enable_if_t> * = nullptr) { return setBinding(Qt::makePropertyBinding(std::forward(f), location)); } #else template QPropertyBinding setBinding(Functor f); #endif bool hasBinding() const { return QBindable(aliasedProperty(), iface).hasBinding(); } QPropertyBinding binding() const { return QBindable(aliasedProperty(), iface).binding(); } QPropertyBinding takeBinding() { return QBindable(aliasedProperty(), iface).takeBinding(); } template QPropertyChangeHandler onValueChanged(Functor f) { return QBindable(aliasedProperty(), iface).onValueChanged(f); } template QPropertyChangeHandler subscribe(Functor f) { return QBindable(aliasedProperty(), iface).subscribe(f); } bool isValid() const { return aliasedProperty() != nullptr; } }; struct QBindingStatus; struct QBindingStorageData; class Q_CORE_EXPORT QBindingStorage { mutable QBindingStorageData *d = nullptr; QBindingStatus *bindingStatus = nullptr; template friend class QObjectCompatProperty; public: QBindingStorage(); ~QBindingStorage(); bool isEmpty() { return !d; } void maybeUpdateBindingAndRegister(const QUntypedPropertyData *data) const; QtPrivate::QPropertyBindingData *bindingData(const QUntypedPropertyData *data) const; QtPrivate::QPropertyBindingData *bindingData(QUntypedPropertyData *data, bool create); }; template class QObjectBindableProperty : public QPropertyData { using ThisType = QObjectBindableProperty; static bool constexpr HasSignal = !std::is_same_v; Class *owner() { char *that = reinterpret_cast(this); return reinterpret_cast(that - QtPrivate::detail::getOffset(Offset)); } const Class *owner() const { char *that = const_cast(reinterpret_cast(this)); return reinterpret_cast(that - QtPrivate::detail::getOffset(Offset)); } static void signalCallBack(QUntypedPropertyData *o) { QObjectBindableProperty *that = static_cast(o); if constexpr (HasSignal) (that->owner()->*Signal)(); } public: using value_type = typename QPropertyData::value_type; using parameter_type = typename QPropertyData::parameter_type; using rvalue_ref = typename QPropertyData::rvalue_ref; using arrow_operator_result = typename QPropertyData::arrow_operator_result; QObjectBindableProperty() = default; explicit QObjectBindableProperty(const T &initialValue) : QPropertyData(initialValue) {} explicit QObjectBindableProperty(T &&initialValue) : QPropertyData(std::move(initialValue)) {} explicit QObjectBindableProperty(const QPropertyBinding &binding) : QObjectBindableProperty() { setBinding(binding); } #ifndef Q_CLANG_QDOC template explicit QObjectBindableProperty(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION, typename std::enable_if_t> * = nullptr) : QObjectBindableProperty(QPropertyBinding(std::forward(f), location)) {} #else template explicit QObjectBindableProperty(Functor &&f); #endif parameter_type value() const { qGetBindingStorage(owner())->maybeUpdateBindingAndRegister(this); return this->val; } arrow_operator_result operator->() const { if constexpr (QTypeTraits::is_dereferenceable_v) { return value(); } else if constexpr (std::is_pointer_v) { value(); return this->val; } else { return; } } parameter_type operator*() const { return value(); } operator parameter_type() const { return value(); } void setValue(parameter_type t) { auto *bd = qGetBindingStorage(owner())->bindingData(this); if (bd) bd->removeBinding(); if (this->val == t) return; this->val = t; notify(bd); } void setValue(rvalue_ref t) { auto *bd = qGetBindingStorage(owner())->bindingData(this); if (bd) bd->removeBinding(); if (this->val == t) return; this->val = std::move(t); notify(bd); } QObjectBindableProperty &operator=(rvalue_ref newValue) { setValue(std::move(newValue)); return *this; } QObjectBindableProperty &operator=(parameter_type newValue) { setValue(newValue); return *this; } QPropertyBinding setBinding(const QPropertyBinding &newBinding) { QtPrivate::QPropertyBindingData *bd = qGetBindingStorage(owner())->bindingData(this, true); QUntypedPropertyBinding oldBinding(bd->setBinding(newBinding, this, HasSignal ? &signalCallBack : nullptr)); notify(bd); return static_cast &>(oldBinding); } bool setBinding(const QUntypedPropertyBinding &newBinding) { if (!newBinding.isNull() && newBinding.valueMetaType().id() != qMetaTypeId()) return false; setBinding(static_cast &>(newBinding)); return true; } #ifndef Q_CLANG_QDOC template QPropertyBinding setBinding(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION, std::enable_if_t> * = nullptr) { return setBinding(Qt::makePropertyBinding(std::forward(f), location)); } #else template QPropertyBinding setBinding(Functor f); #endif bool hasBinding() const { auto *bd = qGetBindingStorage(owner())->bindingData(this); return bd && bd->binding() != nullptr; } QPropertyBinding binding() const { auto *bd = qGetBindingStorage(owner())->bindingData(this); return static_cast &&>(QUntypedPropertyBinding(bd ? bd->binding() : nullptr)); } QPropertyBinding takeBinding() { return setBinding(QPropertyBinding()); } template QPropertyChangeHandler onValueChanged(Functor f) { static_assert(std::is_invocable_v, "Functor callback must be callable without any parameters"); return QPropertyChangeHandler(*this, f); } template QPropertyChangeHandler subscribe(Functor f) { static_assert(std::is_invocable_v, "Functor callback must be callable without any parameters"); f(); return onValueChanged(f); } const QtPrivate::QPropertyBindingData &bindingData() const { auto *storage = const_cast(qGetBindingStorage(owner())); return *storage->bindingData(const_cast(this), true); } private: void notify(const QtPrivate::QPropertyBindingData *binding) { if (binding) binding->notifyObservers(this); if constexpr (HasSignal) (owner()->*Signal)(); } }; #define Q_OBJECT_BINDABLE_PROPERTY(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 name; template class QObjectComputedProperty : public QUntypedPropertyData { Class *owner() { char *that = reinterpret_cast(this); return reinterpret_cast(that - QtPrivate::detail::getOffset(Offset)); } const Class *owner() const { char *that = const_cast(reinterpret_cast(this)); return reinterpret_cast(that - QtPrivate::detail::getOffset(Offset)); } public: using value_type = T; using parameter_type = T; QObjectComputedProperty() = default; parameter_type value() const { qGetBindingStorage(owner())->maybeUpdateBindingAndRegister(this); return (owner()->*Getter)(); } std::conditional_t, parameter_type, void> operator->() const { if constexpr (QTypeTraits::is_dereferenceable_v) return value(); else return; } parameter_type operator*() const { return value(); } operator parameter_type() const { return value(); } constexpr bool hasBinding() const { return false; } template QPropertyChangeHandler onValueChanged(Functor f) { static_assert(std::is_invocable_v, "Functor callback must be callable without any parameters"); return QPropertyChangeHandler(*this, f); } template QPropertyChangeHandler subscribe(Functor f) { static_assert(std::is_invocable_v, "Functor callback must be callable without any parameters"); f(); return onValueChanged(f); } QtPrivate::QPropertyBindingData &bindingData() const { auto *storage = const_cast(qGetBindingStorage(owner())); return *storage->bindingData(const_cast(this), true); } private: }; #define Q_OBJECT_COMPUTED_PROPERTY(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 \ } \ QObjectComputedProperty name; QT_END_NAMESPACE #endif // QPROPERTY_H