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 grouped 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 grouped 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_GroupedProperty: {
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_GroupedProperty:
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 =
1309 QQmlVMEMetaObject::get(propPriv->object, propPriv->engine->handle());
1310 Q_ASSERT(vmemo);
1311 return QV4::RuntimeHelpers::strictEqual(
1312 // fromReturnedValue is OK here because strictEqual will not allocate
1313 QV4::Value::fromReturnedValue(vmemo->vmeProperty(propPriv->core.coreIndex())),
1314 *entry->current.v4Value.valueRef());
1315 }
1316 case QQmlBindEntryKind::Variant: {
1317 const QV4::PersistentValue &v4Value = entry->current.v4Value;
1318 QMetaType propMetaType = entry->prop.propertyMetaType();
1319 /*
1320 * We currently really want to use toVariant here, and not metaTypeFromJS.
1321 * metaTypeFromJS can't do all conversions, and would leave us with an
1322 * empty QVariant if it fails; whereas toVariant tries very hard to give
1323 * us a QVariant representation, even though its type might not match the
1324 * property's meta-type. In case of an actual mismatch, we use
1325 * convertToWriteTargetType to mirror the conversions that writing to a
1326 * property would have done; with the exception of list properties, where
1327 * we directly go through a specialized code path using
1328 * QQmlPropertyPrivate::convertToQQmlListProperty
1329 */
1330 QVariant valueAsVariant = v4Value.engine()->toVariant(*v4Value.valueRef(),
1331 propMetaType);
1332 if (propMetaType.flags() & QMetaType::IsQmlList) {
1333 // our expected value is not necessarily of the correct type, so we copy the steps done by
1334 // QQmlPropertyPrivate::write to bring them into the correct format
1335 QList<QObject *> expectedObjectList;
1336 QQmlListProperty<QObject> expectedAsListProp(nullptr, &expectedObjectList);
1337 QQmlPropertyPrivate::convertToQQmlListProperty(&expectedAsListProp, propMetaType, valueAsVariant);
1338
1339 QVariant actualValue = entry->prop.read();
1340 // reading a QQmlListProperty property yields a QQmlListReference
1341 QQmlListReference* listReference = get_if<QQmlListReference>(&actualValue);
1342 Q_ASSERT(listReference);
1343 auto actualObjectList = QQmlListReferencePrivate::get(listReference)->property.toList<QList<QObject *>>();
1344 return std::equal(expectedObjectList.constBegin(), expectedObjectList.constEnd(),
1345 actualObjectList.constBegin(), actualObjectList.constEnd());
1346
1347 } else if (QMetaType varMetaType = valueAsVariant.metaType(); varMetaType != propMetaType) {
1348 QVariant converted = QQmlPropertyPrivate::convertToWriteTargetType(
1349 valueAsVariant, propMetaType);
1350 if (converted.isValid())
1351 valueAsVariant = std::move(converted);
1352 }
1353 return valueAsVariant
1354 == entry->prop.read();
1355 }
1356 case QQmlBindEntryKind::Binding:
1357 return entry->current.binding == QQmlAnyBinding::ofProperty(entry->prop);
1358 case QQmlBindEntryKind::None:
1359 break;
1360 }
1361
1362 return false;
1363}
1364
1366{
1367 if (!entry->prop.isValid() || (entry->currentKind == QQmlBindEntryKind::None))
1368 return;
1369 if (!entry->prop.object())
1370 return; // if the target is already gone, we can't do anything
1371
1372 if (!when) {
1373 if (!isCurrent(entry)) {
1374 entry->clearPrev();
1375 return;
1376 }
1377
1378 //restore any previous binding
1379 switch (entry->previousKind) {
1380 case QQmlBindEntryKind::Binding:
1381 if (restoreBinding) {
1382 QQmlAnyBinding p = std::move(entry->previous.binding);
1383 entry->clearPrev(); // Do that before setBinding(), as setBinding() may recurse.
1384 p.installOn(entry->prop);
1385 }
1386 break;
1387 case QQmlBindEntryKind::V4Value:
1388 if (restoreValue) {
1389 QQmlAnyBinding::takeFrom(entry->prop); // we don't want to have a binding active
1390 auto propPriv = QQmlPropertyPrivate::get(entry->prop);
1391 QQmlVMEMetaObject *vmemo =
1392 QQmlVMEMetaObject::get(propPriv->object, propPriv->engine->handle());
1393 Q_ASSERT(vmemo);
1394 vmemo->setVMEProperty(propPriv->core.coreIndex(),
1395 *entry->previous.v4Value.valueRef());
1396 entry->clearPrev();
1397 }
1398 break;
1399 case QQmlBindEntryKind::Variant:
1400 if (restoreValue) {
1401 QQmlAnyBinding::takeFrom(entry->prop); // we don't want to have a binding active
1402 const QV4::PersistentValue &v4Value = entry->previous.v4Value;
1403 entry->prop.write(v4Value.engine()->toVariant(
1404 *v4Value.valueRef(), entry->prop.propertyMetaType()));
1405 entry->clearPrev();
1406 }
1407 break;
1408 case QQmlBindEntryKind::None:
1409 break;
1410 }
1411 return;
1412 }
1413
1414 //save any set binding for restoration
1415 if (entry->previousKind == QQmlBindEntryKind::None) {
1416 // try binding first; we need to use takeFrom to properly unlink the binding
1417 QQmlAnyBinding prevBind = QQmlAnyBinding::takeFrom(entry->prop);
1418 if (prevBind) {
1419 entry->previousKind = entry->previous.set(std::move(prevBind), entry->previousKind);
1420 } else {
1421 // nope, try a V4 value next
1422 auto propPriv = QQmlPropertyPrivate::get(entry->prop);
1423 auto propData = propPriv->core;
1424 if (!propPriv->valueTypeData.isValid() && propData.isVarProperty()) {
1425 QQmlVMEMetaObject *vmemo =
1426 QQmlVMEMetaObject::get(propPriv->object, propPriv->engine->handle());
1427 Q_ASSERT(vmemo);
1428 auto retVal = vmemo->vmeProperty(propData.coreIndex());
1429 entry->previousKind = entry->previous.set(
1430 QV4::PersistentValue(vmemo->engine(), retVal), entry->previousKind);
1431 } else if (entry->prop.propertyMetaType().flags() & QMetaType::IsQmlList) {
1432 using namespace QV4;
1433 ExecutionEngine *v4 = qmlEngine(q_func())->handle();
1434 entry->previousKind = entry->previous.setVariant(
1435 QV4::PersistentValue(v4, QV4::QmlListWrapper::createOwned(v4, entry->prop)),
1436 entry->previousKind);
1437
1438 } else {
1439 // nope, use the meta object to get a QVariant
1440 entry->previousKind = entry->previous.set(
1441 propPriv->engine->handle(), entry->prop.read(), entry->previousKind);
1442 }
1443 }
1444 }
1445
1446 // NOTE: removeBinding has no effect on QProperty classes, but
1447 // we already used takeBinding to remove it
1448 QQmlPropertyPrivate::removeBinding(entry->prop, QQmlPropertyPrivate::OverrideSticky);
1449}
1450
1452{
1453 if (!entry->prop.isValid())
1454 return;
1455 switch (entry->currentKind) {
1456 case QQmlBindEntryKind::Variant: {
1457 const QV4::PersistentValue &v4Value = entry->current.v4Value;
1458 entry->prop.write(
1459 v4Value.engine()->toVariant(*v4Value.valueRef(), entry->prop.propertyMetaType()));
1460 break;
1461 }
1462 case QQmlBindEntryKind::Binding:
1463 Q_ASSERT(!delayed);
1464 entry->current.binding.installOn(entry->prop);
1465 break;
1466 case QQmlBindEntryKind::V4Value: {
1467 auto propPriv = QQmlPropertyPrivate::get(entry->prop);
1468 QQmlVMEMetaObject::get(propPriv->object, propPriv->engine->handle())->setVMEProperty(
1469 propPriv->core.coreIndex(), *entry->current.v4Value.valueRef());
1470 break;
1471 }
1472 case QQmlBindEntryKind::None:
1473 break;
1474 }
1475}
1476
1477void QQmlBind::eval()
1478{
1479 Q_D(QQmlBind);
1480 d->pendingEval = false;
1481 if (!d->componentComplete)
1482 return;
1483
1484 switch (d->mode) {
1485 case QQmlBindPrivate::GeneralizedGroup:
1486 for (QQmlBindEntry &entry : d->generalizedGroupData.entries)
1487 d->preEvalEntry(&entry);
1488 break;
1489 case QQmlBindPrivate::ObjectPropertyValue:
1490 d->preEvalEntry(&d->objectPropertyValueData.entry);
1491 break;
1492 case QQmlBindPrivate::Unknown:
1493 break;
1494 }
1495
1496 if (!d->when)
1497 return;
1498
1499 d->writingProperty = true;
1500 switch (d->mode) {
1501 case QQmlBindPrivate::GeneralizedGroup:
1502 for (QQmlBindEntry &entry : d->generalizedGroupData.entries)
1503 d->postEvalEntry(&entry);
1504 break;
1505 case QQmlBindPrivate::ObjectPropertyValue:
1506 d->postEvalEntry(&d->objectPropertyValueData.entry);
1507 break;
1508 case QQmlBindPrivate::Unknown:
1509 break;
1510 }
1511 d->writingProperty = false;
1512}
1513
1514void QQmlBind::targetValueChanged()
1515{
1516 Q_D(QQmlBind);
1517 if (d->writingProperty)
1518 return;
1519
1520 if (!d->when)
1521 return;
1522
1523 QUrl url;
1524 quint16 line = 0;
1525
1526 const QQmlData *ddata = QQmlData::get(this, false);
1527 if (ddata && ddata->outerContext) {
1528 url = ddata->outerContext->url();
1529 line = ddata->lineNumber;
1530 }
1531
1532 qCInfo(lcQtQmlBindingRemoval,
1533 "The target property of the Binding element created at %s:%d was changed from "
1534 "elsewhere. This does not overwrite the binding. The target property will still be "
1535 "updated when the value of the Binding element changes.",
1536 qPrintable(url.toString()), line);
1537}
1538
1539QT_END_NAMESPACE
1540
1541#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