aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmlanybinding_p.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml/qqmlanybinding_p.h')
-rw-r--r--src/qml/qml/qqmlanybinding_p.h473
1 files changed, 473 insertions, 0 deletions
diff --git a/src/qml/qml/qqmlanybinding_p.h b/src/qml/qml/qqmlanybinding_p.h
new file mode 100644
index 0000000000..f432d2abae
--- /dev/null
+++ b/src/qml/qml/qqmlanybinding_p.h
@@ -0,0 +1,473 @@
+// Copyright (C) 2021 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 QQMLANYBINDINGPTR_P_H
+#define QQMLANYBINDINGPTR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qqmlproperty.h>
+#include <private/qqmlpropertybinding_p.h>
+#include <private/qqmlbinding_p.h>
+
+QT_BEGIN_NAMESPACE
+
+// Fully inline so that subsequent prop.isBindable check might get ellided.
+
+/*!
+ \internal
+ \brief QQmlAnyBinding is an abstraction over the various bindings in QML
+
+ QQmlAnyBinding can store both classical bindings (derived from QQmlAbstractBinding)
+ as well as new-style bindings (derived from QPropertyBindingPrivate). For both, it keeps
+ a strong reference to them, and knows how to delete them in case the reference count
+ becomes zero. In that sense it can be thought of as a union of QUntypedPropertyBinding
+ and QQmlAbstractBinding::Ptr.
+
+ It also offers methods to create bindings (from QV4::Function, from translation bindings
+ and from code strings). Moreover, it allows the retrieval, the removal and the
+ installation of bindings on a QQmlProperty.
+
+ Note that the class intentionally does not allow construction from QUntypedProperty and
+ QQmlAbstractBinding::Ptr. This is meant to catch code which doesn't handle bindable properties
+ yet when porting existing code.
+ */
+class QQmlAnyBinding {
+public:
+
+ constexpr QQmlAnyBinding() noexcept = default;
+ QQmlAnyBinding(std::nullptr_t) : d(static_cast<QQmlAbstractBinding *>(nullptr)) {}
+
+ /*!
+ \internal
+ Returns the binding of the property \a prop as a QQmlAnyBinding.
+ The binding continues to be active and set on the property.
+ If there was no binding set, the returned QQmlAnyBinding is null.
+ */
+ static QQmlAnyBinding ofProperty(const QQmlProperty &prop) {
+ QQmlAnyBinding binding;
+ if (prop.isBindable()) {
+ QUntypedBindable bindable = prop.property().bindable(prop.object());
+ binding = bindable.binding();
+ } else {
+ binding = QQmlPropertyPrivate::binding(prop);
+ }
+ return binding;
+ }
+
+ /*!
+ \overload
+
+ \a object must be non-null
+ */
+ static QQmlAnyBinding ofProperty(QObject *object, QQmlPropertyIndex index)
+ {
+ QQmlAnyBinding binding;
+ Q_ASSERT(object);
+ auto coreIndex = index.coreIndex();
+ // we don't support bindable properties on value types so far
+ if (!index.hasValueTypeIndex()
+ && QQmlData::ensurePropertyCache(object)->property(coreIndex)->isBindable()) {
+ auto metaProp = object->metaObject()->property(coreIndex);
+ QUntypedBindable bindable = metaProp.bindable(object);
+ binding = bindable.binding();
+ } else {
+ binding = QQmlPropertyPrivate::binding(object, index);
+ }
+ return binding;
+ }
+
+ /*!
+ Removes the binding from the property \a prop, and returns it as a
+ QQmlAnyBinding if there was any. Otherwise returns a null
+ QQmlAnyBinding.
+ */
+ static QQmlAnyBinding takeFrom(const QQmlProperty &prop)
+ {
+ QQmlAnyBinding binding;
+ if (prop.isBindable()) {
+ QUntypedBindable bindable = prop.property().bindable(prop.object());
+ binding = bindable.takeBinding();
+ } else {
+ auto qmlBinding = QQmlPropertyPrivate::binding(prop);
+ if (qmlBinding) {
+ binding = qmlBinding; // this needs to run before removeFromObject, else the refcount might reach zero
+ qmlBinding->setEnabled(false, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
+ qmlBinding->removeFromObject();
+ }
+ }
+ return binding;
+ }
+
+ /*!
+ \internal
+ Creates a binding for property \a prop from \a function.
+ \a obj is the scope object which shall be used for the function and \a scope its QML scope.
+ The binding is not installed on the property (but if a QQmlBinding is created, it has its
+ target set to \a prop).
+ */
+ static QQmlAnyBinding createFromFunction(const QQmlProperty &prop, QV4::Function *function,
+ QObject *obj, const QQmlRefPointer<QQmlContextData> &ctxt,
+ QV4::ExecutionContext *scope)
+ {
+ QQmlAnyBinding binding;
+ auto propPriv = QQmlPropertyPrivate::get(prop);
+ if (prop.isBindable()) {
+ auto index = QQmlPropertyIndex(propPriv->core.coreIndex(), -1);
+ binding = QQmlPropertyBinding::create(&propPriv->core,
+ function, obj, ctxt,
+ scope, prop.object(), index);
+ } else {
+ auto qmlBinding = QQmlBinding::create(&propPriv->core, function, obj, ctxt, scope);
+ qmlBinding->setTarget(prop);
+ binding = qmlBinding;
+ }
+ return binding;
+ }
+
+ /*!
+ \internal
+ Creates a binding for property \a prop from \a script.
+ \a obj is the scope object which shall be used for the function and \a ctxt its QML scope.
+ The binding is not installed on the property (but if a QQmlBinding is created, it has its
+ target set to \a prop).
+ */
+ static QQmlAnyBinding createFromScriptString(const QQmlProperty &prop, const QQmlScriptString &script,
+ QObject *obj, QQmlContext *ctxt)
+ {
+ QQmlAnyBinding binding;
+ auto propPriv = QQmlPropertyPrivate::get(prop);
+ if (prop.isBindable()) {
+ auto index = QQmlPropertyIndex(propPriv->core.coreIndex(), -1);
+ binding = QQmlPropertyBinding::createFromScriptString(&propPriv->core, script, obj, ctxt, prop.object(), index);
+ } else {
+ auto qmlBinding = QQmlBinding::create(&propPriv->core, script, obj, ctxt);
+ qmlBinding->setTarget(prop);
+ binding = qmlBinding;
+ }
+ return binding;
+ }
+
+
+ /*!
+ \internal
+ Removes the binding from \a prop if there is any.
+ */
+ static void removeBindingFrom(QQmlProperty &prop)
+ {
+ if (prop.isBindable())
+ prop.property().bindable(prop.object()).takeBinding();
+ else
+ QQmlPropertyPrivate::removeBinding(prop);
+ }
+
+ /*!
+ \internal
+ Creates a binding for property \a prop from \a function.
+ \a obj is the scope object which shall be used for the function and \a scope its QML scope.
+ The binding is not installed on the property (but if a QQmlBinding is created, it has its
+ target set to \a prop).
+ */
+ static QQmlAnyBinding createFromCodeString(const QQmlProperty &prop, const QString& code, QObject *obj, const QQmlRefPointer<QQmlContextData> &ctxt, const QString &url, quint16 lineNumber) {
+ QQmlAnyBinding binding;
+ auto propPriv = QQmlPropertyPrivate::get(prop);
+ if (prop.isBindable()) {
+ auto index = QQmlPropertyIndex(propPriv->core.coreIndex(), -1);
+ binding = QQmlPropertyBinding::createFromCodeString(&propPriv->core,
+ code, obj, ctxt,
+ url, lineNumber,
+ prop.object(), index);
+ } else {
+ auto qmlBinding = QQmlBinding::create(&propPriv->core, code, obj, ctxt, url, lineNumber);
+ qmlBinding->setTarget(prop);
+ binding = qmlBinding;
+ }
+ return binding;
+ }
+
+ /*!
+ \internal
+ Creates a translattion binding for \a prop from \a compilationUnit and \a transationBinding.
+ \a obj is the context object, \a context the qml context.
+ */
+ static QQmlAnyBinding createTranslationBinding(const QQmlProperty &prop, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *translationBinding, QObject *scopeObject=nullptr, QQmlRefPointer<QQmlContextData> context={})
+ {
+ QQmlAnyBinding binding;
+ auto propPriv = QQmlPropertyPrivate::get(prop);
+ if (prop.isBindable()) {
+ binding = QQmlTranslationPropertyBinding::create(&propPriv->core, compilationUnit, translationBinding);
+ } else {
+ auto qmlBinding = QQmlBinding::createTranslationBinding(compilationUnit, translationBinding, scopeObject, context);
+ binding = qmlBinding;
+ qmlBinding->setTarget(prop);
+ }
+ return binding;
+ }
+
+ /*!
+ \internal
+ Installs the binding referenced by this QQmlAnyBinding on the target.
+ If \a mode is set to RespectInterceptors, interceptors are honored, otherwise
+ writes and binding installation bypass them (the default).
+ Preconditions:
+ - The binding is non-null.
+ - If the binding is QQmlAbstractBinding derived, the target is non-bindable.
+ - If the binding is a QUntypedPropertyBinding, then the target is bindable.
+ */
+ enum InterceptorMode : bool {
+ IgnoreInterceptors,
+ RespectInterceptors
+ };
+
+ void installOn(const QQmlProperty &target, InterceptorMode mode = IgnoreInterceptors)
+ {
+ Q_ASSERT(!d.isNull());
+ if (isAbstractPropertyBinding()) {
+ auto abstractBinding = asAbstractBinding();
+ Q_ASSERT(abstractBinding->targetObject() == target.object() || QQmlPropertyPrivate::get(target)->core.isAlias());
+ Q_ASSERT(!target.isBindable());
+ if (mode == IgnoreInterceptors)
+ QQmlPropertyPrivate::setBinding(abstractBinding, QQmlPropertyPrivate::None, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
+ else
+ QQmlPropertyPrivate::setBinding(abstractBinding);
+ } else {
+ Q_ASSERT(target.isBindable());
+ QUntypedBindable bindable;
+ void *argv[] = {&bindable};
+ if (mode == IgnoreInterceptors) {
+ target.object()->qt_metacall(QMetaObject::BindableProperty, target.index(), argv);
+ } else {
+ QMetaObject::metacall(target.object(), QMetaObject::BindableProperty, target.index(), argv);
+ }
+ bindable.setBinding(asUntypedPropertyBinding());
+ }
+ }
+
+ /*!
+ \internal
+ Returns true if the binding is in an error state (e.g. binding loop), false otherwise.
+
+ \note For ValueTypeProxyBindings, this methods will always return false
+ */
+ bool hasError() {
+ if (isAbstractPropertyBinding()) {
+ auto abstractBinding = asAbstractBinding();
+ if (abstractBinding->kind() != QQmlAbstractBinding::QmlBinding)
+ return false;
+ return static_cast<QQmlBinding *>(abstractBinding)->hasError();
+ } else {
+ return asUntypedPropertyBinding().error().hasError();
+ }
+ }
+
+ /*!
+ Stores a null binding. For purpose of classification, the null bindings is
+ treated as a QQmlAbstractPropertyBindings.
+ */
+ QQmlAnyBinding &operator=(std::nullptr_t)
+ {
+ clear();
+ return *this;
+ }
+
+ operator bool() const{
+ return !d.isNull();
+ }
+
+ /*!
+ \internal
+ Returns true if a binding derived from QQmlAbstractPropertyBinding is stored.
+ The binding migh still be null.
+ */
+ bool isAbstractPropertyBinding() const
+ { return d.isT1(); }
+
+ /*!
+ \internal
+ Returns true if a binding derived from QPropertyBindingPrivate is stored.
+ The binding might still be null.
+ */
+ bool isUntypedPropertyBinding() const
+ { return d.isT2(); }
+
+ /*!
+ \internal
+ Returns the stored QPropertyBindingPrivate as a QUntypedPropertyBinding.
+ If no such binding is currently stored, a null QUntypedPropertyBinding is returned.
+ */
+ QUntypedPropertyBinding asUntypedPropertyBinding() const
+ {
+ if (d.isT1() || d.isNull())
+ return {};
+ auto priv = d.asT2();
+ return QUntypedPropertyBinding {priv};
+ }
+
+ /*!
+ \internal
+ Returns the stored QQmlAbstractBinding.
+ If no such binding is currently stored, a null pointer is returned.
+ */
+ QQmlAbstractBinding *asAbstractBinding() const
+ {
+ if (d.isT2() || d.isNull())
+ return nullptr;
+ return d.asT1();
+ }
+
+ /*!
+ \internal
+ Reevaluates the binding. If the binding was disabled,
+ it gets enabled.
+ */
+ void refresh()
+ {
+ if (d.isNull())
+ return;
+ if (d.isT1()) {
+ auto binding = static_cast<QQmlBinding *>(d.asT1());
+ binding->setEnabledFlag(true);
+ binding->refresh();
+ } else {
+ auto bindingPriv = d.asT2();
+ PendingBindingObserverList bindingObservers;
+ bindingPriv->evaluateRecursive(bindingObservers);
+ bindingPriv->notifyNonRecursive(bindingObservers);
+ }
+
+ }
+
+ /*!
+ \internal
+ Stores \a binding and keeps a reference to it.
+ */
+ QQmlAnyBinding &operator=(QQmlAbstractBinding *binding)
+ {
+ clear();
+ if (binding) {
+ d = binding;
+ binding->ref.ref();
+ }
+ return *this;
+ }
+
+ /*!
+ \internal
+ Stores the binding stored in \a binding and keeps a reference to it.
+ */
+ QQmlAnyBinding &operator=(const QQmlAbstractBinding::Ptr &binding)
+ {
+ clear();
+ if (binding) {
+ d = binding.data();
+ binding->ref.ref();
+ }
+ return *this;
+ }
+
+ /*!
+ \internal
+ Stores \a binding's binding, taking ownership from \a binding.
+ */
+ QQmlAnyBinding &operator=(QQmlAbstractBinding::Ptr &&binding)
+ {
+ clear();
+ if (binding) {
+ d = binding.take();
+ }
+ return *this;
+ }
+
+ /*!
+ \internal
+ Stores the binding stored in \a untypedBinding and keeps a reference to it.
+ */
+ QQmlAnyBinding &operator=(const QUntypedPropertyBinding &untypedBinding)
+ {
+ clear();
+ auto binding = QPropertyBindingPrivate::get(untypedBinding);
+ if (binding) {
+ d = binding;
+ binding->addRef();
+ }
+ return *this;
+ }
+
+ /*!
+ \internal
+ \overload
+ Stores the binding stored in \a untypedBinding, taking ownership from it.
+ */
+ QQmlAnyBinding &operator=(QUntypedPropertyBinding &&untypedBinding)
+ {
+ clear();
+ auto binding = QPropertyBindingPrivate::get(untypedBinding);
+ QPropertyBindingPrivatePtr ptr(binding);
+ if (binding) {
+ d = static_cast<QPropertyBindingPrivate *>(ptr.take());
+ }
+ return *this;
+ }
+
+ QQmlAnyBinding(QQmlAnyBinding &&other) noexcept
+ : d(std::exchange(other.d, QBiPointer<QQmlAbstractBinding, QPropertyBindingPrivate>()))
+ {}
+
+ QQmlAnyBinding(const QQmlAnyBinding &other) noexcept { *this = other; }
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QQmlAnyBinding)
+
+ void swap(QQmlAnyBinding &other) noexcept { d.swap(other.d); }
+ friend void swap(QQmlAnyBinding &lhs, QQmlAnyBinding &rhs) noexcept { lhs.swap(rhs); }
+
+ QQmlAnyBinding &operator=(const QQmlAnyBinding &other) noexcept
+ {
+ clear();
+ if (auto abstractBinding = other.asAbstractBinding())
+ *this = abstractBinding;
+ else if (auto untypedBinding = other.asUntypedPropertyBinding(); !untypedBinding.isNull())
+ *this = untypedBinding;
+ return *this;
+ }
+
+ friend inline bool operator==(const QQmlAnyBinding &p1, const QQmlAnyBinding &p2)
+ {
+ return p1.d == p2.d;
+ }
+
+ friend inline bool operator!=(const QQmlAnyBinding &p1, const QQmlAnyBinding &p2)
+ {
+ return p1.d != p2.d;
+ }
+
+ ~QQmlAnyBinding() noexcept { clear(); }
+private:
+ void clear() noexcept {
+ if (d.isNull())
+ return;
+ if (d.isT1()) {
+ QQmlAbstractBinding *qqmlptr = d.asT1();
+ if (!qqmlptr->ref.deref())
+ delete qqmlptr;
+ } else if (d.isT2()) {
+ QPropertyBindingPrivate *priv = d.asT2();
+ if (!priv->deref())
+ QPropertyBindingPrivate::destroyAndFreeMemory(priv);
+ }
+ d = static_cast<QQmlAbstractBinding *>(nullptr);
+ }
+ QBiPointer<QQmlAbstractBinding, QPropertyBindingPrivate> d;
+};
+
+QT_END_NAMESPACE
+
+
+#endif // QQMLANYBINDINGPTR_P_H