Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlpropertytopropertybinding.cpp
Go to the documentation of this file.
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
5
6#include <private/qqmlanybinding_p.h>
7#include <private/qqmlengine_p.h>
8#include <private/qqmlvmemetaobject_p.h>
9#include <private/qv4alloca_p.h>
10#include <private/qv4jscall_p.h>
11
12#include <QtQml/qqmlinfo.h>
13
15
16/*!
17 * \internal
18 * \class QQmlPropertyToPropertyBinding
19 *
20 * This class can be used to create a direct binding from a source property to
21 * a target property, without going through QQmlJavaScriptExpression and
22 * QV4::Function. In particular you don't need a compilation unit or byte code
23 * to set this up.
24 *
25 * \note The target cannot be a group property, but the source can.
26 */
27
28QQmlAnyBinding QQmlPropertyToPropertyBinding::create(
29 QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target)
30{
31 QQmlAnyBinding result;
32 if (target.isBindable()) {
33 if (source.isBindable()) {
34 result = QUntypedPropertyBinding(new QQmlBindableToBindablePropertyBinding(
35 engine, source.object(), QQmlPropertyPrivate::get(source)->encodedIndex(),
36 target.object(), target.index()));
37 return result;
38 }
39
40 result = QUntypedPropertyBinding(new QQmlUnbindableToBindablePropertyBinding(
41 engine, source.object(), QQmlPropertyPrivate::get(source)->encodedIndex(),
42 target.object(), target.index()));
43 return result;
44 }
45
46 if (source.isBindable()) {
47 result = new QQmlBindableToUnbindablePropertyBinding(
48 engine, source.object(), QQmlPropertyPrivate::get(source)->encodedIndex(),
49 target.object(), target.index());
50 return result;
51 }
52
53 result = new QQmlUnbindableToUnbindablePropertyBinding(
54 engine, source.object(), QQmlPropertyPrivate::get(source)->encodedIndex(),
55 target.object(), target.index());
56 return result;
57}
58
59QQmlPropertyToPropertyBinding::QQmlPropertyToPropertyBinding(
60 QQmlEngine *engine, QObject *sourceObject, QQmlPropertyIndex sourcePropertyIndex)
61 : engine(engine)
62 , sourceObject(sourceObject)
63 , sourcePropertyIndex(sourcePropertyIndex)
64{
65}
66
68 QQmlEngine *engine, QObject *sourceObject, QQmlPropertyIndex sourcePropertyIndex,
69 QObject *targetObject, int targetPropertyIndex)
72 engine, sourceObject, sourcePropertyIndex, targetObject, targetPropertyIndex)
73{
74}
75
77{
78 return PropertyToPropertyBinding;
79}
80
82 bool e, QQmlPropertyData::WriteFlags flags)
83{
84 const bool wasEnabled = enabledFlag();
85 setEnabledFlag(e);
86 updateCanUseAccessor();
87 if (e && !wasEnabled)
88 update(flags);
89}
90
91void QQmlPropertyToUnbindablePropertyBinding::update(QQmlPropertyData::WriteFlags flags)
92{
93 if (!enabledFlag())
94 return;
95
96 // Check that the target has not been deleted
97 QObject *target = targetObject();
98 if (QQmlData::wasDeleted(target))
99 return;
100
101 const QQmlPropertyData *d = nullptr;
102 QQmlPropertyData vtd;
103 getPropertyData(&d, &vtd);
104 Q_ASSERT(d);
105
106 // Check for a binding update loop
107 if (Q_UNLIKELY(updatingFlag())) {
108 printBindingLoopError(QQmlPropertyPrivate::restore(target, *d, &vtd, nullptr));
109 return;
110 }
111
112 setUpdatingFlag(true);
113
114 if (canUseAccessor())
115 flags.setFlag(QQmlPropertyData::BypassInterceptor);
116
117 QVariant value = m_binding.readSourceValue(
118 [&](const QMetaObject *sourceMetaObject, const QMetaProperty &property) {
119 captureProperty(sourceMetaObject, property);
120 });
121
122 QQmlPropertyPrivate::writeValueProperty(target, *d, vtd, value, {}, flags);
123 setUpdatingFlag(false);
124}
125
127 QQmlEngine *engine, QObject *sourceObject, QQmlPropertyIndex sourcePropertyIndex,
128 QObject *targetObject, int targetPropertyIndex)
130{
131 setTarget(targetObject, targetPropertyIndex, false, -1);
132}
133
135 QQmlEngine *engine, QObject *sourceObject, QQmlPropertyIndex sourcePropertyIndex,
136 QObject *targetObject, int targetPropertyIndex)
139 engine, sourceObject, sourcePropertyIndex, targetObject, targetPropertyIndex)
140{
141}
142
144 QPropertyObserver *observer, QUntypedPropertyData *)
145{
146 static_cast<QQmlBindableToUnbindablePropertyBinding *>(observer)
147 ->QQmlPropertyToUnbindablePropertyBinding::update();
148}
149
151 const QMetaObject *sourceMetaObject, const QMetaProperty &sourceProperty)
152{
153 Q_UNUSED(sourceMetaObject);
154 m_binding.doConnectNotify(this, sourceProperty);
155}
156
158 const QMetaObject *sourceMetaObject, const QMetaProperty &sourceProperty)
159{
160 Q_UNUSED(sourceProperty);
161
162 // We have already captured.
163 if (m_isObserving)
164 return;
165
166 QUntypedBindable bindable;
167 void *argv[] = { &bindable };
168 sourceMetaObject->metacall(
169 m_binding.sourceObject, QMetaObject::BindableProperty,
170 m_binding.sourcePropertyIndex.coreIndex(), argv);
171 bindable.observe(this);
172 m_isObserving = true;
173}
174
175namespace QtPrivate {
176template<typename Binding>
177inline constexpr BindingFunctionVTable
179 &Binding::update,
180 [](void *qpropertyBinding) { delete reinterpret_cast<Binding *>(qpropertyBinding); },
181 [](void *, void *){},
182 0
183 };
184}
185
200
202 QMetaType metaType, QUntypedPropertyData *dataPtr, void *f)
203{
205 = reinterpret_cast<QQmlUnbindableToBindablePropertyBinding *>(
206 // Address of QPropertyBindingPrivate subobject
207 static_cast<std::byte *>(f) - QPropertyBindingPrivate::getSizeEnsuringAlignment());
208
209 // Unbindable source property needs capturing
210 const QVariant value = self->m_binding.readSourceValue(
211 [self](const QMetaObject *sourceMetaObject, const QMetaProperty &property) {
212 Q_UNUSED(sourceMetaObject);
213 self->m_binding.doConnectNotify(self, property);
214 });
215
216 QV4::coerce(
217 self->m_binding.engine->handle(), value.metaType(), value.constData(),
218 metaType, dataPtr);
219 return true;
220}
221
223{
224 PendingBindingObserverList bindingObservers;
225 evaluateRecursive(bindingObservers);
226
227 if (const QPropertyBindingError error = bindingError();
228 Q_UNLIKELY(error.type() == QPropertyBindingError::BindingLoop)) {
229 qmlWarning(m_targetObject)
230 << "Binding loop detected for property"
231 << m_targetObject->metaObject()->property(m_targetPropertyIndex.coreIndex()).name();
232 return;
233 }
234
235 notifyNonRecursive(bindingObservers);
236}
237
249
251 QMetaType metaType, QUntypedPropertyData *dataPtr, void *f)
252{
254 = reinterpret_cast<QQmlBindableToBindablePropertyBinding *>(
255 // Address of QPropertyBindingPrivate subobject
256 static_cast<std::byte *>(f) - QPropertyBindingPrivate::getSizeEnsuringAlignment());
257
258 // Bindable-to-bindable captures automatically.
259 const QVariant value = self->m_binding.readSourceValue(
260 [](const QMetaObject *, const QMetaProperty &) {});
261
262 QV4::coerce(
263 self->m_binding.engine->handle(), value.metaType(), value.constData(),
264 metaType, dataPtr);
265 return true;
266}
267
268void QQmlUnbindableToUnbindableGuard_callback(QQmlNotifierEndpoint *e, void **)
269{
270 static_cast<QQmlUnbindableToUnbindablePropertyBinding *>(e)->update();
271}
272
273void QQmlUnbindableToBindableGuard_callback(QQmlNotifierEndpoint *e, void **)
274{
276}
277
278QT_END_NAMESPACE
QQmlBindableToBindablePropertyBinding(QQmlEngine *engine, QObject *sourceObject, QQmlPropertyIndex sourcePropertyIndex, QObject *targetObject, int targetPropertyIndex)
void captureProperty(const QMetaObject *sourceMetaObject, const QMetaProperty &sourceProperty) final
QQmlBindableToUnbindablePropertyBinding(QQmlEngine *engine, QObject *sourceObject, QQmlPropertyIndex sourcePropertyIndex, QObject *targetObject, int targetPropertyIndex)
void setEnabled(bool e, QQmlPropertyData::WriteFlags flags) final
QQmlPropertyToUnbindablePropertyBinding(QQmlEngine *engine, QObject *sourceObject, QQmlPropertyIndex sourcePropertyIndex, QObject *targetObject, int targetPropertyIndex)
void update(QQmlPropertyData::WriteFlags flags=QQmlPropertyData::DontRemoveBinding)
QQmlUnbindableToBindablePropertyBinding(QQmlEngine *engine, QObject *sourceObject, QQmlPropertyIndex sourcePropertyIndex, QObject *targetObject, int targetPropertyIndex)
QQmlUnbindableToUnbindablePropertyBinding(QQmlEngine *engine, QObject *sourceObject, QQmlPropertyIndex sourcePropertyIndex, QObject *targetObject, int targetPropertyIndex)
void captureProperty(const QMetaObject *sourceMetaObject, const QMetaProperty &sourceProperty) final
Combined button and popup list for selecting options.
constexpr BindingFunctionVTable bindingFunctionVTableForQQmlPropertyToBindablePropertyBinding
void QQmlUnbindableToBindableGuard_callback(QQmlNotifierEndpoint *e, void **)
void QQmlUnbindableToUnbindableGuard_callback(QQmlNotifierEndpoint *e, void **)