aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmlpropertytopropertybinding.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml/qqmlpropertytopropertybinding.cpp')
-rw-r--r--src/qml/qml/qqmlpropertytopropertybinding.cpp128
1 files changed, 128 insertions, 0 deletions
diff --git a/src/qml/qml/qqmlpropertytopropertybinding.cpp b/src/qml/qml/qqmlpropertytopropertybinding.cpp
new file mode 100644
index 0000000000..ec77e8c97a
--- /dev/null
+++ b/src/qml/qml/qqmlpropertytopropertybinding.cpp
@@ -0,0 +1,128 @@
+// Copyright (C) 2022 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
+
+#include "qqmlpropertytopropertybinding_p.h"
+#include <private/qqmlvmemetaobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ * \internal
+ * \class QQmlPropertyToPropertyBinding
+ *
+ * This class can be used to create a direct binding from a source property to
+ * a target property, without going through QQmlJavaScriptExpression and
+ * QV4::Function. In particular you don't need a compilation unit or byte code
+ * to set this up.
+ */
+
+QQmlPropertyToPropertyBinding::QQmlPropertyToPropertyBinding(
+ QQmlEngine *engine, QObject *sourceObject, int sourcePropertyIndex,
+ QObject *targetObject, int targetPropertyIndex)
+ : QQmlNotifierEndpoint(QQmlPropertyGuard)
+ , m_engine(engine)
+ , m_sourceObject(sourceObject)
+ , m_sourcePropertyIndex(sourcePropertyIndex)
+{
+ setTarget(targetObject, targetPropertyIndex, false, -1);
+}
+
+QQmlAbstractBinding::Kind QQmlPropertyToPropertyBinding::kind() const
+{
+ return PropertyToPropertyBinding;
+}
+
+void QQmlPropertyToPropertyBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags)
+{
+ const bool wasEnabled = enabledFlag();
+ setEnabledFlag(e);
+ updateCanUseAccessor();
+ if (e && !wasEnabled)
+ update(flags);
+}
+
+void QQmlPropertyToPropertyBinding::captureProperty(
+ const QMetaObject *sourceMetaObject, int notifyIndex,
+ bool isSourceBindable, bool isTargetBindable)
+{
+ if (isSourceBindable) {
+ // if the property is a QPropery, and we're binding to a QProperty
+ // the automatic capturing process already takes care of everything
+ if (isTargetBindable)
+ return;
+
+ // We have already captured.
+ if (observer)
+ return;
+
+ observer = std::make_unique<Observer>(this);
+ QUntypedBindable bindable;
+ void *argv[] = { &bindable };
+ sourceMetaObject->metacall(
+ m_sourceObject, QMetaObject::BindableProperty, m_sourcePropertyIndex, argv);
+ bindable.observe(observer.get());
+ return;
+ }
+
+ // We cannot capture non-bindable properties without signals
+ if (notifyIndex == -1)
+ return;
+
+ if (isConnected(m_sourceObject, notifyIndex))
+ cancelNotify();
+ else
+ connect(m_sourceObject, notifyIndex, m_engine, true);
+}
+
+void QQmlPropertyToPropertyBinding::update(QQmlPropertyData::WriteFlags flags)
+{
+ if (!enabledFlag())
+ return;
+
+ // Check that the target has not been deleted
+ QObject *target = targetObject();
+ if (QQmlData::wasDeleted(target))
+ return;
+
+ const QQmlPropertyData *d = nullptr;
+ QQmlPropertyData vtd;
+ getPropertyData(&d, &vtd);
+ Q_ASSERT(d);
+
+ // Check for a binding update loop
+ if (Q_UNLIKELY(updatingFlag())) {
+ QQmlAbstractBinding::printBindingLoopError(
+ QQmlPropertyPrivate::restore(target, *d, &vtd, nullptr));
+ return;
+ }
+
+ setUpdatingFlag(true);
+
+ if (canUseAccessor())
+ flags.setFlag(QQmlPropertyData::BypassInterceptor);
+
+ const QMetaObject *sourceMetaObject = m_sourceObject->metaObject();
+ const QMetaProperty property = sourceMetaObject->property(m_sourcePropertyIndex);
+ if (!property.isConstant()) {
+ captureProperty(sourceMetaObject, QMetaObjectPrivate::signalIndex(property.notifySignal()),
+ property.isBindable(), !vtd.isValid() && d->isBindable());
+ }
+
+ QQmlPropertyPrivate::writeValueProperty(
+ target, *d, vtd, property.read(m_sourceObject), {}, flags);
+
+ setUpdatingFlag(false);
+}
+
+void QQmlPropertyGuard_callback(QQmlNotifierEndpoint *e, void **)
+{
+ static_cast<QQmlPropertyToPropertyBinding *>(e)->update();
+}
+
+void QQmlPropertyToPropertyBinding::Observer::trigger(
+ QPropertyObserver *observer, QUntypedPropertyData *)
+{
+ static_cast<Observer *>(observer)->binding->update();
+}
+
+QT_END_NAMESPACE