1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qqmlpropertytopropertybinding_p.h"
5#include <private/qqmlvmemetaobject_p.h>
6
7QT_BEGIN_NAMESPACE
8
9/*!
10 * \internal
11 * \class QQmlPropertyToPropertyBinding
12 *
13 * This class can be used to create a direct binding from a source property to
14 * a target property, without going through QQmlJavaScriptExpression and
15 * QV4::Function. In particular you don't need a compilation unit or byte code
16 * to set this up.
17 */
18
19QQmlPropertyToPropertyBinding::QQmlPropertyToPropertyBinding(
20 QQmlEngine *engine, QObject *sourceObject, int sourcePropertyIndex,
21 QObject *targetObject, int targetPropertyIndex)
22 : QQmlNotifierEndpoint(QQmlPropertyGuard)
23 , m_engine(engine)
24 , m_sourceObject(sourceObject)
25 , m_sourcePropertyIndex(sourcePropertyIndex)
26{
27 setTarget(targetObject, coreIndex: targetPropertyIndex, coreIsAlias: false, valueTypeIndex: -1);
28}
29
30QQmlAbstractBinding::Kind QQmlPropertyToPropertyBinding::kind() const
31{
32 return PropertyToPropertyBinding;
33}
34
35void QQmlPropertyToPropertyBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags)
36{
37 const bool wasEnabled = enabledFlag();
38 setEnabledFlag(e);
39 updateCanUseAccessor();
40 if (e && !wasEnabled)
41 update(flags);
42}
43
44void QQmlPropertyToPropertyBinding::captureProperty(
45 const QMetaObject *sourceMetaObject, int notifyIndex,
46 bool isSourceBindable, bool isTargetBindable)
47{
48 if (isSourceBindable) {
49 // if the property is a QPropery, and we're binding to a QProperty
50 // the automatic capturing process already takes care of everything
51 if (isTargetBindable)
52 return;
53
54 // We have already captured.
55 if (observer)
56 return;
57
58 observer = std::make_unique<Observer>(args: this);
59 QUntypedBindable bindable;
60 void *argv[] = { &bindable };
61 sourceMetaObject->metacall(
62 m_sourceObject, QMetaObject::BindableProperty, m_sourcePropertyIndex, argv);
63 bindable.observe(observer: observer.get());
64 return;
65 }
66
67 // We cannot capture non-bindable properties without signals
68 if (notifyIndex == -1)
69 return;
70
71 if (isConnected(source: m_sourceObject, sourceSignal: notifyIndex))
72 cancelNotify();
73 else
74 connect(source: m_sourceObject, sourceSignal: notifyIndex, engine: m_engine, doNotify: true);
75}
76
77void QQmlPropertyToPropertyBinding::update(QQmlPropertyData::WriteFlags flags)
78{
79 if (!enabledFlag())
80 return;
81
82 // Check that the target has not been deleted
83 QObject *target = targetObject();
84 if (QQmlData::wasDeleted(object: target))
85 return;
86
87 const QQmlPropertyData *d = nullptr;
88 QQmlPropertyData vtd;
89 getPropertyData(propertyData: &d, valueTypeData: &vtd);
90 Q_ASSERT(d);
91
92 // Check for a binding update loop
93 if (Q_UNLIKELY(updatingFlag())) {
94 QQmlAbstractBinding::printBindingLoopError(
95 prop: QQmlPropertyPrivate::restore(target, *d, &vtd, nullptr));
96 return;
97 }
98
99 setUpdatingFlag(true);
100
101 if (canUseAccessor())
102 flags.setFlag(flag: QQmlPropertyData::BypassInterceptor);
103
104 const QMetaObject *sourceMetaObject = m_sourceObject->metaObject();
105 const QMetaProperty property = sourceMetaObject->property(index: m_sourcePropertyIndex);
106 if (!property.isConstant()) {
107 captureProperty(sourceMetaObject, notifyIndex: QMetaObjectPrivate::signalIndex(m: property.notifySignal()),
108 isSourceBindable: property.isBindable(), isTargetBindable: !vtd.isValid() && d->isBindable());
109 }
110
111 QQmlPropertyPrivate::writeValueProperty(
112 target, *d, valueTypeData: vtd, property.read(obj: m_sourceObject), {}, flags);
113
114 setUpdatingFlag(false);
115}
116
117void QQmlPropertyGuard_callback(QQmlNotifierEndpoint *e, void **)
118{
119 static_cast<QQmlPropertyToPropertyBinding *>(e)->update();
120}
121
122void QQmlPropertyToPropertyBinding::Observer::trigger(
123 QPropertyObserver *observer, QUntypedPropertyData *)
124{
125 static_cast<Observer *>(observer)->binding->update();
126}
127
128QT_END_NAMESPACE
129

source code of qtdeclarative/src/qml/qml/qqmlpropertytopropertybinding.cpp