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 | |
7 | QT_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 | |
19 | QQmlPropertyToPropertyBinding::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 | |
30 | QQmlAbstractBinding::Kind QQmlPropertyToPropertyBinding::kind() const |
31 | { |
32 | return PropertyToPropertyBinding; |
33 | } |
34 | |
35 | void 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 | |
44 | void 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 | |
77 | void 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 | |
117 | void QQmlPropertyGuard_callback(QQmlNotifierEndpoint *e, void **) |
118 | { |
119 | static_cast<QQmlPropertyToPropertyBinding *>(e)->update(); |
120 | } |
121 | |
122 | void QQmlPropertyToPropertyBinding::Observer::trigger( |
123 | QPropertyObserver *observer, QUntypedPropertyData *) |
124 | { |
125 | static_cast<Observer *>(observer)->binding->update(); |
126 | } |
127 | |
128 | QT_END_NAMESPACE |
129 | |