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// Qt-Security score:significant
4
6
7#include <private/qqmlanybinding_p.h>
8#include <private/qqmlengine_p.h>
9#include <private/qqmlproperty_p.h>
10#include <private/qqmlvmemetaobject_p.h>
11#include <private/qv4alloca_p.h>
12
13#include <QtQml/qqmlinfo.h>
14
16
17/*!
18 * \internal
19 * \class QQmlPropertyToPropertyBinding
20 *
21 * This class can be used to create a direct binding from a source property to
22 * a target property, without going through QQmlJavaScriptExpression and
23 * QV4::Function. In particular you don't need a compilation unit or byte code
24 * to set this up.
25 *
26 * \note The target cannot be a group property, but the source can.
27 */
28
29QQmlAnyBinding QQmlPropertyToPropertyBinding::create(
30 QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target)
31{
32 QQmlAnyBinding result;
33 if (target.isBindable() && !QQmlPropertyPrivate::get(target)->isValueType()) {
34 QPropertyBindingPrivate *binding = nullptr;
35 if (source.isBindable())
36 binding = new QQmlBindableToBindablePropertyBinding(engine, source, target);
37 else
38 binding = new QQmlUnbindableToBindablePropertyBinding(engine, source, target);
39 result = QPropertyBindingPrivate::makeUntyped(binding);
40 return result;
41 }
42
43 if (source.isBindable()) {
44 result = new QQmlBindableToUnbindablePropertyBinding(engine, source, target);
45 return result;
46 }
47
48 result = new QQmlUnbindableToUnbindablePropertyBinding(engine, source, target);
49 return result;
50}
51
52QQmlPropertyToPropertyBinding::QQmlPropertyToPropertyBinding(
53 QQmlEngine *engine, const QQmlProperty &source)
54 : engine(engine)
55 , sourceObject(source.object())
56 , sourcePropertyIndex(QQmlPropertyPrivate::get(source)->encodedIndex())
57{
58}
59
61 QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target)
63 , QQmlPropertyToUnbindablePropertyBinding(engine, source, target)
64{
65}
66
68{
69 return PropertyToPropertyBinding;
70}
71
73 bool e, QQmlPropertyData::WriteFlags flags)
74{
75 const bool wasEnabled = enabledFlag();
76 setEnabledFlag(e);
77 updateCanUseAccessor();
78 if (e && !wasEnabled)
79 update(flags);
80}
81
82void QQmlPropertyToUnbindablePropertyBinding::update(QQmlPropertyData::WriteFlags flags)
83{
84 if (!enabledFlag())
85 return;
86
87 // Check that the target has not been deleted
88 QObject *target = targetObject();
89 if (QQmlData::wasDeleted(target))
90 return;
91
92 const QQmlPropertyData *d = nullptr;
93 QQmlPropertyData vtd;
94 getPropertyData(&d, &vtd);
95 Q_ASSERT(d);
96
97 // Check for a binding update loop
98 if (Q_UNLIKELY(updatingFlag()))
99 return;
100
101 setUpdatingFlag(true);
102
103 if (canUseAccessor())
104 flags.setFlag(QQmlPropertyData::BypassInterceptor);
105
106 QVariant value = m_binding.readSourceValue(
107 [&](const QMetaObject *sourceMetaObject, const QMetaProperty &property) {
108 captureProperty(sourceMetaObject, property);
109 });
110
111 QV4::ExecutionEngine *v4 = m_binding.engine->handle();
112 Q_ASSERT(v4);
113
114 const QMetaType targetMetaType = vtd.isValid() ? vtd.propType() : d->propType();
115 const QMetaType valueMetaType = value.metaType();
116
117 if (valueMetaType != targetMetaType && targetMetaType != QMetaType::fromType<QVariant>()) {
118 // Coerce the value to the target type using JavaScript semantics.
119 // Don't use QQmlProperty's internal coercion. It's not the same.
120 QVariant coerced(targetMetaType);
121 QV4::Scope scope(v4);
122 QV4::ScopedValue jsValue(
123 scope, value.isValid()
124 ? v4->metaTypeToJS(valueMetaType, value.constData())
125 : QV4::Encode::undefined());
126
127 // If the coercion fails, let writeValueProperty() do its thing after all.
128 if (QV4::ExecutionEngine::metaTypeFromJS(jsValue, targetMetaType, coerced.data()))
129 value = std::move(coerced);
130 }
131
132 QQmlPropertyPrivate::writeValueProperty(target, *d, vtd, value, {}, flags);
133 setUpdatingFlag(false);
134}
135
137 QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target)
139{
140 setTarget(target);
141}
142
144 QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target)
146 , QQmlPropertyToUnbindablePropertyBinding(engine, source, target)
147{
148}
149
151 QPropertyObserver *observer, QUntypedPropertyData *)
152{
153 static_cast<QQmlBindableToUnbindablePropertyBinding *>(observer)
154 ->QQmlPropertyToUnbindablePropertyBinding::update();
155}
156
158 const QMetaObject *sourceMetaObject, const QMetaProperty &sourceProperty)
159{
160 Q_UNUSED(sourceMetaObject);
161 m_binding.doConnectNotify(this, sourceProperty);
162}
163
165 const QMetaObject *sourceMetaObject, const QMetaProperty &sourceProperty)
166{
167 Q_UNUSED(sourceProperty);
168
169 // We have already captured.
170 if (m_isObserving)
171 return;
172
173 QUntypedBindable bindable;
174 void *argv[] = { &bindable };
175 sourceMetaObject->metacall(
176 m_binding.sourceObject, QMetaObject::BindableProperty,
177 m_binding.sourcePropertyIndex.coreIndex(), argv);
178 bindable.observe(this);
179 m_isObserving = true;
180}
181
182namespace QtPrivate {
183template<typename Binding>
184inline constexpr BindingFunctionVTable
187 [](void *qpropertyBinding) { delete reinterpret_cast<Binding *>(qpropertyBinding); },
188 [](void *, void *){},
189 0
190 };
191}
192
194 QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target,
195 const QtPrivate::BindingFunctionVTable *vtable)
199{
200 errorCallBack = [](QPropertyBindingPrivate *that) {
201 auto self = static_cast<QQmlPropertyToBindablePropertyBinding *>(that);
202 if (self->m_binding.engine) {
203 const auto error = self->bindingError();
204 if (error.type() == QPropertyBindingError::BindingLoop) {
205 qmlWarning(self->m_binding.sourceObject)
206 << QStringLiteral("Binding loop detected for property bound to ")
207 << self->propertyDataPtr;
208 }
209 }
210 };
211}
212
222
224{
225 PendingBindingObserverList bindingObservers;
226 evaluateRecursive(bindingObservers);
227
228 if (const QPropertyBindingError error = bindingError();
229 Q_UNLIKELY(error.type() == QPropertyBindingError::BindingLoop)) {
230 return;
231 }
232
233 notifyNonRecursive(bindingObservers);
234}
235
244
245void QQmlUnbindableToUnbindableGuard_callback(QQmlNotifierEndpoint *e, void **)
246{
247 static_cast<QQmlUnbindableToUnbindablePropertyBinding *>(e)->update();
248}
249
250void QQmlUnbindableToBindableGuard_callback(QQmlNotifierEndpoint *e, void **)
251{
253}
254
255QT_END_NAMESPACE
QQmlBindableToBindablePropertyBinding(QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target)
void captureProperty(const QMetaObject *sourceMetaObject, const QMetaProperty &sourceProperty) final
QQmlBindableToUnbindablePropertyBinding(QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target)
QQmlPropertyToBindablePropertyBinding(QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target, const QtPrivate::BindingFunctionVTable *vtable)
void setEnabled(bool e, QQmlPropertyData::WriteFlags flags) final
QQmlPropertyToUnbindablePropertyBinding(QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target)
void update(QQmlPropertyData::WriteFlags flags=QQmlPropertyData::DontRemoveBinding)
QQmlUnbindableToBindablePropertyBinding(QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target)
QQmlUnbindableToUnbindablePropertyBinding(QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target)
void captureProperty(const QMetaObject *sourceMetaObject, const QMetaProperty &sourceProperty) final
Combined button and popup list for selecting options.
constexpr BindingFunctionVTable bindingFunctionVTableForQQmlPropertyToBindablePropertyBinding
void QQmlUnbindableToUnbindableGuard_callback(QQmlNotifierEndpoint *, void **)
void QQmlUnbindableToBindableGuard_callback(QQmlNotifierEndpoint *, void **)