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
qqmlanybinding_p.h
Go to the documentation of this file.
1// Copyright (C) 2021 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#ifndef QQMLANYBINDINGPTR_P_H
5#define QQMLANYBINDINGPTR_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <qqmlproperty.h>
19#include <private/qqmlpropertybinding_p.h>
20#include <private/qqmlbinding_p.h>
21
23
24// Fully inline so that subsequent prop.isBindable check might get ellided.
25
26/*!
27 \internal
28 \brief QQmlAnyBinding is an abstraction over the various bindings in QML
29
30 QQmlAnyBinding can store both classical bindings (derived from QQmlAbstractBinding)
31 as well as new-style bindings (derived from QPropertyBindingPrivate). For both, it keeps
32 a strong reference to them, and knows how to delete them in case the reference count
33 becomes zero. In that sense it can be thought of as a union of QUntypedPropertyBinding
34 and QQmlAbstractBinding::Ptr.
35
36 It also offers methods to create bindings (from QV4::Function, from translation bindings
37 and from code strings). Moreover, it allows the retrieval, the removal and the
38 installation of bindings on a QQmlProperty.
39
40 Note that the class intentionally does not allow construction from QUntypedProperty and
41 QQmlAbstractBinding::Ptr. This is meant to catch code which doesn't handle bindable properties
42 yet when porting existing code.
43 */
45public:
46
47 constexpr QQmlAnyBinding() noexcept = default;
48 QQmlAnyBinding(std::nullptr_t) : d(static_cast<QQmlAbstractBinding *>(nullptr)) {}
49
50 /*!
51 \internal
52 Returns the binding of the property \a prop as a QQmlAnyBinding.
53 The binding continues to be active and set on the property.
54 If there was no binding set, the returned QQmlAnyBinding is null.
55 */
56 static QQmlAnyBinding ofProperty(const QQmlProperty &prop) {
57 QQmlAnyBinding binding;
58 if (prop.isBindable()) {
59 QUntypedBindable bindable = prop.property().bindable(prop.object());
60 binding = bindable.binding();
61 } else {
62 binding = QQmlPropertyPrivate::binding(prop);
63 }
64 return binding;
65 }
66
67 /*!
68 \overload
69
70 \a object must be non-null
71 */
72 static QQmlAnyBinding ofProperty(QObject *object, QQmlPropertyIndex index)
73 {
74 const auto result = [](auto &&result) -> QQmlAnyBinding {
75 QQmlAnyBinding binding;
76 binding = std::forward<decltype(result)>(result);
77 return binding;
78 };
79
80 Q_ASSERT(object);
81 const auto coreIndex = index.coreIndex();
82
83 // we don't support bindable properties on value types so far
84 if (index.hasValueTypeIndex())
85 return result(QQmlPropertyPrivate::binding(object, index));
86
87 if (QQmlPropertyCache::ConstPtr propertyCache = QQmlData::ensurePropertyCache(object)) {
88 const QQmlPropertyData *property = propertyCache->property(coreIndex);
89 return (property->acceptsQBinding())
90 ? result(property->propertyBindable(object).binding())
91 : result(QQmlPropertyPrivate::binding(object, index));
92 }
93
94 const QMetaObject *metaObject = object->metaObject();
95 const QMetaProperty metaProp = metaObject->property(coreIndex);
96 return metaProp.isBindable()
97 ? result(metaProp.bindable(object).binding())
98 : result(QQmlPropertyPrivate::binding(object, index));
99 }
100
101 /*!
102 Removes the binding from the property \a prop, and returns it as a
103 QQmlAnyBinding if there was any. Otherwise returns a null
104 QQmlAnyBinding.
105 */
106 static QQmlAnyBinding takeFrom(const QQmlProperty &prop)
107 {
108 QQmlAnyBinding binding;
109 if (prop.isBindable()) {
110 QUntypedBindable bindable = prop.property().bindable(prop.object());
111 binding = bindable.takeBinding();
112 } else {
113 auto qmlBinding = QQmlPropertyPrivate::binding(prop);
114 if (qmlBinding) {
115 binding = qmlBinding; // this needs to run before removeFromObject, else the refcount might reach zero
116 qmlBinding->setEnabled(false, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
117 qmlBinding->removeFromObject();
118 }
119 }
120 return binding;
121 }
122
123 /*!
124 \internal
125 Creates a binding for property \a prop from \a function.
126 \a obj is the scope object which shall be used for the function and \a scope its QML scope.
127 The binding is not installed on the property (but if a QQmlBinding is created, it has its
128 target set to \a prop).
129 */
130 static QQmlAnyBinding createFromFunction(const QQmlProperty &prop, QV4::Function *function,
131 QObject *obj, const QQmlRefPointer<QQmlContextData> &ctxt,
132 QV4::ExecutionContext *scope)
133 {
134 QQmlAnyBinding binding;
135 auto propPriv = QQmlPropertyPrivate::get(prop);
136 if (prop.isBindable()) {
137 auto index = QQmlPropertyIndex(propPriv->core.coreIndex(), -1);
138 binding = QQmlPropertyBinding::create(&propPriv->core,
139 function, obj, ctxt,
140 scope, prop.object(), index);
141 } else {
142 auto qmlBinding = QQmlBinding::create(&propPriv->core, function, obj, ctxt, scope);
143 qmlBinding->setTarget(prop);
144 binding = qmlBinding;
145 }
146 return binding;
147 }
148
149 /*!
150 \internal
151 Creates a binding for property \a prop from \a script.
152 \a obj is the scope object which shall be used for the function and \a ctxt its QML scope.
153 The binding is not installed on the property (but if a QQmlBinding is created, it has its
154 target set to \a prop).
155 */
156 static QQmlAnyBinding createFromScriptString(const QQmlProperty &prop, const QQmlScriptString &script,
157 QObject *obj, QQmlContext *ctxt)
158 {
159 QQmlAnyBinding binding;
160 auto propPriv = QQmlPropertyPrivate::get(prop);
161 if (prop.isBindable()) {
162 auto index = QQmlPropertyIndex(propPriv->core.coreIndex(), -1);
163 binding = QQmlPropertyBinding::createFromScriptString(&propPriv->core, script, obj, ctxt, prop.object(), index);
164 } else {
165 auto qmlBinding = QQmlBinding::create(&propPriv->core, script, obj, ctxt);
166 qmlBinding->setTarget(prop);
167 binding = qmlBinding;
168 }
169 return binding;
170 }
171
172
173 /*!
174 \internal
175 Removes the binding from \a prop if there is any.
176 */
177 static void removeBindingFrom(QQmlProperty &prop)
178 {
179 if (prop.isBindable())
180 prop.property().bindable(prop.object()).takeBinding();
181 else
182 QQmlPropertyPrivate::removeBinding(prop, QQmlPropertyPrivate::OverrideSticky);
183 }
184
185 /*!
186 \internal
187 Creates a binding for property \a prop from \a function.
188 \a obj is the scope object which shall be used for the function and \a scope its QML scope.
189 The binding is not installed on the property (but if a QQmlBinding is created, it has its
190 target set to \a prop).
191 */
192 static QQmlAnyBinding createFromCodeString(const QQmlProperty &prop, const QString& code, QObject *obj, const QQmlRefPointer<QQmlContextData> &ctxt, const QString &url, quint16 lineNumber) {
193 QQmlAnyBinding binding;
194 auto propPriv = QQmlPropertyPrivate::get(prop);
195 if (prop.isBindable()) {
196 auto index = QQmlPropertyIndex(propPriv->core.coreIndex(), -1);
197 binding = QQmlPropertyBinding::createFromCodeString(&propPriv->core,
198 code, obj, ctxt,
199 url, lineNumber,
200 prop.object(), index);
201 } else {
202 auto qmlBinding = QQmlBinding::create(&propPriv->core, code, obj, ctxt, url, lineNumber);
203 qmlBinding->setTarget(prop);
204 binding = qmlBinding;
205 }
206 return binding;
207 }
208
209 /*!
210 \internal
211 Creates a translattion binding for \a prop from \a compilationUnit and \a transationBinding.
212 \a obj is the context object, \a context the qml context.
213 */
214 static QQmlAnyBinding createTranslationBinding(const QQmlProperty &prop, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *translationBinding, QObject *scopeObject=nullptr, QQmlRefPointer<QQmlContextData> context={})
215 {
216 QQmlAnyBinding binding;
217 auto propPriv = QQmlPropertyPrivate::get(prop);
218 if (prop.isBindable()) {
219 binding = QQmlTranslationPropertyBinding::create(&propPriv->core, compilationUnit, translationBinding);
220 } else {
221 auto qmlBinding = QQmlBinding::createTranslationBinding(compilationUnit, translationBinding, scopeObject, context);
222 binding = qmlBinding;
223 qmlBinding->setTarget(prop);
224 }
225 return binding;
226 }
227
228 /*!
229 \internal
230 Installs the binding referenced by this QQmlAnyBinding on the target.
231 If \a mode is set to RespectInterceptors, interceptors are honored, otherwise
232 writes and binding installation bypass them (the default).
233 Preconditions:
234 - The binding is non-null.
235 - If the binding is QQmlAbstractBinding derived, the target is non-bindable.
236 - If the binding is a QUntypedPropertyBinding, then the target is bindable.
237 */
242
243 void installOn(const QQmlProperty &target, InterceptorMode mode = IgnoreInterceptors)
244 {
245 Q_ASSERT(!d.isNull());
247 auto abstractBinding = asAbstractBinding();
248 Q_ASSERT(abstractBinding->targetObject() == target.object() || QQmlPropertyPrivate::get(target)->core.isAlias());
249 Q_ASSERT(!target.isBindable() || QQmlPropertyPrivate::get(target)->isValueType());
250 if (mode == IgnoreInterceptors)
251 QQmlPropertyPrivate::setBinding(abstractBinding, QQmlPropertyPrivate::None, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
252 else
253 QQmlPropertyPrivate::setBinding(abstractBinding);
254 } else {
255 Q_ASSERT(target.isBindable());
256 QUntypedBindable bindable;
257 void *argv[] = {&bindable};
258 if (mode == IgnoreInterceptors) {
259 target.object()->qt_metacall(QMetaObject::BindableProperty, target.index(), argv);
260 } else {
261 QMetaObject::metacall(target.object(), QMetaObject::BindableProperty, target.index(), argv);
262 }
263 bindable.setBinding(asUntypedPropertyBinding());
264 }
265 }
266
267 /*!
268 \internal
269 Returns true if the binding is in an error state (e.g. binding loop), false otherwise.
270
271 \note For ValueTypeProxyBindings, this methods will always return false
272 */
273 bool hasError() {
275 auto abstractBinding = asAbstractBinding();
276 if (abstractBinding->kind() != QQmlAbstractBinding::QmlBinding)
277 return false;
278 return static_cast<QQmlBinding *>(abstractBinding)->hasError();
279 } else {
280 return asUntypedPropertyBinding().error().hasError();
281 }
282 }
283
284 bool isSticky() const
285 {
286 if (d.isNull())
287 return false;
288 if (d.isT1())
289 return d.asT1()->isSticky();
290 return d.asT2()->isSticky();
291 }
292
293 void setSticky(bool sticky = true)
294 {
295 if (d.isNull())
296 return;
297 if (d.isT1())
298 d.asT1()->setSticky(sticky);
299 else
300 d.asT2()->setSticky(sticky);
301 }
302
303 /*!
304 Stores a null binding. For purpose of classification, the null bindings is
305 treated as a QQmlAbstractPropertyBindings.
306 */
308 {
309 clear();
310 return *this;
311 }
312
313 operator bool() const{
314 return !d.isNull();
315 }
316
317 /*!
318 \internal
319 Returns true if a binding derived from QQmlAbstractPropertyBinding is stored.
320 The binding migh still be null.
321 */
323 { return d.isT1(); }
324
325 /*!
326 \internal
327 Returns true if a binding derived from QPropertyBindingPrivate is stored.
328 The binding might still be null.
329 */
331 { return d.isT2(); }
332
333 /*!
334 \internal
335 Returns the stored QPropertyBindingPrivate as a QUntypedPropertyBinding.
336 If no such binding is currently stored, a null QUntypedPropertyBinding is returned.
337 */
339 {
340 if (d.isT1() || d.isNull())
341 return {};
342 auto priv = d.asT2();
343 return QUntypedPropertyBinding {priv};
344 }
345
346 /*!
347 \internal
348 Returns the stored QQmlAbstractBinding.
349 If no such binding is currently stored, a null pointer is returned.
350 */
352 {
353 if (d.isT2() || d.isNull())
354 return nullptr;
355 return d.asT1();
356 }
357
358 /*!
359 \internal
360 Reevaluates the binding. If the binding was disabled,
361 it gets enabled.
362 */
363 void refresh()
364 {
365 if (d.isNull())
366 return;
367 if (d.isT1()) {
368 auto binding = static_cast<QQmlBinding *>(d.asT1());
369 binding->setEnabledFlag(true);
370 binding->refresh();
371 } else {
372 auto bindingPriv = d.asT2();
373 PendingBindingObserverList bindingObservers;
374 bindingPriv->evaluateRecursive(bindingObservers);
375 bindingPriv->notifyNonRecursive(bindingObservers);
376 }
377
378 }
379
380 /*!
381 \internal
382 Stores \a binding and keeps a reference to it.
383 */
384 QQmlAnyBinding &operator=(QQmlAbstractBinding *binding)
385 {
386 clear();
387 if (binding) {
388 d = binding;
389 binding->ref.ref();
390 }
391 return *this;
392 }
393
394 /*!
395 \internal
396 Stores the binding stored in \a binding and keeps a reference to it.
397 */
398 QQmlAnyBinding &operator=(const QQmlAbstractBinding::Ptr &binding)
399 {
400 clear();
401 if (binding) {
402 d = binding.data();
403 binding->ref.ref();
404 }
405 return *this;
406 }
407
408 /*!
409 \internal
410 Stores \a binding's binding, taking ownership from \a binding.
411 */
412 QQmlAnyBinding &operator=(QQmlAbstractBinding::Ptr &&binding)
413 {
414 clear();
415 if (binding) {
416 d = binding.take();
417 }
418 return *this;
419 }
420
421 /*!
422 \internal
423 Stores the binding stored in \a untypedBinding and keeps a reference to it.
424 */
425 QQmlAnyBinding &operator=(const QUntypedPropertyBinding &untypedBinding)
426 {
427 clear();
428 auto binding = QPropertyBindingPrivate::get(untypedBinding);
429 if (binding) {
430 d = binding;
431 binding->addRef();
432 }
433 return *this;
434 }
435
436 /*!
437 \internal
438 \overload
439 Stores the binding stored in \a untypedBinding, taking ownership from it.
440 */
441 QQmlAnyBinding &operator=(QUntypedPropertyBinding &&untypedBinding)
442 {
443 clear();
444 auto binding = QPropertyBindingPrivate::get(untypedBinding);
445 QPropertyBindingPrivatePtr ptr(binding);
446 if (binding) {
447 d = static_cast<QPropertyBindingPrivate *>(ptr.take());
448 }
449 return *this;
450 }
451
455
456 QQmlAnyBinding(const QQmlAnyBinding &other) noexcept { *this = other; }
458
459 void swap(QQmlAnyBinding &other) noexcept { d.swap(other.d); }
460 friend void swap(QQmlAnyBinding &lhs, QQmlAnyBinding &rhs) noexcept { lhs.swap(rhs); }
461
463 {
464 clear();
466 *this = abstractBinding;
468 *this = untypedBinding;
469 return *this;
470 }
471
472 friend inline bool operator==(const QQmlAnyBinding &p1, const QQmlAnyBinding &p2)
473 {
474 return p1.d == p2.d;
475 }
476
477 friend inline bool operator!=(const QQmlAnyBinding &p1, const QQmlAnyBinding &p2)
478 {
479 return p1.d != p2.d;
480 }
481
482 ~QQmlAnyBinding() noexcept { clear(); }
483private:
484 void clear() noexcept {
485 if (d.isNull())
486 return;
487 if (d.isT1()) {
489 if (!qqmlptr->ref.deref())
490 delete qqmlptr;
491 } else if (d.isT2()) {
493 if (!priv->deref())
495 }
496 d = static_cast<QQmlAbstractBinding *>(nullptr);
497 }
499};
500
501QT_END_NAMESPACE
502
503
504#endif // QQMLANYBINDINGPTR_P_H
QQmlAnyBinding is an abstraction over the various bindings in QML.
QUntypedPropertyBinding asUntypedPropertyBinding() const
static QQmlAnyBinding createFromCodeString(const QQmlProperty &prop, const QString &code, QObject *obj, const QQmlRefPointer< QQmlContextData > &ctxt, const QString &url, quint16 lineNumber)
static void removeBindingFrom(QQmlProperty &prop)
static QQmlAnyBinding createFromScriptString(const QQmlProperty &prop, const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt)
void setSticky(bool sticky=true)
bool isUntypedPropertyBinding() const
static QQmlAnyBinding createTranslationBinding(const QQmlProperty &prop, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &compilationUnit, const QV4::CompiledData::Binding *translationBinding, QObject *scopeObject=nullptr, QQmlRefPointer< QQmlContextData > context={})
QQmlAnyBinding & operator=(std::nullptr_t)
Stores a null binding.
static QQmlAnyBinding takeFrom(const QQmlProperty &prop)
Removes the binding from the property prop, and returns it as a QQmlAnyBinding if there was any.
QQmlAbstractBinding * asAbstractBinding() const
static QQmlAnyBinding ofProperty(QObject *object, QQmlPropertyIndex index)
This is an overloaded member function, provided for convenience. It differs from the above function o...
void installOn(const QQmlProperty &target, InterceptorMode mode=IgnoreInterceptors)
QQmlAnyBinding & operator=(QQmlAbstractBinding *binding)
bool isAbstractPropertyBinding() const
QQmlAnyBinding(QQmlAnyBinding &&other) noexcept
static QQmlAnyBinding ofProperty(const QQmlProperty &prop)
QQmlAnyBinding(const QQmlAnyBinding &other) noexcept
QQmlAnyBinding & operator=(QQmlAbstractBinding::Ptr &&binding)
static QQmlAnyBinding createFromFunction(const QQmlProperty &prop, QV4::Function *function, QObject *obj, const QQmlRefPointer< QQmlContextData > &ctxt, QV4::ExecutionContext *scope)
constexpr QQmlAnyBinding() noexcept=default
QQmlAnyBinding(std::nullptr_t)
bool isSticky() const
QQmlAnyBinding & operator=(const QQmlAbstractBinding::Ptr &binding)