1// Copyright (C) 2024 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#include "qqmlbind_p.h"
5
6#include <private/qqmlanybinding_p.h>
7#include <private/qqmlbinding_p.h>
8#include <private/qqmlcomponent_p.h>
9#include <private/qqmlmetatype_p.h>
10#include <private/qqmlnullablevalue_p.h>
11#include <private/qqmlproperty_p.h>
12#include <private/qqmlvmemetaobject_p.h>
13#include <private/qv4persistent_p.h>
14#include <private/qv4qmlcontext_p.h>
15#include <private/qv4resolvedtypereference_p.h>
16#include <private/qv4runtime_p.h>
17
18#include <QtQml/qqmlcontext.h>
19#include <QtQml/qqmlengine.h>
20#include <QtQml/qqmlinfo.h>
21#include <QtQml/qqmlproperty.h>
22#include <QtQml/qqmlpropertymap.h>
23
24#include <QtCore/private/qobject_p.h>
25
26#include <QtCore/qdebug.h>
27#include <QtCore/qfile.h>
28#include <QtCore/qloggingcategory.h>
29#include <QtCore/qpointer.h>
30#include <QtCore/qtimer.h>
31
32QT_BEGIN_NAMESPACE
33
34Q_STATIC_LOGGING_CATEGORY(lcQtQmlBindingRemoval, "qt.qml.binding.removal", QtWarningMsg)
35
36enum class QQmlBindEntryKind: quint8 {
37 V4Value,
38 Variant,
39 Binding,
40 None
41};
42
43/*!
44 * \internal
45 * QQmlBindEntryContent can store one of QV4::Value, QVariant, QQmlAnyBinding, or nothing,
46 * as denoted by QQmlBindEntryKind. It expects the calling code to know what is stored at
47 * any time. On each method invocation, the current kind has to be passed as last parameter
48 * and the new kind is returned.
49 */
50union QQmlBindEntryContent {
51 Q_DISABLE_COPY_MOVE(QQmlBindEntryContent)
52public:
53 QQmlBindEntryContent() {}
54 ~QQmlBindEntryContent() {}
55
56 [[nodiscard]] QQmlBindEntryKind set(
57 QQmlBindEntryContent &&other, QQmlBindEntryKind newKind, QQmlBindEntryKind oldKind)
58 {
59 silentDestroy(oldKind);
60 switch (newKind) {
61 case QQmlBindEntryKind::V4Value:
62 case QQmlBindEntryKind::Variant:
63 new (&v4Value) QV4::PersistentValue(std::move(other.v4Value));
64 break;
65 case QQmlBindEntryKind::Binding:
66 new (&binding) QQmlAnyBinding(std::move(other.binding));
67 break;
68 case QQmlBindEntryKind::None:
69 break;
70 }
71 return newKind;
72 }
73
74 [[nodiscard]] QQmlBindEntryKind set(
75 const QQmlBindEntryContent &other, QQmlBindEntryKind newKind, QQmlBindEntryKind oldKind)
76 {
77 silentDestroy(oldKind);
78 switch (newKind) {
79 case QQmlBindEntryKind::V4Value:
80 case QQmlBindEntryKind::Variant:
81 new (&v4Value) QV4::PersistentValue(other.v4Value);
82 break;
83 case QQmlBindEntryKind::Binding:
84 new (&binding) QQmlAnyBinding(other.binding);
85 break;
86 case QQmlBindEntryKind::None:
87 break;
88 }
89 return newKind;
90 }
91
92 [[nodiscard]] QQmlBindEntryKind destroy(QQmlBindEntryKind kind)
93 {
94 switch (kind) {
95 case QQmlBindEntryKind::V4Value:
96 case QQmlBindEntryKind::Variant:
97 v4Value.~PersistentValue();
98 break;
99 case QQmlBindEntryKind::Binding:
100 binding.~QQmlAnyBinding();
101 break;
102 case QQmlBindEntryKind::None:
103 break;
104 }
105 return QQmlBindEntryKind::None;
106 }
107
108 [[nodiscard]] QQmlBindEntryKind set(
109 QV4::ExecutionEngine *engine, const QVariant &v, QQmlBindEntryKind oldKind)
110 {
111 silentDestroy(oldKind);
112 new (&v4Value) QV4::PersistentValue(engine, engine->fromVariant(v));
113 return QQmlBindEntryKind::Variant;
114 }
115
116 [[nodiscard]] QQmlBindEntryKind set(QV4::PersistentValue v, QQmlBindEntryKind oldKind)
117 {
118 silentDestroy(oldKind);
119 new (&v4Value) QV4::PersistentValue(std::move(v));
120 return QQmlBindEntryKind::V4Value;
121 }
122
123 [[nodiscard]] QQmlBindEntryKind setVariant(QV4::PersistentValue v, QQmlBindEntryKind oldKind)
124 {
125 silentDestroy(oldKind);
126 new (&v4Value) QV4::PersistentValue(std::move(v));
127 return QQmlBindEntryKind::Variant;
128 }
129
130 [[nodiscard]] QQmlBindEntryKind set(QQmlAnyBinding v, QQmlBindEntryKind oldKind)
131 {
132 silentDestroy(oldKind);
133 new (&binding) QQmlAnyBinding(std::move(v));
134 return QQmlBindEntryKind::Binding;
135 }
136
137 QV4::PersistentValue v4Value;
138 QQmlAnyBinding binding;
139
140private:
141 void silentDestroy(QQmlBindEntryKind oldKind)
142 {
143 const QQmlBindEntryKind dead = destroy(kind: oldKind);
144 Q_ASSERT(dead == QQmlBindEntryKind::None);
145 Q_UNUSED(dead);
146 }
147};
148
149/*!
150 * \internal
151 * QQmlBindEntry holds two QQmlBindEntryContent members, along with their kinds.
152 * The \l current content is the value or binding the Binding element installs on
153 * the target if enabled (that is, if \l{when}). The \l previous content is what
154 * the target holds before the Binding element installs its binding or value. It
155 * is restored if !\l{when}. The \l prop member holds the target property.
156 */
157struct QQmlBindEntry
158{
159 QQmlBindEntry() = default;
160 QQmlBindEntry(QQmlBindEntry &&other) noexcept : prop(std::move(other.prop))
161 {
162 currentKind = current.set(other: std::move(other.current), newKind: other.currentKind, oldKind: currentKind);
163 previousKind = previous.set(other: std::move(other.previous), newKind: other.previousKind, oldKind: previousKind);
164 }
165
166 QQmlBindEntry(const QQmlBindEntry &other)
167 : prop(other.prop)
168 {
169 currentKind = current.set(other: other.current, newKind: other.currentKind, oldKind: currentKind);
170 previousKind = previous.set(other: other.previous, newKind: other.previousKind, oldKind: previousKind);
171 }
172
173 ~QQmlBindEntry()
174 {
175 currentKind = current.destroy(kind: currentKind);
176 previousKind = previous.destroy(kind: previousKind);
177 }
178
179 QQmlBindEntry &operator=(QQmlBindEntry &&other) noexcept
180 {
181 if (this == &other)
182 return *this;
183 prop = std::move(other.prop);
184 currentKind = current.set(other: std::move(other.current), newKind: other.currentKind, oldKind: currentKind);
185 previousKind = previous.set(other: std::move(other.previous), newKind: other.previousKind, oldKind: previousKind);
186 return *this;
187 }
188
189 QQmlBindEntry &operator=(const QQmlBindEntry &other)
190 {
191 if (this == &other)
192 return *this;
193 prop = other.prop;
194 currentKind = current.set(other: other.current, newKind: other.currentKind, oldKind: currentKind);
195 previousKind = previous.set(other: other.previous, newKind: other.previousKind, oldKind: previousKind);
196 return *this;
197 }
198
199
200 QQmlBindEntryContent current;
201 QQmlBindEntryContent previous;
202 QQmlProperty prop;
203 QQmlBindEntryKind currentKind = QQmlBindEntryKind::None;
204 QQmlBindEntryKind previousKind = QQmlBindEntryKind::None;
205
206 void validate(QQmlBind *q) const;
207 void clearPrev();
208 void setTarget(QQmlBind *q, const QQmlProperty &p);
209};
210
211struct ObjectPropertyValueData
212{
213 // Only one entry is used for target/property/value
214 QQmlBindEntry entry;
215
216 // The \l target object
217 QPointer<QObject> obj;
218
219 // The \l property name
220 QString propName;
221};
222
223struct GeneralizedGroupData
224{
225 // There can be multiple entries when using the generalized grouped
226 // property mode.
227 QVarLengthArray<QQmlBindEntry, 1> entries;
228
229 // Any values we need to create a proxy for. This is necessary when
230 // using the \l delayed member on generalized grouped properties. See
231 // the note on \l delayed.
232 std::unique_ptr<QQmlPropertyMap> delayedValues;
233};
234
235class QQmlBindPrivate : public QObjectPrivate
236{
237public:
238 enum Mode : quint8 {
239 GeneralizedGroup,
240 ObjectPropertyValue,
241 Unknown,
242 };
243
244 QQmlBindPrivate()
245 : when(true)
246 , componentComplete(true)
247 , delayed(false)
248 , pendingEval(false)
249 , restoreBinding(true)
250 , restoreValue(true)
251 , writingProperty(false)
252 {
253 }
254
255 ~QQmlBindPrivate()
256 {
257 switch (mode) {
258 case GeneralizedGroup:
259 generalizedGroupData.~GeneralizedGroupData();
260 return;
261 case ObjectPropertyValue:
262 objectPropertyValueData.~ObjectPropertyValueData();
263 return;
264 case Unknown:
265 return;
266 }
267 }
268
269 union {
270 int noData = 0;
271 GeneralizedGroupData generalizedGroupData;
272 ObjectPropertyValueData objectPropertyValueData;
273 };
274
275 Mode mode = Unknown;
276
277 // Whether the binding is enabled.
278 bool when: 1;
279
280 // Whether we have already parsed any generalized grouped properties
281 // we might need.
282 bool componentComplete:1;
283
284 // Whether we should run in "delayed" mode and proxy all values before
285 // applying them to the target.
286 bool delayed:1;
287
288 // In delayed mode, when using the target/property mode, the \l value
289 // is the proxy. Then pendingEval denotes that a timer is active to
290 // apply the value. We should not start another timer then.
291 bool pendingEval:1;
292
293 // Whether we should restore bindings on !when.
294 // TODO: Deprecate this and always do.
295 bool restoreBinding:1;
296
297 // Whether we should restore values on !when.
298 // TODO: Deprecate this and always do.
299 bool restoreValue:1;
300
301 // writingProperty tracks whether we are updating the target property
302 // when using target/property/value. We use this information to warn about
303 // binding removal if we detect the target property to be updated while we
304 // are not writing it. This doesn't remove the Binding after all.
305 // For generalized grouped properties, we don't have to do this as writing
306 // the target property does remove the binding, just like it removes any
307 // other binding.
308 bool writingProperty:1;
309
310 QQmlBindEntry *targetEntry();
311 void validate(QQmlBind *binding) const;
312 void decodeBinding(
313 QQmlBind *q, const QString &propertyPrefix, QQmlData::DeferredData *deferredData,
314 const QV4::CompiledData::Binding *binding,
315 QQmlComponentPrivate::ConstructionState *immediateState);
316 void createDelayedValues();
317 void onDelayedValueChanged(QString delayedName);
318 void evalDelayed();
319 void buildBindEntries(QQmlBind *q, QQmlComponentPrivate::DeferredState *deferredState);
320 void preEvalEntry(QQmlBindEntry *entry);
321 void postEvalEntry(QQmlBindEntry *entry);
322 bool isCurrent(QQmlBindEntry *entry) const;
323};
324
325static void warnIgnoredProperties(QQmlBind *q)
326{
327 qmlWarning(me: q)
328 << "You should not set the 'object', 'property', or 'value' properties when using "
329 "generalized group properties. They are ignored.";
330}
331
332void QQmlBindEntry::validate(QQmlBind *q) const
333{
334 if (!prop.isWritable()) {
335 qmlWarning(me: q) << "Property '" << prop.name() << "' on "
336 << QQmlMetaType::prettyTypeName(object: prop.object()) << " is read-only.";
337 }
338}
339
340QQmlBindEntry *QQmlBindPrivate::targetEntry()
341{
342 switch (mode) {
343 case GeneralizedGroup:
344 Q_UNREACHABLE_RETURN(nullptr);
345 case Unknown:
346 new (&objectPropertyValueData) ObjectPropertyValueData;
347 mode = ObjectPropertyValue;
348 Q_FALLTHROUGH();
349 case ObjectPropertyValue:
350 return &objectPropertyValueData.entry;
351 }
352
353 return nullptr;
354}
355
356void QQmlBindPrivate::validate(QQmlBind *q) const
357{
358 if (!when)
359 return;
360
361 switch (mode) {
362 case ObjectPropertyValue:
363 if (!objectPropertyValueData.obj)
364 break;
365 if (objectPropertyValueData.entry.prop.isValid()) {
366 objectPropertyValueData.entry.validate(q);
367 } else {
368 qmlWarning(me: q) << "Property '" << objectPropertyValueData.propName
369 << "' does not exist on "
370 << QQmlMetaType::prettyTypeName(object: objectPropertyValueData.obj) << ".";
371 }
372 break;
373 case GeneralizedGroup:
374 for (const QQmlBindEntry &entry : generalizedGroupData.entries)
375 entry.validate(q);
376 break;
377 case Unknown:
378 break;
379 }
380}
381
382/*!
383 \qmltype Binding
384 \inqmlmodule QtQml
385 \ingroup qtquick-interceptors
386 \brief Enables the arbitrary creation of property bindings.
387
388 In QML, property bindings result in a dependency between the properties of
389 different objects.
390
391 \section1 Binding to an Inaccessible Property
392
393 Sometimes it is necessary to bind an object's property to
394 that of another object that isn't directly instantiated by QML, such as a
395 property of a class exported to QML by C++. You can use the Binding type
396 to establish this dependency; binding any value to any object's property.
397
398 For example, in a C++ application that maps an "app.enteredText" property
399 into QML, you can use Binding to update the enteredText property.
400
401 \qml
402 TextEdit { id: myTextField; text: "Please type here..." }
403 Binding { app.enteredText: myTextField.text }
404 \endqml
405
406 When \c{text} changes, the C++ property \c{enteredText} will update
407 automatically.
408
409 \section1 Conditional Bindings
410
411 In some cases you may want to modify the value of a property when a certain
412 condition is met but leave it unmodified otherwise. Often, it's not possible
413 to do this with direct bindings, as you have to supply values for all
414 possible branches.
415
416 For example, the code snippet below results in a warning whenever you
417 release the mouse. This is because the value of the binding is undefined
418 when the mouse isn't pressed.
419
420 \qml
421 // produces warning: "Unable to assign [undefined] to double value"
422 value: if (mouse.pressed) mouse.mouseX
423 \endqml
424
425 The Binding type can prevent this warning.
426
427 \qml
428 Binding on value {
429 when: mouse.pressed
430 value: mouse.mouseX
431 }
432 \endqml
433
434 The Binding type restores any previously set direct bindings on the
435 property.
436
437 \section1 Multiple targets in one Binding
438
439 You can specify multiple bindings to the same object in one Binding element:
440
441 \qml
442 Text {
443 id: t1
444 }
445
446 Binding {
447 t1 {
448 color: "#00FF00"
449 text: "green text"
450 }
451 }
452 \endqml
453
454 You can also specify several bindings with different target objects in a
455 single Binding element:
456
457 \qml
458 Text {
459 id: t1
460 }
461
462 Text {
463 id: t2
464 }
465
466 Binding {
467 t1.text: "Foo"
468 t2.text: "Bar"
469 }
470 \endqml
471
472 \sa {Qt Qml}
473*/
474QQmlBind::QQmlBind(QObject *parent)
475 : QObject(*(new QQmlBindPrivate), parent)
476{
477}
478
479QQmlBind::~QQmlBind()
480{
481 Q_D(QQmlBind);
482 // restore state when dynamic Binding is destroyed
483 if (!(d->when && d->componentComplete && restoreMode() != RestoreNone))
484 return;
485 // isDeletingChildren is supposed to happen later; we couldn't use declarativeData
486 // if isDeletingChildren were set
487 Q_ASSERT(!d->isDeletingChildren);
488 // We can't use qmlEngine (or QQmlData::get), as that checks for scheduled deletion
489 if (auto ddata = static_cast<QQmlData *>(d->declarativeData);
490 ddata && ddata->context && QQmlData::wasDeleted(object: ddata->context->engine()))
491 return; // whole engine is going away; don't bother resetting
492 d->when = false; // internal only change, no signal emission
493 eval();
494}
495
496/*!
497 \qmlproperty bool QtQml::Binding::when
498
499 This property holds when the binding is active.
500 This should be set to an expression that evaluates to true when you want the binding to be active.
501
502 \qml
503 Binding {
504 contactName.text: name
505 when: list.ListView.isCurrentItem
506 }
507 \endqml
508
509 By default, any binding or value that was set perviously is restored when the binding becomes
510 inactive. You can customize the restoration behavior using the \l restoreMode property.
511
512 \sa restoreMode
513*/
514bool QQmlBind::when() const
515{
516 Q_D(const QQmlBind);
517 return d->when;
518}
519
520void QQmlBind::setWhen(bool v)
521{
522 Q_D(QQmlBind);
523 if (d->when == v)
524 return;
525
526 d->when = v;
527 if (v && d->componentComplete)
528 d->validate(q: this);
529 eval();
530 emit whenChanged();
531}
532
533/*!
534 \qmlproperty QtObject QtQml::Binding::target
535
536 The object to be updated. You need to use this property if the binding target
537 does not have an \c id attribute (for example, when the target is a singleton).
538 Otherwise, the following two pieces of code are equivalent:
539
540 \qml
541 Binding { contactName.text: name }
542 \endqml
543
544 \qml
545 Binding {
546 target: contactName
547 property: "text"
548 value: name
549 }
550 \endqml
551
552 The former one is much more compact, but you cannot replace the target
553 object or property at run time. With the latter one you can.
554*/
555QObject *QQmlBind::object() const
556{
557 Q_D(const QQmlBind);
558 switch (d->mode) {
559 case QQmlBindPrivate::GeneralizedGroup:
560 case QQmlBindPrivate::Unknown:
561 return nullptr;
562 case QQmlBindPrivate::ObjectPropertyValue:
563 return d->objectPropertyValueData.obj;
564 }
565
566 Q_UNREACHABLE_RETURN(nullptr);
567}
568
569void QQmlBind::setObject(QObject *obj)
570{
571 Q_D(QQmlBind);
572 switch (d->mode) {
573 case QQmlBindPrivate::GeneralizedGroup:
574 if (obj != nullptr)
575 warnIgnoredProperties(q: this);
576 return;
577 case QQmlBindPrivate::ObjectPropertyValue:
578 if (d->objectPropertyValueData.obj == obj)
579 return;
580 break;
581 case QQmlBindPrivate::Unknown:
582 if (obj == nullptr)
583 return;
584 new (&d->objectPropertyValueData) ObjectPropertyValueData;
585 d->mode = QQmlBindPrivate::ObjectPropertyValue;
586 break;
587 }
588
589 if (d->when) {
590 /* if we switch the object at runtime, we need to restore the
591 previous binding on the old object before continuing */
592 d->when = false;
593 eval();
594 d->when = true;
595 }
596 /* if "when" and "target" depend on the same property, we might
597 end up here before we could have updated "when". So reevaluate
598 when manually here.
599 */
600 const QQmlProperty whenProp(this, QLatin1StringView("when"));
601 const auto potentialWhenBinding = QQmlAnyBinding::ofProperty(prop: whenProp);
602 if (auto abstractBinding = potentialWhenBinding.asAbstractBinding()) {
603 QQmlBinding *binding = static_cast<QQmlBinding *>(abstractBinding);
604 if (binding->hasValidContext()) {
605 const auto boolType = QMetaType::fromType<bool>();
606 bool when;
607 binding->evaluate(result: &when, type: boolType);
608 if (when != d->when) {
609 d->when = when;
610 emit whenChanged();
611 }
612 }
613 }
614
615 switch (d->mode) {
616 case QQmlBindPrivate::GeneralizedGroup:
617 case QQmlBindPrivate::Unknown:
618 Q_UNREACHABLE();
619 return;
620 case QQmlBindPrivate::ObjectPropertyValue:
621 d->objectPropertyValueData.obj = obj;
622 if (d->componentComplete) {
623 setTarget(QQmlProperty(
624 d->objectPropertyValueData.obj, d->objectPropertyValueData.propName,
625 qmlContext(this)));
626 }
627 break;
628 }
629
630 if (d->componentComplete && d->when)
631 d->validate(q: this);
632
633 eval();
634 emit objectChanged();
635}
636
637/*!
638 \qmlproperty string QtQml::Binding::property
639
640 The property to be updated.
641
642 This can be a group property if the expression results in accessing a
643 property of a \l {QML Value Types}{value type}. For example:
644
645 \qml
646 Item {
647 id: item
648
649 property rect rectangle: Qt.rect(0, 0, 200, 200)
650 }
651
652 Binding {
653 target: item
654 property: "rectangle.x"
655 value: 100
656 }
657 \endqml
658
659 You only need to use this property if you can't supply the binding target
660 declaratively. The following snippet of code is equivalent to the above
661 binding, but more compact:
662
663 \qml
664 Binding { item.rectangle.x: 100 }
665 \endqml
666*/
667QString QQmlBind::property() const
668{
669 Q_D(const QQmlBind);
670 switch (d->mode) {
671 case QQmlBindPrivate::GeneralizedGroup:
672 case QQmlBindPrivate::Unknown:
673 return QString();
674 case QQmlBindPrivate::ObjectPropertyValue:
675 return d->objectPropertyValueData.propName;
676 }
677
678 Q_UNREACHABLE_RETURN(QString());
679}
680
681void QQmlBind::setProperty(const QString &p)
682{
683 Q_D(QQmlBind);
684 switch (d->mode) {
685 case QQmlBindPrivate::GeneralizedGroup:
686 if (!p.isEmpty())
687 warnIgnoredProperties(q: this);
688 return;
689 case QQmlBindPrivate::ObjectPropertyValue:
690 if (d->objectPropertyValueData.propName == p)
691 return;
692 break;
693 case QQmlBindPrivate::Unknown:
694 if (p.isEmpty())
695 return;
696 new (&d->objectPropertyValueData) ObjectPropertyValueData;
697 d->mode = QQmlBindPrivate::ObjectPropertyValue;
698 break;
699 }
700
701 if (!d->objectPropertyValueData.propName.isEmpty() && d->when) {
702 /* if we switch the property name at runtime, we need to restore the
703 previous binding on the old object before continuing */
704 d->when = false;
705 eval();
706 d->when = true;
707 }
708 d->objectPropertyValueData.propName = p;
709 if (d->componentComplete) {
710 setTarget(QQmlProperty(
711 d->objectPropertyValueData.obj, d->objectPropertyValueData.propName,
712 qmlContext(this)));
713 if (d->when)
714 d->validate(q: this);
715 }
716 eval();
717 emit propertyChanged();
718}
719
720/*!
721 \qmlproperty var QtQml::Binding::value
722
723 The value to be set on the target object and property. This can be a
724 constant (which isn't very useful), or a bound expression.
725
726 You only need to use this property if you can't supply the binding target
727 declaratively. Otherwise you can directly bind to the target.
728*/
729QVariant QQmlBind::value() const
730{
731 Q_D(const QQmlBind);
732 if (d->mode == QQmlBindPrivate::ObjectPropertyValue) {
733 Q_ASSERT(d->objectPropertyValueData.entry.currentKind == QQmlBindEntryKind::Variant);
734 QV4::ExecutionEngine *engine = d->objectPropertyValueData.entry.current.v4Value.engine();
735 return engine->toVariant(
736 value: *d->objectPropertyValueData.entry.current.v4Value.valueRef(), typeHint: QMetaType());
737
738 }
739 return QVariant();
740}
741
742void QQmlBind::setValue(const QVariant &v)
743{
744 Q_D(QQmlBind);
745 switch (d->mode) {
746 case QQmlBindPrivate::GeneralizedGroup:
747 if (v.isValid())
748 warnIgnoredProperties(q: this);
749 return;
750 case QQmlBindPrivate::Unknown:
751 if (!v.isValid())
752 return;
753 new (&d->objectPropertyValueData) ObjectPropertyValueData;
754 d->mode = QQmlBindPrivate::ObjectPropertyValue;
755 Q_FALLTHROUGH();
756 case QQmlBindPrivate::ObjectPropertyValue: {
757 QQmlBindEntry *targetEntry = &d->objectPropertyValueData.entry;
758 QQmlEngine *engine = qmlEngine(this);
759 if (!engine) {
760 qWarning() << "QQmlBind must be created in a QML context";
761 return;
762 }
763 targetEntry->currentKind
764 = targetEntry->current.set(engine: engine->handle(), v, oldKind: targetEntry->currentKind);
765 prepareEval();
766 break;
767 }
768 }
769 emit valueChanged();
770}
771
772/*!
773 \qmlproperty bool QtQml::Binding::delayed
774 \since 5.8
775
776 This property holds whether the binding should be delayed.
777
778 A delayed binding will not immediately update the target, but rather wait
779 until the event queue has been cleared. This can be used as an optimization,
780 or to prevent intermediary values from being assigned.
781
782 \code
783 Binding {
784 contactName.text.value: givenName + " " + familyName
785 when: list.ListView.isCurrentItem
786 delayed: true
787 }
788 \endcode
789
790 \note Using the \l delayed property incurs a run time cost as the Binding
791 element has to create a proxy for the value, so that it can delay its
792 application to the actual target. When using the \l target and
793 \l property properties, this cost is lower because the \l value
794 property can be re-used as proxy. When using the form shown above,
795 Binding will allocate a separate object with a dynamic meta-object to
796 hold the proxy values.
797*/
798bool QQmlBind::delayed() const
799{
800 Q_D(const QQmlBind);
801 return d->delayed;
802}
803
804void QQmlBind::setDelayed(bool delayed)
805{
806 Q_D(QQmlBind);
807 if (d->delayed == delayed)
808 return;
809
810 d->delayed = delayed;
811 if (!d->componentComplete)
812 return;
813
814 if (d->mode == QQmlBindPrivate::GeneralizedGroup) {
815 d->generalizedGroupData.delayedValues.reset();
816
817 QVarLengthArray<QQmlBindEntry, 1> oldEntries = std::move(d->generalizedGroupData.entries);
818 d->generalizedGroupData.entries.clear();
819 d->buildBindEntries(q: this, deferredState: nullptr);
820
821 for (qsizetype i = 0, end = oldEntries.size(); i < end; ++i) {
822 QQmlBindEntry &newEntry = d->generalizedGroupData.entries[i];
823 QQmlBindEntry &oldEntry = oldEntries[i];
824 newEntry.previousKind = newEntry.previous.set(
825 other: std::move(oldEntry.previous), newKind: oldEntry.previousKind, oldKind: newEntry.previousKind);
826 if (d->delayed && oldEntry.currentKind == QQmlBindEntryKind::Binding)
827 QQmlAnyBinding::removeBindingFrom(prop&: oldEntry.prop);
828 }
829 }
830
831 if (!d->delayed)
832 eval();
833
834 emit delayedChanged();
835}
836
837/*!
838 \qmlproperty enumeration QtQml::Binding::restoreMode
839 \since 5.14
840
841 This property can be used to describe if and how the original value should
842 be restored when the binding is disabled.
843
844 The possible values are:
845
846 \value Binding.RestoreNone The original value is not restored at all
847 \value Binding.RestoreBinding The original value is restored if it was another binding.
848 In that case the old binding is in effect again.
849 \value Binding.RestoreValue The original value is restored if it was a plain
850 value rather than a binding.
851 \value Binding.RestoreBindingOrValue The original value is always restored.
852
853 The default value is \c Binding.RestoreBindingOrValue.
854
855 \note This property exists for backwards compatibility with earlier versions
856 of Qt. Don't use it in new code.
857*/
858QQmlBind::RestorationMode QQmlBind::restoreMode() const
859{
860 Q_D(const QQmlBind);
861 unsigned result = RestoreNone;
862 if (d->restoreValue)
863 result |= RestoreValue;
864 if (d->restoreBinding)
865 result |= RestoreBinding;
866 return RestorationMode(result);
867}
868
869void QQmlBind::setRestoreMode(RestorationMode newMode)
870{
871 Q_D(QQmlBind);
872 if (newMode != restoreMode()) {
873 d->restoreValue = (newMode & RestoreValue);
874 d->restoreBinding = (newMode & RestoreBinding);
875 emit restoreModeChanged();
876 }
877}
878
879void QQmlBind::setTarget(const QQmlProperty &p)
880{
881 Q_D(QQmlBind);
882 if (QQmlBindEntry *target = d->targetEntry()) {
883 target->setTarget(q: this, p);
884 return;
885 }
886 qmlWarning(me: this).nospace()
887 << "You should not use the 'on' syntax for Binding elements with generalized group "
888 "properties. It is ignored.";
889
890}
891
892void QQmlBindEntry::setTarget(QQmlBind *q, const QQmlProperty &p)
893{
894 if (Q_UNLIKELY(lcQtQmlBindingRemoval().isInfoEnabled())) {
895 if (QObject *oldObject = prop.object()) {
896 QMetaProperty metaProp = oldObject->metaObject()->property(index: prop.index());
897 if (metaProp.hasNotifySignal()) {
898 QByteArray signal('2' + metaProp.notifySignal().methodSignature());
899 QObject::disconnect(sender: oldObject, signal: signal.constData(),
900 receiver: q, SLOT(targetValueChanged()));
901 }
902 }
903 p.connectNotifySignal(dest: q, SLOT(targetValueChanged()));
904 }
905
906 prop = p;
907}
908
909void QQmlBind::classBegin()
910{
911 Q_D(QQmlBind);
912 d->componentComplete = false;
913}
914
915static QQmlAnyBinding createBinding(
916 const QQmlProperty &prop, const QV4::CompiledData::Binding *binding,
917 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
918 const QQmlRefPointer<QQmlContextData> &contextData,
919 QObject *scopeObject)
920{
921 switch (binding->type()) {
922 case QV4::CompiledData::Binding::Type_Translation:
923 case QV4::CompiledData::Binding::Type_TranslationById:
924 return QQmlAnyBinding::createTranslationBinding(prop, compilationUnit, translationBinding: binding, scopeObject);
925 case QV4::CompiledData::Binding::Type_Script: {
926 const QQmlBinding::Identifier id = binding->value.compiledScriptIndex;
927 if (id == QQmlBinding::Invalid) {
928 return QQmlAnyBinding::createFromCodeString(
929 prop, code: compilationUnit->bindingValueAsString(binding), obj: scopeObject,
930 ctxt: contextData, url: compilationUnit->finalUrlString(), lineNumber: binding->location.line());
931 }
932 QV4::Scope scope(contextData->engine()->handle());
933 QV4::Scoped<QV4::QmlContext> qmlCtxt(
934 scope, QV4::QmlContext::create(
935 parent: scope.engine->rootContext(), context: contextData, scopeObject));
936 return QQmlAnyBinding::createFromFunction(
937 prop, function: compilationUnit->runtimeFunctions.at(i: id), obj: scopeObject, ctxt: contextData,
938 scope: qmlCtxt);
939 }
940 default:
941 break;
942 }
943 return QQmlAnyBinding();
944}
945
946static void initCreator(
947 QQmlData::DeferredData *deferredData,
948 const QQmlRefPointer<QQmlContextData> &contextData,
949 QQmlComponentPrivate::ConstructionState *immediateState)
950{
951 if (!immediateState->hasCreator()) {
952 immediateState->setCompletePending(true);
953 immediateState->initCreator(
954 parentContext: deferredData->context->parent(), compilationUnit: deferredData->compilationUnit,
955 creationContext: contextData, inlineComponentName: deferredData->inlineComponentName);
956 immediateState->creator()->beginPopulateDeferred(context: deferredData->context);
957 }
958}
959
960void QQmlBindPrivate::decodeBinding(
961 QQmlBind *q, const QString &propertyPrefix,
962 QQmlData::DeferredData *deferredData,
963 const QV4::CompiledData::Binding *binding,
964 QQmlComponentPrivate::ConstructionState *immediateState)
965{
966 const QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit
967 = deferredData->compilationUnit;
968 const QString propertySuffix = compilationUnit->stringAt(index: binding->propertyNameIndex);
969 const QString propertyName = propertyPrefix + propertySuffix;
970
971 switch (binding->type()) {
972 case QV4::CompiledData::Binding::Type_AttachedProperty:
973 if (propertyPrefix.isEmpty()) {
974 // Top-level attached properties cannot be generalized grouped properties.
975 // Treat them as regular properties.
976 // ... unless we're not supposed to handle regular properties. Then ignore them.
977 if (!immediateState)
978 return;
979
980 Q_ASSERT(compilationUnit->stringAt(compilationUnit->objectAt(binding->value.objectIndex)
981 ->inheritedTypeNameIndex).isEmpty());
982
983 const QV4::ResolvedTypeReference *typeReference
984 = compilationUnit->resolvedType(id: binding->propertyNameIndex);
985 Q_ASSERT(typeReference);
986 QQmlType attachedType = typeReference->type();
987 if (!attachedType.isValid()) {
988 if (QQmlTypeLoader *typeLoader = compilationUnit->engine->typeLoader()) {
989 const QQmlTypeNameCache::Result result
990 = deferredData->context->imports()->query(key: propertySuffix, typeLoader);
991 if (!result.isValid()) {
992 qmlWarning(me: q).nospace()
993 << "Unknown name " << propertySuffix << ". The binding is ignored.";
994 return;
995 }
996 attachedType = result.type;
997 }
998 }
999
1000 QQmlContext *context = qmlContext(q);
1001 QObject *attachedObject = qmlAttachedPropertiesObject(
1002 q, func: attachedType.attachedPropertiesFunction(
1003 enginePrivate: QQmlEnginePrivate::get(e: context->engine())));
1004 if (!attachedObject) {
1005 qmlWarning(me: q).nospace() <<"Could not create attached properties object '"
1006 << attachedType.typeName() << "'";
1007 return;
1008 }
1009
1010 initCreator(deferredData, contextData: QQmlContextData::get(context), immediateState);
1011 immediateState->creator()->populateDeferredInstance(
1012 outerObject: q, deferredIndex: deferredData->deferredIdx, index: binding->value.objectIndex, instance: attachedObject,
1013 bindingTarget: attachedObject, /*value type property*/ valueTypeProperty: nullptr, binding);
1014 return;
1015 }
1016 Q_FALLTHROUGH();
1017 case QV4::CompiledData::Binding::Type_GroupProperty: {
1018 const QString pre = propertyName + u'.';
1019 const QV4::CompiledData::Object *subObj
1020 = compilationUnit->objectAt(index: binding->value.objectIndex);
1021 const QV4::CompiledData::Binding *subBinding = subObj->bindingTable();
1022 for (quint32 i = 0; i < subObj->nBindings; ++i, ++subBinding)
1023 decodeBinding(q, propertyPrefix: pre, deferredData, binding: subBinding, immediateState);
1024 return;
1025 }
1026 default:
1027 break;
1028 }
1029
1030 QQmlBindEntry entry;
1031 QQmlContext *context = qmlContext(q);
1032 const QQmlRefPointer<QQmlContextData> contextData = QQmlContextData::get(context);
1033 entry.prop = QQmlPropertyPrivate::create(target: nullptr, propertyName, context: contextData,
1034 flags: QQmlPropertyPrivate::InitFlag::AllowId);
1035 if (!entry.prop.isValid()) {
1036 // Try again in the context of this object. If that works, it's a regular property.
1037 // ... unless we're not supposed to handle regular properties. Then ignore it.
1038 if (!immediateState)
1039 return;
1040
1041 QQmlProperty property = QQmlPropertyPrivate::create(
1042 target: q, propertyName, context: contextData, flags: QQmlPropertyPrivate::InitFlag::AllowSignal);
1043 if (property.isValid()) {
1044 initCreator(deferredData, contextData, immediateState);
1045 immediateState->creator()->populateDeferredBinding(
1046 qmlProperty: property, deferredIndex: deferredData->deferredIdx, binding);
1047 } else {
1048 qmlWarning(me: q).nospace() << "Unknown name " << propertyName
1049 << ". The binding is ignored.";
1050 }
1051 return;
1052 }
1053
1054 switch (mode) {
1055 case GeneralizedGroup:
1056 break;
1057 case ObjectPropertyValue:
1058 warnIgnoredProperties(q);
1059 objectPropertyValueData.~ObjectPropertyValueData();
1060 Q_FALLTHROUGH();
1061 case Unknown:
1062 new (&generalizedGroupData) GeneralizedGroupData;
1063 mode = GeneralizedGroup;
1064 break;
1065 }
1066
1067 const auto setVariant = [&entry](QV4::PersistentValue value) {
1068 entry.currentKind = entry.current.setVariant(v: value, oldKind: entry.currentKind);
1069 };
1070
1071 const auto setBinding = [&entry](QQmlAnyBinding binding) {
1072 entry.currentKind = entry.current.set(v: binding, oldKind: entry.currentKind);
1073 };
1074
1075 switch (binding->type()) {
1076 case QV4::CompiledData::Binding::Type_AttachedProperty:
1077 case QV4::CompiledData::Binding::Type_GroupProperty:
1078 Q_UNREACHABLE(); // Handled above
1079 break;
1080 case QV4::CompiledData::Binding::Type_Translation:
1081 case QV4::CompiledData::Binding::Type_TranslationById:
1082 case QV4::CompiledData::Binding::Type_Script:
1083 if (delayed) {
1084 if (!generalizedGroupData.delayedValues)
1085 createDelayedValues();
1086 const QString delayedName = QString::number(generalizedGroupData.entries.size());
1087 generalizedGroupData.delayedValues->insert(key: delayedName, value: QVariant());
1088 QQmlProperty bindingTarget
1089 = QQmlProperty(generalizedGroupData.delayedValues.get(), delayedName);
1090 Q_ASSERT(bindingTarget.isValid());
1091 QQmlAnyBinding anyBinding = createBinding(
1092 prop: bindingTarget, binding, compilationUnit, contextData, scopeObject: q);
1093 anyBinding.installOn(target: bindingTarget);
1094 } else {
1095 setBinding(createBinding(prop: entry.prop, binding, compilationUnit, contextData, scopeObject: q));
1096 }
1097 break;
1098 case QV4::CompiledData::Binding::Type_String:
1099 setVariant(QV4::PersistentValue(
1100 compilationUnit->engine,
1101 compilationUnit->runtimeStrings[binding->stringIndex]->asReturnedValue()));
1102 break;
1103 case QV4::CompiledData::Binding::Type_Number:
1104 setVariant(QV4::PersistentValue(
1105 compilationUnit->engine,
1106 compilationUnit->constants[binding->value.constantValueIndex].asReturnedValue()));
1107 break;
1108 case QV4::CompiledData::Binding::Type_Boolean:
1109 setVariant(QV4::PersistentValue(compilationUnit->engine, QV4::Encode(binding->value.b)));
1110 break;
1111 case QV4::CompiledData::Binding::Type_Null:
1112 setVariant(QV4::PersistentValue(compilationUnit->engine, QV4::Encode::null()));
1113 break;
1114 case QV4::CompiledData::Binding::Type_Object:
1115 case QV4::CompiledData::Binding::Type_Invalid:
1116 break;
1117 }
1118
1119 generalizedGroupData.entries.append(t: std::move(entry));
1120}
1121
1122void QQmlBindPrivate::createDelayedValues()
1123{
1124 generalizedGroupData.delayedValues = std::make_unique<QQmlPropertyMap>();
1125 QQmlPropertyMap *delayedValues = generalizedGroupData.delayedValues.get();
1126 QObject::connect(
1127 sender: delayedValues, signal: &QQmlPropertyMap::valueChanged,
1128 context: delayedValues, slot: [this](QString delayedName, const QVariant &value) {
1129 Q_UNUSED(value);
1130 onDelayedValueChanged(delayedName: std::move(delayedName));
1131 }
1132 );
1133}
1134
1135void QQmlBindPrivate::onDelayedValueChanged(QString delayedName)
1136{
1137 Q_ASSERT(delayed);
1138 Q_ASSERT(mode == GeneralizedGroup);
1139 QQmlPropertyMap *delayedValues = generalizedGroupData.delayedValues.get();
1140 Q_ASSERT(delayedValues);
1141 const QString pendingName = QStringLiteral("pending");
1142 QStringList pending = qvariant_cast<QStringList>(v: (*delayedValues)[pendingName]);
1143 if (componentComplete && pending.size() == 0)
1144 QTimer::singleShot(interval: 0, receiver: delayedValues, slot: [this]() { evalDelayed(); });
1145 else if (pending.contains(str: delayedName))
1146 return;
1147
1148 pending.append(t: std::move(delayedName));
1149 (*delayedValues)[pendingName].setValue(std::move(pending));
1150}
1151
1152void QQmlBindPrivate::evalDelayed()
1153{
1154 Q_ASSERT(mode == GeneralizedGroup);
1155 QQmlPropertyMap *delayedValues = generalizedGroupData.delayedValues.get();
1156 if (!when || !delayedValues)
1157 return;
1158
1159 const QString pendingName = QStringLiteral("pending");
1160 const QStringList pending = qvariant_cast<QStringList>(v: (*delayedValues)[pendingName]);
1161 for (const QString &delayedName : pending) {
1162 bool ok;
1163 const int delayedIndex = delayedName.toInt(ok: &ok);
1164 Q_ASSERT(ok);
1165 Q_ASSERT(delayedIndex >= 0 && delayedIndex < generalizedGroupData.entries.size());
1166 generalizedGroupData.entries[delayedIndex].prop.write((*delayedValues)[delayedName]);
1167 }
1168 (*delayedValues)[pendingName].setValue(QStringList());
1169}
1170
1171void QQmlBindPrivate::buildBindEntries(QQmlBind *q, QQmlComponentPrivate::DeferredState *deferredState)
1172{
1173 QQmlData *data = QQmlData::get(object: q);
1174 if (data && !data->deferredData.isEmpty()) {
1175 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(e: data->context->engine());
1176 for (QQmlData::DeferredData *deferredData : data->deferredData) {
1177 QMultiHash<int, const QV4::CompiledData::Binding *> *bindings = &deferredData->bindings;
1178 if (deferredState) {
1179 QQmlComponentPrivate::ConstructionState constructionState;
1180 for (auto it = bindings->cbegin(); it != bindings->cend(); ++it)
1181 decodeBinding(q, propertyPrefix: QString(), deferredData, binding: *it, immediateState: &constructionState);
1182
1183
1184 if (constructionState.hasCreator()) {
1185 ++ep->inProgressCreations;
1186 constructionState.creator()->finalizePopulateDeferred();
1187 constructionState.appendCreatorErrors();
1188 deferredState->push_back(x: std::move(constructionState));
1189 }
1190 } else {
1191 for (auto it = bindings->cbegin(); it != bindings->cend(); ++it)
1192 decodeBinding(q, propertyPrefix: QString(), deferredData, binding: *it, immediateState: nullptr);
1193 }
1194 }
1195
1196 if (deferredState) {
1197 data->releaseDeferredData();
1198 if (!deferredState->empty())
1199 QQmlComponentPrivate::completeDeferred(enginePriv: ep, deferredState);
1200 }
1201 }
1202}
1203
1204void QQmlBind::componentComplete()
1205{
1206 Q_D(QQmlBind);
1207 QQmlComponentPrivate::DeferredState deferredState;
1208 d->buildBindEntries(q: this, deferredState: &deferredState);
1209 d->componentComplete = true;
1210 if (d->mode == QQmlBindPrivate::ObjectPropertyValue) {
1211 QQmlBindEntry *target = d->targetEntry();
1212 if (!target->prop.isValid()) {
1213 target->setTarget(q: this, p: QQmlProperty(
1214 d->objectPropertyValueData.obj,
1215 d->objectPropertyValueData.propName, qmlContext(this)));
1216 }
1217 }
1218 d->validate(q: this);
1219 if (d->mode == QQmlBindPrivate::GeneralizedGroup)
1220 d->evalDelayed();
1221 eval();
1222}
1223
1224void QQmlBind::prepareEval()
1225{
1226 Q_D(QQmlBind);
1227 if (d->delayed) {
1228 if (!d->pendingEval)
1229 QTimer::singleShot(interval: 0, receiver: this, slot: &QQmlBind::eval);
1230 d->pendingEval = true;
1231 } else {
1232 eval();
1233 }
1234}
1235
1236void QQmlBindEntry::clearPrev()
1237{
1238 previousKind = previous.destroy(kind: previousKind);
1239}
1240
1241bool QQmlBindPrivate::isCurrent(QQmlBindEntry *entry) const
1242{
1243 switch (entry->currentKind) {
1244 case QQmlBindEntryKind::V4Value: {
1245 auto propPriv = QQmlPropertyPrivate::get(p: entry->prop);
1246 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(obj: propPriv->object);
1247 Q_ASSERT(vmemo);
1248 return QV4::RuntimeHelpers::strictEqual(
1249 // fromReturnedValue is OK here because strictEqual will not allocate
1250 x: QV4::Value::fromReturnedValue(val: vmemo->vmeProperty(index: propPriv->core.coreIndex())),
1251 y: *entry->current.v4Value.valueRef());
1252 }
1253 case QQmlBindEntryKind::Variant: {
1254 const QV4::PersistentValue &v4Value = entry->current.v4Value;
1255 return v4Value.engine()->toVariant(value: *v4Value.valueRef(), typeHint: entry->prop.propertyMetaType())
1256 == entry->prop.read();
1257 }
1258 case QQmlBindEntryKind::Binding:
1259 return entry->current.binding == QQmlAnyBinding::ofProperty(prop: entry->prop);
1260 case QQmlBindEntryKind::None:
1261 break;
1262 }
1263
1264 return false;
1265}
1266
1267void QQmlBindPrivate::preEvalEntry(QQmlBindEntry *entry)
1268{
1269 if (!entry->prop.isValid() || (entry->currentKind == QQmlBindEntryKind::None))
1270 return;
1271 if (!entry->prop.object())
1272 return; // if the target is already gone, we can't do anything
1273
1274 if (!when) {
1275 if (!isCurrent(entry)) {
1276 entry->clearPrev();
1277 return;
1278 }
1279
1280 //restore any previous binding
1281 switch (entry->previousKind) {
1282 case QQmlBindEntryKind::Binding:
1283 if (restoreBinding) {
1284 QQmlAnyBinding p = std::move(entry->previous.binding);
1285 entry->clearPrev(); // Do that before setBinding(), as setBinding() may recurse.
1286 p.installOn(target: entry->prop);
1287 }
1288 break;
1289 case QQmlBindEntryKind::V4Value:
1290 if (restoreValue) {
1291 QQmlAnyBinding::takeFrom(prop: entry->prop); // we don't want to have a binding active
1292 auto propPriv = QQmlPropertyPrivate::get(p: entry->prop);
1293 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(obj: propPriv->object);
1294 Q_ASSERT(vmemo);
1295 vmemo->setVMEProperty(index: propPriv->core.coreIndex(),
1296 v: *entry->previous.v4Value.valueRef());
1297 entry->clearPrev();
1298 }
1299 break;
1300 case QQmlBindEntryKind::Variant:
1301 if (restoreValue) {
1302 QQmlAnyBinding::takeFrom(prop: entry->prop); // we don't want to have a binding active
1303 const QV4::PersistentValue &v4Value = entry->previous.v4Value;
1304 entry->prop.write(v4Value.engine()->toVariant(
1305 value: *v4Value.valueRef(), typeHint: entry->prop.propertyMetaType()));
1306 entry->clearPrev();
1307 }
1308 break;
1309 case QQmlBindEntryKind::None:
1310 break;
1311 }
1312 return;
1313 }
1314
1315 //save any set binding for restoration
1316 if (entry->previousKind == QQmlBindEntryKind::None) {
1317 // try binding first; we need to use takeFrom to properly unlink the binding
1318 QQmlAnyBinding prevBind = QQmlAnyBinding::takeFrom(prop: entry->prop);
1319 if (prevBind) {
1320 entry->previousKind = entry->previous.set(v: std::move(prevBind), oldKind: entry->previousKind);
1321 } else {
1322 // nope, try a V4 value next
1323 auto propPriv = QQmlPropertyPrivate::get(p: entry->prop);
1324 auto propData = propPriv->core;
1325 if (!propPriv->valueTypeData.isValid() && propData.isVarProperty()) {
1326 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(obj: propPriv->object);
1327 Q_ASSERT(vmemo);
1328 auto retVal = vmemo->vmeProperty(index: propData.coreIndex());
1329 entry->previousKind = entry->previous.set(
1330 v: QV4::PersistentValue(vmemo->engine, retVal), oldKind: entry->previousKind);
1331 } else {
1332 // nope, use the meta object to get a QVariant
1333 entry->previousKind = entry->previous.set(
1334 engine: propPriv->engine->handle(), v: entry->prop.read(), oldKind: entry->previousKind);
1335 }
1336 }
1337 }
1338
1339 // NOTE: removeBinding has no effect on QProperty classes, but
1340 // we already used takeBinding to remove it
1341 QQmlPropertyPrivate::removeBinding(that: entry->prop, flags: QQmlPropertyPrivate::OverrideSticky);
1342}
1343
1344void QQmlBindPrivate::postEvalEntry(QQmlBindEntry *entry)
1345{
1346 if (!entry->prop.isValid())
1347 return;
1348 switch (entry->currentKind) {
1349 case QQmlBindEntryKind::Variant: {
1350 const QV4::PersistentValue &v4Value = entry->current.v4Value;
1351 entry->prop.write(
1352 v4Value.engine()->toVariant(value: *v4Value.valueRef(), typeHint: entry->prop.propertyMetaType()));
1353 break;
1354 }
1355 case QQmlBindEntryKind::Binding:
1356 Q_ASSERT(!delayed);
1357 entry->current.binding.installOn(target: entry->prop);
1358 break;
1359 case QQmlBindEntryKind::V4Value: {
1360 auto propPriv = QQmlPropertyPrivate::get(p: entry->prop);
1361 QQmlVMEMetaObject::get(obj: propPriv->object)->setVMEProperty(
1362 index: propPriv->core.coreIndex(), v: *entry->current.v4Value.valueRef());
1363 break;
1364 }
1365 case QQmlBindEntryKind::None:
1366 break;
1367 }
1368}
1369
1370void QQmlBind::eval()
1371{
1372 Q_D(QQmlBind);
1373 d->pendingEval = false;
1374 if (!d->componentComplete)
1375 return;
1376
1377 switch (d->mode) {
1378 case QQmlBindPrivate::GeneralizedGroup:
1379 for (QQmlBindEntry &entry : d->generalizedGroupData.entries)
1380 d->preEvalEntry(entry: &entry);
1381 break;
1382 case QQmlBindPrivate::ObjectPropertyValue:
1383 d->preEvalEntry(entry: &d->objectPropertyValueData.entry);
1384 break;
1385 case QQmlBindPrivate::Unknown:
1386 break;
1387 }
1388
1389 if (!d->when)
1390 return;
1391
1392 d->writingProperty = true;
1393 switch (d->mode) {
1394 case QQmlBindPrivate::GeneralizedGroup:
1395 for (QQmlBindEntry &entry : d->generalizedGroupData.entries)
1396 d->postEvalEntry(entry: &entry);
1397 break;
1398 case QQmlBindPrivate::ObjectPropertyValue:
1399 d->postEvalEntry(entry: &d->objectPropertyValueData.entry);
1400 break;
1401 case QQmlBindPrivate::Unknown:
1402 break;
1403 }
1404 d->writingProperty = false;
1405}
1406
1407void QQmlBind::targetValueChanged()
1408{
1409 Q_D(QQmlBind);
1410 if (d->writingProperty)
1411 return;
1412
1413 if (!d->when)
1414 return;
1415
1416 QUrl url;
1417 quint16 line = 0;
1418
1419 const QQmlData *ddata = QQmlData::get(object: this, create: false);
1420 if (ddata && ddata->outerContext) {
1421 url = ddata->outerContext->url();
1422 line = ddata->lineNumber;
1423 }
1424
1425 qCInfo(lcQtQmlBindingRemoval,
1426 "The target property of the Binding element created at %s:%d was changed from "
1427 "elsewhere. This does not overwrite the binding. The target property will still be "
1428 "updated when the value of the Binding element changes.",
1429 qPrintable(url.toString()), line);
1430}
1431
1432QT_END_NAMESPACE
1433
1434#include "moc_qqmlbind_p.cpp"
1435

source code of qtdeclarative/src/qmlmeta/types/qqmlbind.cpp