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