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