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