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