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 if (source.isBindable()) {
35 result = QUntypedPropertyBinding(new QQmlBindableToBindablePropertyBinding(
36 engine, source, target));
37 return result;
38 }
39
40 result = QUntypedPropertyBinding(new QQmlUnbindableToBindablePropertyBinding(
41 engine, source, target));
42 return result;
43 }
44
45 if (source.isBindable()) {
46 result = new QQmlBindableToUnbindablePropertyBinding(engine, source, target);
47 return result;
48 }
49
50 result = new QQmlUnbindableToUnbindablePropertyBinding(engine, source, target);
51 return result;
52}
53
54QQmlPropertyToPropertyBinding::QQmlPropertyToPropertyBinding(
55 QQmlEngine *engine, const QQmlProperty &source)
56 : engine(engine)
57 , sourceObject(source.object())
58 , sourcePropertyIndex(QQmlPropertyPrivate::get(source)->encodedIndex())
59{
60}
61
63 QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target)
65 , QQmlPropertyToUnbindablePropertyBinding(engine, source, target)
66{
67}
68
70{
71 return PropertyToPropertyBinding;
72}
73
75 bool e, QQmlPropertyData::WriteFlags flags)
76{
77 const bool wasEnabled = enabledFlag();
78 setEnabledFlag(e);
79 updateCanUseAccessor();
80 if (e && !wasEnabled)
81 update(flags);
82}
83
84void QQmlPropertyToUnbindablePropertyBinding::update(QQmlPropertyData::WriteFlags flags)
85{
86 if (!enabledFlag())
87 return;
88
89 // Check that the target has not been deleted
90 QObject *target = targetObject();
91 if (QQmlData::wasDeleted(target))
92 return;
93
94 const QQmlPropertyData *d = nullptr;
95 QQmlPropertyData vtd;
96 getPropertyData(&d, &vtd);
97 Q_ASSERT(d);
98
99 // Check for a binding update loop
100 if (Q_UNLIKELY(updatingFlag()))
101 return;
102
103 setUpdatingFlag(true);
104
105 if (canUseAccessor())
106 flags.setFlag(QQmlPropertyData::BypassInterceptor);
107
108 QVariant value = m_binding.readSourceValue(
109 [&](const QMetaObject *sourceMetaObject, const QMetaProperty &property) {
110 captureProperty(sourceMetaObject, property);
111 });
112
113 QV4::ExecutionEngine *v4 = m_binding.engine->handle();
114 Q_ASSERT(v4);
115
116 const QMetaType targetMetaType = vtd.isValid() ? vtd.propType() : d->propType();
117 const QMetaType valueMetaType = value.metaType();
118
119 if (valueMetaType != targetMetaType && targetMetaType != QMetaType::fromType<QVariant>()) {
120 // Coerce the value to the target type using JavaScript semantics.
121 // Don't use QQmlProperty's internal coercion. It's not the same.
122 QVariant coerced(targetMetaType);
123 QV4::Scope scope(v4);
124 QV4::ScopedValue jsValue(
125 scope, value.isValid()
126 ? v4->metaTypeToJS(valueMetaType, value.constData())
127 : QV4::Encode::undefined());
128
129 // If the coercion fails, let writeValueProperty() do its thing after all.
130 if (QV4::ExecutionEngine::metaTypeFromJS(jsValue, targetMetaType, coerced.data()))
131 value = std::move(coerced);
132 }
133
134 QQmlPropertyPrivate::writeValueProperty(target, *d, vtd, value, {}, flags);
135 setUpdatingFlag(false);
136}
137
139 QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target)
141{
142 setTarget(target);
143}
144
146 QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target)
148 , QQmlPropertyToUnbindablePropertyBinding(engine, source, target)
149{
150}
151
153 QPropertyObserver *observer, QUntypedPropertyData *)
154{
155 static_cast<QQmlBindableToUnbindablePropertyBinding *>(observer)
156 ->QQmlPropertyToUnbindablePropertyBinding::update();
157}
158
160 const QMetaObject *sourceMetaObject, const QMetaProperty &sourceProperty)
161{
162 Q_UNUSED(sourceMetaObject);
163 m_binding.doConnectNotify(this, sourceProperty);
164}
165
167 const QMetaObject *sourceMetaObject, const QMetaProperty &sourceProperty)
168{
169 Q_UNUSED(sourceProperty);
170
171 // We have already captured.
172 if (m_isObserving)
173 return;
174
175 QUntypedBindable bindable;
176 void *argv[] = { &bindable };
177 sourceMetaObject->metacall(
178 m_binding.sourceObject, QMetaObject::BindableProperty,
179 m_binding.sourcePropertyIndex.coreIndex(), argv);
180 bindable.observe(this);
181 m_isObserving = true;
182}
183
184namespace QtPrivate {
185template<typename Binding>
186inline constexpr BindingFunctionVTable
189 [](void *qpropertyBinding) { delete reinterpret_cast<Binding *>(qpropertyBinding); },
190 [](void *, void *){},
191 0
192 };
193}
194
196 QQmlEngine *engine, const QQmlProperty &source, const QQmlProperty &target,
197 const QtPrivate::BindingFunctionVTable *vtable)
201{
202}
203
213
215{
216 PendingBindingObserverList bindingObservers;
217 evaluateRecursive(bindingObservers);
218
219 if (const QPropertyBindingError error = bindingError();
220 Q_UNLIKELY(error.type() == QPropertyBindingError::BindingLoop)) {
221 return;
222 }
223
224 notifyNonRecursive(bindingObservers);
225}
226
235
236void QQmlUnbindableToUnbindableGuard_callback(QQmlNotifierEndpoint *e, void **)
237{
238 static_cast<QQmlUnbindableToUnbindablePropertyBinding *>(e)->update();
239}
240
241void QQmlUnbindableToBindableGuard_callback(QQmlNotifierEndpoint *e, void **)
242{
244}
245
246QT_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 QQmlUnbindableToBindableGuard_callback(QQmlNotifierEndpoint *e, void **)
void QQmlUnbindableToUnbindableGuard_callback(QQmlNotifierEndpoint *e, void **)