summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qproperty.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/kernel/qproperty.h')
-rw-r--r--src/corelib/kernel/qproperty.h1512
1 files changed, 1002 insertions, 510 deletions
diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h
index de08a71349..0373867a66 100644
--- a/src/corelib/kernel/qproperty.h
+++ b/src/corelib/kernel/qproperty.h
@@ -1,69 +1,96 @@
-/****************************************************************************
-**
-** 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
#include <QtCore/qglobal.h>
-#include <QtCore/QSharedDataPointer>
-#include <QtCore/QString>
-#include <QtCore/qmetatype.h>
-#include <functional>
+#include <QtCore/qshareddata.h>
+#include <QtCore/qstring.h>
+#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
+{
+protected:
+ mutable T val = T();
+private:
+ class DisableRValueRefs {};
+protected:
+ static constexpr bool UseReferences = !(std::is_arithmetic_v<T> || std::is_enum_v<T> || std::is_pointer_v<T>);
+public:
+ using value_type = T;
+ using parameter_type = std::conditional_t<UseReferences, const T &, T>;
+ using rvalue_ref = typename std::conditional_t<UseReferences, T &&, DisableRValueRefs>;
+ using arrow_operator_result = std::conditional_t<std::is_pointer_v<T>, const T &,
+ std::conditional_t<QTypeTraits::is_dereferenceable_v<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); }
+};
+
+// ### Qt 7: un-export
struct Q_CORE_EXPORT QPropertyBindingSourceLocation
{
const char *fileName = nullptr;
@@ -71,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();
@@ -83,10 +125,6 @@ struct Q_CORE_EXPORT QPropertyBindingSourceLocation
};
template <typename Functor> class QPropertyChangeHandler;
-
-template <typename T> class QProperty;
-template <typename T, auto callbackMember, auto guardCallback> class QNotifiedProperty;
-
class QPropertyBindingErrorPrivate;
class Q_CORE_EXPORT QPropertyBindingError
@@ -99,17 +137,18 @@ public:
UnknownError
};
- QPropertyBindingError(Type type = NoError);
+ 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;
- void setDescription(const QString &description);
QString description() const;
- QPropertyBindingSourceLocation location() const;
private:
QSharedDataPointer<QPropertyBindingErrorPrivate> d;
@@ -118,12 +157,17 @@ private:
class Q_CORE_EXPORT QUntypedPropertyBinding
{
public:
- using BindingEvaluationResult = QPropertyBindingError;
// writes binding result into dataPtr
- using BindingEvaluationFunction = std::function<BindingEvaluationResult(const QMetaType &metaType, void *dataPtr)>;
+ using BindingFunctionVTable = QtPrivate::BindingFunctionVTable;
QUntypedPropertyBinding();
- QUntypedPropertyBinding(const QMetaType &metaType, BindingEvaluationFunction function, const QPropertyBindingSourceLocation &location);
+ QUntypedPropertyBinding(QMetaType metaType, const BindingFunctionVTable *vtable, void *function, const QPropertyBindingSourceLocation &location);
+
+ template<typename Functor>
+ QUntypedPropertyBinding(QMetaType metaType, Functor &&f, const QPropertyBindingSourceLocation &location)
+ : QUntypedPropertyBinding(metaType, &QtPrivate::bindingFunctionVTable<std::remove_reference_t<Functor>>, &f, location)
+ {}
+
QUntypedPropertyBinding(QUntypedPropertyBinding &&other);
QUntypedPropertyBinding(const QUntypedPropertyBinding &other);
QUntypedPropertyBinding &operator=(const QUntypedPropertyBinding &other);
@@ -138,7 +182,7 @@ public:
explicit QUntypedPropertyBinding(QPropertyBindingPrivate *priv);
private:
- friend class QtPrivate::QPropertyBase;
+ friend class QtPrivate::QPropertyBindingData;
friend class QPropertyBindingPrivate;
template <typename> friend class QPropertyBinding;
QPropertyBindingPrivatePtr d;
@@ -147,42 +191,15 @@ private:
template <typename PropertyType>
class QPropertyBinding : public QUntypedPropertyBinding
{
- template <typename Functor>
- struct BindingAdaptor
- {
- Functor impl;
- QUntypedPropertyBinding::BindingEvaluationResult operator()(const QMetaType &/*metaType*/, void *dataPtr)
- {
- std::variant<PropertyType, QPropertyBindingError> result(impl());
- if (auto errorPtr = std::get_if<QPropertyBindingError>(&result))
- return *errorPtr;
-
- if (auto valuePtr = std::get_if<PropertyType>(&result)) {
- PropertyType *propertyPtr = reinterpret_cast<PropertyType *>(dataPtr);
- *propertyPtr = std::move(*valuePtr);
- return {};
- }
-
- return {};
- }
- };
public:
QPropertyBinding() = default;
template<typename Functor>
QPropertyBinding(Functor &&f, const QPropertyBindingSourceLocation &location)
- : QUntypedPropertyBinding(QMetaType::fromType<PropertyType>(), BindingAdaptor<Functor>{std::forward<Functor>(f)}, location)
- {}
-
- QPropertyBinding(const QProperty<PropertyType> &property)
- : QUntypedPropertyBinding(property.d.priv.binding())
+ : QUntypedPropertyBinding(QMetaType::fromType<PropertyType>(), &QtPrivate::bindingFunctionVTable<std::remove_reference_t<Functor>, PropertyType>, &f, location)
{}
- template<auto notifier, auto guard>
- QPropertyBinding(const QNotifiedProperty<PropertyType, notifier, guard> &property)
- : QUntypedPropertyBinding(property.d.priv.binding())
- {}
// Internal
explicit QPropertyBinding(const QUntypedPropertyBinding &binding)
@@ -190,51 +207,174 @@ public:
{}
};
-namespace QtPrivate {
- template<typename... Ts>
- constexpr auto is_variant_v = false;
- template<typename... Ts>
- constexpr auto is_variant_v<std::variant<Ts...>> = true;
-}
-
namespace Qt {
template <typename Functor>
auto makePropertyBinding(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
- std::enable_if_t<std::is_invocable_v<Functor>> * = 0)
+ std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
{
- if constexpr (QtPrivate::is_variant_v<std::invoke_result_t<Functor>>) {
- return QPropertyBinding<std::variant_alternative_t<0, std::invoke_result_t<Functor>>>(std::forward<Functor>(f), location);
- } else {
- return QPropertyBinding<std::invoke_result_t<Functor>>(std::forward<Functor>(f), location);
- }
- // Work around bogus warning
- Q_UNUSED(QtPrivate::is_variant_v<bool>)
+ return QPropertyBinding<std::invoke_result_t<Functor>>(std::forward<Functor>(f), location);
}
}
-struct QPropertyBasePointer;
+struct QPropertyObserverPrivate;
+struct QPropertyObserverPointer;
+class QPropertyObserver;
+
+class QPropertyObserverBase
+{
+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
+ 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;
+ friend struct QPropertyBindingDataPointer;
+ friend class QPropertyBindingPrivate;
+
+ QTaggedPointer<QPropertyObserver, ObserverTag> 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<QPropertyObserver, ObserverTag> prev;
+
+ union {
+ QPropertyBindingPrivate *binding = nullptr;
+ ChangeHandler changeHandler;
+ QUntypedPropertyData *aliasData;
+ };
+};
+
+class Q_CORE_EXPORT QPropertyObserver : public QPropertyObserverBase
+{
+public:
+ constexpr QPropertyObserver() = default;
+ QPropertyObserver(QPropertyObserver &&other) noexcept;
+ QPropertyObserver &operator=(QPropertyObserver &&other) noexcept;
+ ~QPropertyObserver();
+
+ 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 aliasData;
+ }
+
+private:
+
+ QPropertyObserver(const QPropertyObserver &) = delete;
+ QPropertyObserver &operator=(const QPropertyObserver &) = delete;
+
+};
+
+template <typename Functor>
+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);
+ This->m_handler();
+ })
+ , m_handler(handler)
+ {
+ }
+
+ 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);
+ This->m_handler();
+ })
+ , m_handler(handler)
+ {
+ setSource(property);
+ }
+};
+
+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
+class QProperty : public QPropertyData<T>
{
+ QtPrivate::QPropertyBindingData d;
+ bool is_equal(const T &v)
+ {
+ if constexpr (QTypeTraits::has_operator_equal_v<T>) {
+ if (v == this->val)
+ return true;
+ }
+ return false;
+ }
+
public:
- using value_type = T;
+ using value_type = typename QPropertyData<T>::value_type;
+ using parameter_type = typename QPropertyData<T>::parameter_type;
+ using rvalue_ref = typename QPropertyData<T>::rvalue_ref;
+ using arrow_operator_result = typename QPropertyData<T>::arrow_operator_result;
QProperty() = default;
- explicit QProperty(const T &initialValue) : d(initialValue) {}
- explicit QProperty(T &&initialValue) : d(std::move(initialValue)) {}
- QProperty(QProperty &&other) : d(std::move(other.d)) { notify(); }
- QProperty &operator=(QProperty &&other) { d = std::move(other.d); notify(); return *this; }
- QProperty(const QPropertyBinding<T> &binding)
+ explicit QProperty(parameter_type initialValue) : QPropertyData<T>(initialValue) {}
+ explicit QProperty(rvalue_ref initialValue) : QPropertyData<T>(std::move(initialValue)) {}
+ explicit QProperty(const QPropertyBinding<T> &binding)
: QProperty()
- { operator=(binding); }
- QProperty(QPropertyBinding<T> &&binding)
- : QProperty()
- { operator=(std::move(binding)); }
-#ifndef Q_CLANG_QDOC
+ { setBinding(binding); }
+#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&>> * = 0)
+ typename std::enable_if_t<std::is_invocable_r_v<T, Functor&>> * = nullptr)
: QProperty(QPropertyBinding<T>(std::forward<Functor>(f), location))
{}
#else
@@ -243,82 +383,78 @@ public:
#endif
~QProperty() = default;
- T value() const
+ parameter_type value() const
{
- if (d.priv.hasBinding())
- d.priv.evaluateIfDirty();
- d.priv.registerWithCurrentlyEvaluatingBinding();
- return d.getValue();
+ d.registerWithCurrentlyEvaluatingBinding();
+ return this->val;
}
- operator T() const
+ arrow_operator_result operator->() const
{
- return value();
+ if constexpr (QTypeTraits::is_dereferenceable_v<T>) {
+ return value();
+ } else if constexpr (std::is_pointer_v<T>) {
+ value();
+ return this->val;
+ } else {
+ return;
+ }
}
- void setValue(T &&newValue)
+ parameter_type operator*() const
{
- d.priv.removeBinding();
- if (d.setValueAndReturnTrueIfChanged(std::move(newValue)))
- notify();
+ return value();
}
- void setValue(const T &newValue)
+ operator parameter_type() const
{
- d.priv.removeBinding();
- if (d.setValueAndReturnTrueIfChanged(newValue))
- notify();
+ return value();
}
- QProperty<T> &operator=(T &&newValue)
+ void setValue(rvalue_ref newValue)
{
- setValue(std::move(newValue));
- return *this;
+ d.removeBinding();
+ if (is_equal(newValue))
+ return;
+ this->val = std::move(newValue);
+ notify();
}
- QProperty<T> &operator=(const T &newValue)
+ void setValue(parameter_type newValue)
{
- setValue(newValue);
- return *this;
+ d.removeBinding();
+ if (is_equal(newValue))
+ return;
+ this->val = newValue;
+ notify();
}
- QProperty<T> &operator=(const QPropertyBinding<T> &newBinding)
+ QProperty<T> &operator=(rvalue_ref newValue)
{
- setBinding(newBinding);
+ setValue(std::move(newValue));
return *this;
}
- QProperty<T> &operator=(QPropertyBinding<T> &&newBinding)
+ QProperty<T> &operator=(parameter_type newValue)
{
- setBinding(std::move(newBinding));
+ setValue(newValue);
return *this;
}
QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
{
- QPropertyBinding<T> oldBinding(d.priv.setBinding(newBinding, &d));
- notify();
- return oldBinding;
- }
-
- QPropertyBinding<T> setBinding(QPropertyBinding<T> &&newBinding)
- {
- QPropertyBinding<T> b(std::move(newBinding));
- QPropertyBinding<T> oldBinding(d.priv.setBinding(b, &d));
- notify();
- return oldBinding;
+ return QPropertyBinding<T>(d.setBinding(newBinding, this));
}
bool setBinding(const QUntypedPropertyBinding &newBinding)
{
- if (newBinding.valueMetaType().id() != qMetaTypeId<T>())
+ if (!newBinding.isNull() && newBinding.valueMetaType().id() != qMetaTypeId<T>())
return false;
- d.priv.setBinding(newBinding, &d);
- notify();
+ setBinding(static_cast<const QPropertyBinding<T> &>(newBinding));
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,
@@ -331,37 +467,48 @@ public:
QPropertyBinding<T> setBinding(Functor f);
#endif
- bool hasBinding() const { return d.priv.hasBinding(); }
+ bool hasBinding() const { return d.hasBinding(); }
QPropertyBinding<T> binding() const
{
- return QPropertyBinding<T>(*this);
+ return QPropertyBinding<T>(QUntypedPropertyBinding(d.binding()));
}
QPropertyBinding<T> takeBinding()
{
- return QPropertyBinding<T>(d.priv.setBinding(QUntypedPropertyBinding(), &d));
+ return QPropertyBinding<T>(d.setBinding(QUntypedPropertyBinding(), this));
}
template<typename Functor>
- QPropertyChangeHandler<Functor> onValueChanged(Functor f);
+ QPropertyChangeHandler<Functor> onValueChanged(Functor f)
+ {
+ static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
+ return QPropertyChangeHandler<Functor>(*this, f);
+ }
+
+ template<typename Functor>
+ QPropertyChangeHandler<Functor> subscribe(Functor f)
+ {
+ static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
+ f();
+ return onValueChanged(f);
+ }
+
template<typename Functor>
- QPropertyChangeHandler<Functor> subscribe(Functor f);
+ 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()
{
- d.priv.notifyObservers(&d);
+ d.notifyObservers(this);
}
- Q_DISABLE_COPY(QProperty)
-
- friend struct QPropertyBasePointer;
- friend class QPropertyBinding<T>;
- friend class QPropertyObserver;
- // Mutable because querying for the value may require evalating the binding expression, calling
- // non-const functions on QPropertyBase.
- mutable QtPrivate::QPropertyValueStorage<T> d;
+ Q_DISABLE_COPY_MOVE(QProperty)
};
namespace Qt {
@@ -374,448 +521,635 @@ namespace Qt {
}
}
-namespace detail {
- template <typename F>
- struct ExtractClassFromFunctionPointer;
- template<typename T, typename C>
- struct ExtractClassFromFunctionPointer<T C::*> { using Class = C; };
-}
+namespace QtPrivate
+{
-template <typename T, auto Callback, auto ValueGuard=nullptr>
-class QNotifiedProperty
+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;
+
+ static constexpr quintptr MetaTypeAccessorFlag = 0x1;
+};
+
+template<typename Property, typename = void>
+class QBindableInterfaceForProperty
+{
+ using T = typename Property::value_type;
public:
- using value_type = T;
- using Class = typename detail::ExtractClassFromFunctionPointer<decltype(Callback)>::Class;
-private:
- static bool constexpr ValueGuardModifiesArgument = std::is_invocable_r_v<bool, decltype(ValueGuard), Class, T&>;
- static bool constexpr CallbackAcceptsOldValue = std::is_invocable_v<decltype(Callback), Class, T>;
- static bool constexpr HasValueGuard = !std::is_same_v<decltype(ValueGuard), std::nullptr_t>;
+ // interface for computed 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<T*>(value) = static_cast<const Property *>(d)->value(); },
+ nullptr,
+ nullptr,
+ nullptr,
+ [](const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding
+ { return Qt::makePropertyBinding([d]() -> T { return static_cast<const Property *>(d)->value(); }, location); },
+ [](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void
+ { observer->setSource(static_cast<const Property *>(d)->bindingData()); },
+ []() { return QMetaType::fromType<T>(); }
+ };
+};
+
+template<typename Property>
+class QBindableInterfaceForProperty<const Property, std::void_t<decltype(std::declval<Property>().binding())>>
+{
+ using T = typename Property::value_type;
public:
- static_assert(CallbackAcceptsOldValue || std::is_invocable_v<decltype(Callback), Class>);
- static_assert(
- std::is_invocable_r_v<bool, decltype(ValueGuard), Class, T> ||
- ValueGuardModifiesArgument ||
- !HasValueGuard,
- "Guard has wrong signature");
-private:
- // type erased guard functions, casts its arguments to the correct types
- static constexpr bool (*GuardTEHelper)(void *, void*) = [](void *o, void *newValue){
- if constexpr (HasValueGuard) { // Guard->* is invalid if Guard == nullptr
- return (reinterpret_cast<Class *>(o)->*(ValueGuard))(*static_cast<T *>(newValue));
- } else {
- Q_UNUSED(o); // some compilers complain about unused variables
- Q_UNUSED(newValue);
- return true;
- }
+ // A bindable created from a const property results in a read-only interface, too.
+ static constexpr QBindableInterface iface = {
+
+ [](const QUntypedPropertyData *d, void *value) -> void
+ { *static_cast<T*>(value) = static_cast<const Property *>(d)->value(); },
+ /*setter=*/nullptr,
+ [](const QUntypedPropertyData *d) -> QUntypedPropertyBinding
+ { return static_cast<const Property *>(d)->binding(); },
+ /*setBinding=*/nullptr,
+ [](const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding
+ { return Qt::makePropertyBinding([d]() -> T { return static_cast<const Property *>(d)->value(); }, location); },
+ [](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void
+ { observer->setSource(static_cast<const Property *>(d)->bindingData()); },
+ []() { return QMetaType::fromType<T>(); }
};
- static constexpr bool(*GuardTE)(void *, void*) = HasValueGuard ? GuardTEHelper : nullptr;
+};
+
+template<typename Property>
+class QBindableInterfaceForProperty<Property, std::void_t<decltype(std::declval<Property>().binding())>>
+{
+ using T = typename Property::value_type;
public:
+ static constexpr QBindableInterface iface = {
+ [](const QUntypedPropertyData *d, void *value) -> void
+ { *static_cast<T*>(value) = static_cast<const Property *>(d)->value(); },
+ [](QUntypedPropertyData *d, const void *value) -> void
+ { static_cast<Property *>(d)->setValue(*static_cast<const T*>(value)); },
+ [](const QUntypedPropertyData *d) -> QUntypedPropertyBinding
+ { return static_cast<const Property *>(d)->binding(); },
+ [](QUntypedPropertyData *d, const QUntypedPropertyBinding &binding) -> QUntypedPropertyBinding
+ { return static_cast<Property *>(d)->setBinding(static_cast<const QPropertyBinding<T> &>(binding)); },
+ [](const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding
+ { return Qt::makePropertyBinding([d]() -> T { return static_cast<const Property *>(d)->value(); }, location); },
+ [](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void
+ { observer->setSource(static_cast<const Property *>(d)->bindingData()); },
+ []() { return QMetaType::fromType<T>(); }
+ };
+};
- QNotifiedProperty() = default;
+}
- explicit QNotifiedProperty(const T &initialValue) : d(initialValue) {}
- explicit QNotifiedProperty(T &&initialValue) : d(std::move(initialValue)) {}
+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);
+}
- QNotifiedProperty(Class *owner, const QPropertyBinding<T> &binding)
- : QNotifiedProperty()
- { setBinding(owner, binding); }
- QNotifiedProperty(Class *owner, QPropertyBinding<T> &&binding)
- : QNotifiedProperty()
- { setBinding(owner, std::move(binding)); }
+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);
-#ifndef Q_CLANG_QDOC
- template <typename Functor>
- explicit QNotifiedProperty(Class *owner, Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
- typename std::enable_if_t<std::is_invocable_r_v<T, Functor&>> * = 0)
- : QNotifiedProperty(QPropertyBinding<T>(owner, std::forward<Functor>(f), location))
+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;
+ constexpr QUntypedBindable(QUntypedPropertyData *d, const QtPrivate::QBindableInterface *i)
+ : data(d), iface(i)
{}
-#else
- template <typename Functor>
- explicit QProperty(Class *owner, Functor &&f);
-#endif
- ~QNotifiedProperty() = default;
+ 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);
- T value() const
+public:
+ constexpr QUntypedBindable() = default;
+ template<typename Property>
+ QUntypedBindable(Property *p)
+ : data(const_cast<std::remove_cv_t<Property> *>(p)),
+ iface(&QtPrivate::QBindableInterfaceForProperty<Property>::iface)
+ { Q_ASSERT(data && iface); }
+
+ bool isValid() const { return data != nullptr; }
+ 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) const
{
- if (d.priv.hasBinding())
- d.priv.evaluateIfDirty();
- d.priv.registerWithCurrentlyEvaluatingBinding();
- return d.getValue();
+ return iface ? iface->makeBinding(data, location) : QUntypedPropertyBinding();
}
- operator T() const
+ QUntypedPropertyBinding takeBinding()
{
- return value();
+ 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;
}
- template<typename S>
- auto setValue(Class *owner, S &&newValue) -> std::enable_if_t<!ValueGuardModifiesArgument && std::is_same_v<S, T>, void>
+ void observe(QPropertyObserver *observer) const
{
- if constexpr (HasValueGuard) {
- if (!(owner->*ValueGuard)(newValue))
- return;
- }
- if constexpr (CallbackAcceptsOldValue) {
- T oldValue = value(); // TODO: kind of pointless if there was no change
- if (d.setValueAndReturnTrueIfChanged(std::move(newValue)))
- notify(owner, &oldValue);
- } else {
- if (d.setValueAndReturnTrueIfChanged(std::move(newValue)))
- notify(owner);
- }
- d.priv.removeBinding();
+ if (iface)
+ iface->setObserver(data, observer);
+#ifndef QT_NO_DEBUG
+ else
+ QtPrivate::BindableWarnings::printUnsuitableBindableWarning("observe:",
+ QtPrivate::BindableWarnings::InvalidInterface);
+#endif
}
- void setValue(Class *owner, std::conditional_t<ValueGuardModifiesArgument, T, const T &> newValue)
+ template<typename Functor>
+ QPropertyChangeHandler<Functor> onValueChanged(Functor f) const
{
- if constexpr (HasValueGuard) {
- if (!(owner->*ValueGuard)(newValue))
- return;
- }
- if constexpr (CallbackAcceptsOldValue) {
- // When newValue is T, we move it, if it's const T& it stays const T& and won't get moved
- T oldValue = value();
- if (d.setValueAndReturnTrueIfChanged(std::move(newValue)))
- notify(owner, &oldValue);
- } else {
- if (d.setValueAndReturnTrueIfChanged(std::move(newValue)))
- notify(owner);
- }
- d.priv.removeBinding();
+ QPropertyChangeHandler<Functor> handler(f);
+ observe(&handler);
+ return handler;
}
- QPropertyBinding<T> setBinding(Class *owner, const QPropertyBinding<T> &newBinding)
+ template<typename Functor>
+ QPropertyChangeHandler<Functor> subscribe(Functor f) const
{
- if constexpr (CallbackAcceptsOldValue) {
- T oldValue = value();
- QPropertyBinding<T> oldBinding(d.priv.setBinding(newBinding, &d, owner, [](void *o, void *oldVal) {
- (reinterpret_cast<Class *>(o)->*Callback)(*reinterpret_cast<T *>(oldVal));
- }, GuardTE));
- notify(owner, &oldValue);
- return oldBinding;
- } else {
- QPropertyBinding<T> oldBinding(d.priv.setBinding(newBinding, &d, owner, [](void *o, void *) {
- (reinterpret_cast<Class *>(o)->*Callback)();
- }, GuardTE));
- notify(owner);
- return oldBinding;
- }
+ f();
+ return onValueChanged(f);
}
- QPropertyBinding<T> setBinding(Class *owner, QPropertyBinding<T> &&newBinding)
+ template<typename Functor>
+ QPropertyNotifier addNotifier(Functor f)
{
- QPropertyBinding<T> b(std::move(newBinding));
- if constexpr (CallbackAcceptsOldValue) {
- T oldValue = value();
- QPropertyBinding<T> oldBinding(d.priv.setBinding(b, &d, owner, [](void *o, void *oldVal) {
- (reinterpret_cast<Class *>(o)->*Callback)(*reinterpret_cast<T *>(oldVal));
- }, GuardTE));
- notify(owner, &oldValue);
- return oldBinding;
- } else {
- QPropertyBinding<T> oldBinding(d.priv.setBinding(b, &d, owner, [](void *o, void *) {
- (reinterpret_cast<Class *>(o)->*Callback)();
- }, GuardTE));
- notify(owner);
- return oldBinding;
- }
+ QPropertyNotifier handler(f);
+ observe(&handler);
+ return handler;
}
- bool setBinding(Class *owner, const QUntypedPropertyBinding &newBinding)
+ QUntypedPropertyBinding binding() const
+ {
+ 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 (newBinding.valueMetaType().id() != qMetaTypeId<T>())
+ 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() != metaType()) {
+#ifndef QT_NO_DEBUG
+ QtPrivate::BindableWarnings::printMetaTypeMismatch(metaType(), binding.valueMetaType());
+#endif
return false;
- if constexpr (CallbackAcceptsOldValue) {
- T oldValue = value();
- d.priv.setBinding(newBinding, &d, owner, [](void *o, void *oldVal) {
- (reinterpret_cast<Class *>(o)->*Callback)(*reinterpret_cast<T *>(oldVal));
- }, GuardTE);
- notify(owner, &oldValue);
- } else {
- d.priv.setBinding(newBinding, &d, owner, [](void *o, void *) {
- (reinterpret_cast<Class *>(o)->*Callback)();
- }, GuardTE);
- notify(owner);
}
+ iface->setBinding(data, binding);
return true;
}
+ bool hasBinding() const
+ {
+ return !binding().isNull();
+ }
-#ifndef Q_CLANG_QDOC
- template <typename Functor>
- QPropertyBinding<T> setBinding(Class *owner, Functor &&f,
- const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
- std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
+ QMetaType metaType() const
{
- return setBinding(owner, Qt::makePropertyBinding(std::forward<Functor>(f), location));
+ 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;
}
-#else
- template <typename Functor>
- QPropertyBinding<T> setBinding(Class *owner, Functor f);
-#endif
- bool hasBinding() const { return d.priv.hasBinding(); }
+};
+
+template<typename T>
+class QBindable : public QUntypedBindable
+{
+ template<typename U>
+ 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 && 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));
+ }
QPropertyBinding<T> binding() const
{
- return QPropertyBinding<T>(*this);
+ return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::binding());
}
QPropertyBinding<T> takeBinding()
{
- return QPropertyBinding<T>(d.priv.setBinding(QUntypedPropertyBinding(), &d));
+ return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::takeBinding());
}
- template<typename Functor>
- QPropertyChangeHandler<Functor> onValueChanged(Functor f);
- template<typename Functor>
- QPropertyChangeHandler<Functor> subscribe(Functor f);
+ using QUntypedBindable::setBinding;
+ QPropertyBinding<T> setBinding(const QPropertyBinding<T> &binding)
+ {
+ 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_QDOC
+ template <typename Functor>
+ QPropertyBinding<T> setBinding(Functor &&f,
+ const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
+ std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
+ {
+ return setBinding(Qt::makePropertyBinding(std::forward<Functor>(f), location));
+ }
+#else
+ template <typename Functor>
+ QPropertyBinding<T> setBinding(Functor f);
+#endif
-private:
- void notify(Class *owner, T *oldValue=nullptr)
+ T value() const
{
- d.priv.notifyObservers(&d);
- if constexpr (std::is_invocable_v<decltype(Callback), Class>) {
- Q_UNUSED(oldValue);
- (owner->*Callback)();
- } else {
- (owner->*Callback)(*oldValue);
+ if (iface) {
+ T result;
+ iface->getter(data, &result);
+ return result;
}
+ return T{};
}
- Q_DISABLE_COPY_MOVE(QNotifiedProperty)
-
- friend class QPropertyBinding<T>;
- friend class QPropertyObserver;
- // Mutable because querying for the value may require evalating the binding expression, calling
- // non-const functions on QPropertyBase.
- mutable QtPrivate::QPropertyValueStorage<T> d;
+ void setValue(const T &value)
+ {
+ if (iface && iface->setter)
+ iface->setter(data, &value);
+ }
};
-struct QPropertyObserverPrivate;
-struct QPropertyObserverPointer;
-
-class Q_CORE_EXPORT QPropertyObserver
+#if QT_DEPRECATED_SINCE(6, 6)
+template<typename T>
+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
{
-public:
- // Internal
- enum ObserverTag {
- ObserverNotifiesBinding,
- ObserverNotifiesChangeHandler,
- ObserverNotifiesAlias,
- };
+ Q_DISABLE_COPY_MOVE(QPropertyAlias)
+ const QtPrivate::QBindableInterface *iface = nullptr;
- QPropertyObserver();
- QPropertyObserver(QPropertyObserver &&other);
- QPropertyObserver &operator=(QPropertyObserver &&other);
- ~QPropertyObserver();
+public:
+ QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
+ QPropertyAlias(QProperty<T> *property)
+ : QPropertyObserver(property),
+ iface(&QtPrivate::QBindableInterfaceForProperty<QProperty<T>>::iface)
+ {
+ if (iface)
+ iface->setObserver(aliasedProperty(), this);
+ }
- template <typename PropertyType>
- void setSource(const QProperty<PropertyType> &property)
- { setSource(property.d.priv); }
+ template <typename Property, QtPrivate::IsUntypedPropertyData<Property> = true>
+ QPropertyAlias(Property *property)
+ : QPropertyObserver(property),
+ iface(&QtPrivate::QBindableInterfaceForProperty<Property>::iface)
+ {
+ if (iface)
+ iface->setObserver(aliasedProperty(), this);
+ }
- template <typename PropertyType, auto notifier, auto guard>
- void setSource(const QNotifiedProperty<PropertyType, notifier, guard> &property)
- { setSource(property.d.priv); }
+ QPropertyAlias(QPropertyAlias<T> *alias)
+ : QPropertyObserver(alias->aliasedProperty()),
+ iface(alias->iface)
+ {
+ if (iface)
+ iface->setObserver(aliasedProperty(), this);
+ }
-protected:
- QPropertyObserver(void (*callback)(QPropertyObserver*, void *));
- QPropertyObserver(void *aliasedPropertyPtr);
+ QPropertyAlias(const QBindable<T> &property)
+ : QPropertyObserver(property.data),
+ iface(property.iface)
+ {
+ if (iface)
+ iface->setObserver(aliasedProperty(), this);
+ }
- template<typename PropertyType>
- QProperty<PropertyType> *aliasedProperty() const
+ T value() const
{
- return reinterpret_cast<QProperty<PropertyType> *>(aliasedPropertyPtr);
+ T t = T();
+ if (auto *p = aliasedProperty())
+ iface->getter(p, &t);
+ return t;
}
-private:
- void setSource(QtPrivate::QPropertyBase &property);
+ operator T() const { return value(); }
- QTaggedPointer<QPropertyObserver, ObserverTag> 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<QPropertyObserver, ObserverTag> prev;
+ void setValue(const T &newValue)
+ {
+ if (auto *p = aliasedProperty())
+ iface->setter(p, &newValue);
+ }
- union {
- QPropertyBindingPrivate *bindingToMarkDirty = nullptr;
- void (*changeHandler)(QPropertyObserver*, void *);
- quintptr aliasedPropertyPtr;
- };
+ QPropertyAlias<T> &operator=(const T &newValue)
+ {
+ setValue(newValue);
+ return *this;
+ }
- QPropertyObserver(const QPropertyObserver &) = delete;
- QPropertyObserver &operator=(const QPropertyObserver &) = delete;
+ QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
+ {
+ return QBindable<T>(aliasedProperty(), iface).setBinding(newBinding);
+ }
- friend struct QPropertyObserverPointer;
- friend struct QPropertyBasePointer;
- friend class QPropertyBindingPrivate;
-};
+ bool setBinding(const QUntypedPropertyBinding &newBinding)
+ {
+ return QBindable<T>(aliasedProperty(), iface).setBinding(newBinding);
+ }
-template <typename Functor>
-class QPropertyChangeHandler : public QPropertyObserver
-{
- Functor m_handler;
-public:
- QPropertyChangeHandler(Functor handler)
- : QPropertyObserver([](QPropertyObserver *self, void *) {
- auto This = static_cast<QPropertyChangeHandler<Functor>*>(self);
- This->m_handler();
- })
- , m_handler(handler)
+#ifndef Q_QDOC
+ template <typename Functor>
+ QPropertyBinding<T> setBinding(Functor &&f,
+ const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
+ std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
{
+ return setBinding(Qt::makePropertyBinding(std::forward<Functor>(f), location));
}
+#else
+ template <typename Functor>
+ QPropertyBinding<T> setBinding(Functor f);
+#endif
- template <typename PropertyType>
- QPropertyChangeHandler(const QProperty<PropertyType> &property, Functor handler)
- : QPropertyObserver([](QPropertyObserver *self, void *) {
- auto This = static_cast<QPropertyChangeHandler<Functor>*>(self);
- This->m_handler();
- })
- , m_handler(handler)
+ bool hasBinding() const
{
- setSource(property);
+ return QBindable<T>(aliasedProperty(), iface).hasBinding();
}
- template <typename PropertyType, auto Callback, auto Guard>
- QPropertyChangeHandler(const QNotifiedProperty<PropertyType, Callback, Guard> &property, Functor handler)
- : QPropertyObserver([](QPropertyObserver *self, void *) {
- auto This = static_cast<QPropertyChangeHandler<Functor>*>(self);
- This->m_handler();
- })
- , m_handler(handler)
+ QPropertyBinding<T> binding() const
{
- setSource(property);
+ return QBindable<T>(aliasedProperty(), iface).binding();
}
-};
-template <typename T>
-template<typename Functor>
-QPropertyChangeHandler<Functor> QProperty<T>::onValueChanged(Functor f)
-{
-#if defined(__cpp_lib_is_invocable) && (__cpp_lib_is_invocable >= 201703L)
- static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
-#endif
- return QPropertyChangeHandler<Functor>(*this, f);
-}
+ QPropertyBinding<T> takeBinding()
+ {
+ return QBindable<T>(aliasedProperty(), iface).takeBinding();
+ }
-template <typename T>
-template<typename Functor>
-QPropertyChangeHandler<Functor> QProperty<T>::subscribe(Functor f)
-{
-#if defined(__cpp_lib_is_invocable) && (__cpp_lib_is_invocable >= 201703L)
- static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
-#endif
- f();
- return onValueChanged(f);
-}
+ template<typename Functor>
+ QPropertyChangeHandler<Functor> onValueChanged(Functor f)
+ {
+ return QBindable<T>(aliasedProperty(), iface).onValueChanged(f);
+ }
-template <typename T, auto Callback, auto ValueGuard>
-template<typename Functor>
-QPropertyChangeHandler<Functor> QNotifiedProperty<T, Callback, ValueGuard>::onValueChanged(Functor f)
-{
-#if defined(__cpp_lib_is_invocable) && (__cpp_lib_is_invocable >= 201703L)
- static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
-#endif
- return QPropertyChangeHandler<Functor>(*this, f);
-}
+ template<typename Functor>
+ QPropertyChangeHandler<Functor> subscribe(Functor f)
+ {
+ return QBindable<T>(aliasedProperty(), iface).subscribe(f);
+ }
-template <typename T, auto Callback, auto ValueGuard>
-template<typename Functor>
-QPropertyChangeHandler<Functor> QNotifiedProperty<T, Callback, ValueGuard>::subscribe(Functor f)
-{
-#if defined(__cpp_lib_is_invocable) && (__cpp_lib_is_invocable >= 201703L)
- static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
-#endif
- f();
- return onValueChanged(f);
-}
+ template<typename Functor>
+ QPropertyNotifier addNotifier(Functor f)
+ {
+ return QBindable<T>(aliasedProperty(), iface).addNotifier(f);
+ }
-template<typename T>
-class QPropertyAlias : public QPropertyObserver
-{
- Q_DISABLE_COPY_MOVE(QPropertyAlias)
-public:
- QPropertyAlias(QProperty<T> *property)
- : QPropertyObserver(property)
+ bool isValid() const
{
- if (property)
- setSource(*property);
+ return aliasedProperty() != nullptr;
}
+ QT_WARNING_POP
+};
+#endif // QT_DEPRECATED_SINCE(6, 6)
- QPropertyAlias(QPropertyAlias<T> *alias)
- : QPropertyAlias(alias->aliasedProperty<T>())
+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);
+ return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
+ }
+ const Class *owner() const
+ {
+ char *that = const_cast<char *>(reinterpret_cast<const char *>(this));
+ return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
+ }
+ static void signalCallBack(QUntypedPropertyData *o)
+ {
+ QObjectBindableProperty *that = static_cast<QObjectBindableProperty *>(o);
+ 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;
+ using parameter_type = typename QPropertyData<T>::parameter_type;
+ using rvalue_ref = typename QPropertyData<T>::rvalue_ref;
+ using arrow_operator_result = typename QPropertyData<T>::arrow_operator_result;
+
+ QObjectBindableProperty() = default;
+ explicit QObjectBindableProperty(const T &initialValue) : QPropertyData<T>(initialValue) {}
+ explicit QObjectBindableProperty(T &&initialValue) : QPropertyData<T>(std::move(initialValue)) {}
+ explicit QObjectBindableProperty(const QPropertyBinding<T> &binding)
+ : QObjectBindableProperty()
+ { setBinding(binding); }
+#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)
+ : QObjectBindableProperty(QPropertyBinding<T>(std::forward<Functor>(f), location))
{}
+#else
+ template <typename Functor>
+ explicit QObjectBindableProperty(Functor &&f);
+#endif
- T value() const
+ parameter_type value() const
{
- if (auto *p = aliasedProperty<T>())
- return p->value();
- return T();
+ qGetBindingStorage(owner())->registerDependency(this);
+ return this->val;
}
- operator T() const { return value(); }
-
- void setValue(T &&newValue)
+ arrow_operator_result operator->() const
{
- if (auto *p = aliasedProperty<T>())
- p->setValue(std::move(newValue));
+ if constexpr (QTypeTraits::is_dereferenceable_v<T>) {
+ return value();
+ } else if constexpr (std::is_pointer_v<T>) {
+ value();
+ return this->val;
+ } else {
+ return;
+ }
}
- void setValue(const T &newValue)
+ parameter_type operator*() const
{
- if (auto *p = aliasedProperty<T>())
- p->setValue(newValue);
+ return value();
}
- QPropertyAlias<T> &operator=(T &&newValue)
+ operator parameter_type() const
{
- if (auto *p = aliasedProperty<T>())
- *p = std::move(newValue);
- return *this;
+ return value();
}
- QPropertyAlias<T> &operator=(const T &newValue)
+ void setValue(parameter_type t)
{
- if (auto *p = aliasedProperty<T>())
- *p = newValue;
- return *this;
+ auto *bd = qGetBindingStorage(owner())->bindingData(this);
+ if (bd)
+ bd->removeBinding();
+ if (this->val == t)
+ return;
+ this->val = t;
+ notify(bd);
+ }
+
+ void notify() {
+ auto *bd = qGetBindingStorage(owner())->bindingData(this);
+ notify(bd);
}
- QPropertyAlias<T> &operator=(const QPropertyBinding<T> &newBinding)
+ void setValue(rvalue_ref t)
{
- setBinding(newBinding);
- return *this;
+ auto *bd = qGetBindingStorage(owner())->bindingData(this);
+ if (bd)
+ bd->removeBinding();
+ if (this->val == t)
+ return;
+ this->val = std::move(t);
+ notify(bd);
}
- QPropertyAlias<T> &operator=(QPropertyBinding<T> &&newBinding)
+ QObjectBindableProperty &operator=(rvalue_ref newValue)
{
- setBinding(std::move(newBinding));
+ setValue(std::move(newValue));
return *this;
}
- QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
+ QObjectBindableProperty &operator=(parameter_type newValue)
{
- if (auto *p = aliasedProperty<T>())
- return p->setBinding(newBinding);
- return QPropertyBinding<T>();
+ setValue(newValue);
+ return *this;
}
- QPropertyBinding<T> setBinding(QPropertyBinding<T> &&newBinding)
+ QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
{
- if (auto *p = aliasedProperty<T>())
- return p->setBinding(std::move(newBinding));
- return QPropertyBinding<T>();
+ QtPrivate::QPropertyBindingData *bd = qGetBindingStorage(owner())->bindingData(this, true);
+ QUntypedPropertyBinding oldBinding(bd->setBinding(newBinding, this, HasSignal ? &signalCallBack : nullptr));
+ return static_cast<QPropertyBinding<T> &>(oldBinding);
}
bool setBinding(const QUntypedPropertyBinding &newBinding)
{
- if (auto *p = aliasedProperty<T>())
- return p->setBinding(newBinding);
- return false;
+ if (!newBinding.isNull() && newBinding.valueMetaType().id() != qMetaTypeId<T>())
+ return false;
+ setBinding(static_cast<const QPropertyBinding<T> &>(newBinding));
+ 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,
@@ -830,46 +1164,204 @@ public:
bool hasBinding() const
{
- if (auto *p = aliasedProperty<T>())
- return p->hasBinding();
- return false;
+ auto *bd = qGetBindingStorage(owner())->bindingData(this);
+ return bd && bd->binding() != nullptr;
}
QPropertyBinding<T> binding() const
{
- if (auto *p = aliasedProperty<T>())
- return p->binding();
- return QPropertyBinding<T>();
+ auto *bd = qGetBindingStorage(owner())->bindingData(this);
+ return static_cast<QPropertyBinding<T> &&>(QUntypedPropertyBinding(bd ? bd->binding() : nullptr));
}
QPropertyBinding<T> takeBinding()
{
- if (auto *p = aliasedProperty<T>())
- return p->takeBinding();
- return QPropertyBinding<T>();
+ return setBinding(QPropertyBinding<T>());
}
template<typename Functor>
QPropertyChangeHandler<Functor> onValueChanged(Functor f)
{
- if (auto *p = aliasedProperty<T>())
- return p->onValueChanged(f);
- return QPropertyChangeHandler<Functor>(f);
+ static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
+ return QPropertyChangeHandler<Functor>(*this, f);
}
template<typename Functor>
QPropertyChangeHandler<Functor> subscribe(Functor f)
{
- if (auto *p = aliasedProperty<T>())
- return p->subscribe(f);
- return QPropertyChangeHandler<Functor>(f);
+ static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
+ f();
+ return onValueChanged(f);
}
- bool isValid() const
+ template<typename Functor>
+ QPropertyNotifier addNotifier(Functor f)
{
- return aliasedProperty<T>() != nullptr;
+ 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()));
+ return *storage->bindingData(const_cast<ThisType *>(this), true);
+ }
+private:
+ void notify(const QtPrivate::QPropertyBindingData *binding)
+ {
+ if (binding)
+ binding->notifyObservers(this, qGetBindingStorage(owner()));
+ if constexpr (HasSignal) {
+ if constexpr (SignalTakesValue::value)
+ (owner()->*Signal)(this->valueBypassingBindings());
+ else
+ (owner()->*Signal)();
+ }
}
};
+
+#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;
+
+#define Q_OBJECT_BINDABLE_PROPERTY(...) \
+ QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
+ QT_OVERLOADED_MACRO(QT_OBJECT_BINDABLE_PROPERTY, __VA_ARGS__) \
+ QT_WARNING_POP
+
+#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 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>( \
+ value);
+
+#define Q_OBJECT_BINDABLE_PROPERTY_WITH_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
+{
+ Class *owner()
+ {
+ char *that = reinterpret_cast<char *>(this);
+ return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
+ }
+ const Class *owner() const
+ {
+ char *that = const_cast<char *>(reinterpret_cast<const char *>(this));
+ return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
+ }
+
+public:
+ using value_type = T;
+ using parameter_type = T;
+
+ QObjectComputedProperty() = default;
+
+ parameter_type value() const
+ {
+ qGetBindingStorage(owner())->registerDependency(this);
+ return (owner()->*Getter)();
+ }
+
+ std::conditional_t<QTypeTraits::is_dereferenceable_v<T>, parameter_type, void>
+ operator->() const
+ {
+ if constexpr (QTypeTraits::is_dereferenceable_v<T>)
+ return value();
+ else
+ return;
+ }
+
+ parameter_type operator*() const
+ {
+ return value();
+ }
+
+ operator parameter_type() const
+ {
+ return value();
+ }
+
+ constexpr bool hasBinding() const { return false; }
+
+ template<typename Functor>
+ QPropertyChangeHandler<Functor> onValueChanged(Functor f)
+ {
+ static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
+ return QPropertyChangeHandler<Functor>(*this, f);
+ }
+
+ template<typename Functor>
+ QPropertyChangeHandler<Functor> subscribe(Functor f)
+ {
+ static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
+ f();
+ 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 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)
+ bd->notifyObservers(this, qGetBindingStorage(owner()));
+ }
+};
+
+#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<Class, Type, Class::_qt_property_##name##_offset, __VA_ARGS__> name;
+
+#undef QT_SOURCE_LOCATION_NAMESPACE
+
QT_END_NAMESPACE
#endif // QPROPERTY_H