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
qqmlproperty.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qqmlproperty.h"
5
6#include <private/qjsvalue_p.h>
7#include <private/qmetaobject_p.h>
8#include <private/qproperty_p.h>
9#include <private/qqmlboundsignal_p.h>
10#include <private/qqmlbuiltinfunctions_p.h>
11#include <private/qqmldata_p.h>
12#include <private/qqmlengine_p.h>
13#include <private/qqmlirbuilder_p.h>
14#include <private/qqmllist_p.h>
15#include <private/qqmllistwrapper_p.h>
16#include <private/qqmlproperty_p.h>
17#include <private/qqmlsignalnames_p.h>
18#include <private/qqmlstringconverters_p.h>
19#include <private/qqmlvaluetypeproxybinding_p.h>
20#include <private/qqmlvaluetypewrapper_p.h>
21#include <private/qqmlvmemetaobject_p.h>
22#include <private/qv4functionobject_p.h>
23#include <private/qv4qobjectwrapper_p.h>
24
25#include <QtQml/qqmlcontext.h>
26#include <QtQml/qqmlengine.h>
27#include <QtQml/qqmlpropertymap.h>
28
29#include <QtCore/qdebug.h>
30#include <QtCore/qsequentialiterable.h>
31#include <QtCore/qstringlist.h>
32#include <QtCore/qvector.h>
33
34#include <cmath>
35
37
39
40/*!
41\class QQmlProperty
42\since 5.0
43\inmodule QtQml
44\brief The QQmlProperty class abstracts accessing properties on objects created from QML.
45
46As QML uses Qt's meta-type system all of the existing QMetaObject classes can be used to introspect
47and interact with objects created by QML. However, some of the new features provided by QML - such
48as type safety and attached properties - are most easily used through the QQmlProperty class
49that simplifies some of their natural complexity.
50
51Unlike QMetaProperty which represents a property on a class type, QQmlProperty encapsulates
52a property on a specific object instance. To read a property's value, programmers create a
53QQmlProperty instance and call the read() method. Likewise to write a property value the
54write() method is used.
55
56For example, for the following QML code:
57
58\qml
59// MyItem.qml
60import QtQuick 2.0
61
62Text { text: "A bit of text" }
63\endqml
64
65The \l Text object's properties could be accessed using QQmlProperty, like this:
66
67\code
68#include <QQmlProperty>
69#include <QGraphicsObject>
70
71...
72
73QQuickView view(QUrl::fromLocalFile("MyItem.qml"));
74QQmlProperty property(view.rootObject(), "font.pixelSize");
75qWarning() << "Current pixel size:" << property.read().toInt();
76property.write(24);
77qWarning() << "Pixel size should now be 24:" << property.read().toInt();
78\endcode
79*/
80
81/*!
82 Create an invalid QQmlProperty.
83*/
84QQmlProperty::QQmlProperty() = default;
85
86/*! \internal */
87QQmlProperty::~QQmlProperty()
88{
89 if (d)
90 d->release();
91 d = nullptr;
92}
93
94/*!
95 Creates a QQmlProperty for the default property of \a obj. If there is no
96 default property, an invalid QQmlProperty will be created.
97 */
98QQmlProperty::QQmlProperty(QObject *obj)
99: d(new QQmlPropertyPrivate)
100{
101 d->initDefault(obj);
102}
103
104/*!
105 Creates a QQmlProperty for the default property of \a obj
106 using the \l{QQmlContext} {context} \a ctxt. If there is
107 no default property, an invalid QQmlProperty will be
108 created.
109 */
110QQmlProperty::QQmlProperty(QObject *obj, QQmlContext *ctxt)
111: d(new QQmlPropertyPrivate)
112{
113 if (ctxt) {
114 d->context = QQmlContextData::get(ctxt);
115 d->engine = ctxt->engine();
116 }
117 d->initDefault(obj);
118}
119
120/*!
121 Creates a QQmlProperty for the default property of \a obj
122 using the environment for instantiating QML components that is
123 provided by \a engine. If there is no default property, an
124 invalid QQmlProperty will be created.
125 */
126QQmlProperty::QQmlProperty(QObject *obj, QQmlEngine *engine)
127 : d(new QQmlPropertyPrivate)
128{
129 d->engine = engine;
130 d->initDefault(obj);
131}
132
133/*!
134 Initialize from the default property of \a obj
135*/
136void QQmlPropertyPrivate::initDefault(QObject *obj)
137{
138 if (!obj)
139 return;
140
141 QMetaProperty p = QQmlMetaType::defaultProperty(obj);
142 core.load(p);
143 if (core.isValid())
144 object = obj;
145}
146
147/*!
148 Creates a QQmlProperty for the property \a name of \a obj.
149 */
150QQmlProperty::QQmlProperty(QObject *obj, const QString &name)
151: d(new QQmlPropertyPrivate)
152{
153 d->initProperty(obj, name);
154 if (!isValid()) d->object = nullptr;
155}
156
157/*!
158 Creates a QQmlProperty for the property \a name of \a obj
159 using the \l{QQmlContext} {context} \a ctxt.
160
161 Creating a QQmlProperty without a context will render some
162 properties - like attached properties - inaccessible.
163*/
164QQmlProperty::QQmlProperty(QObject *obj, const QString &name, QQmlContext *ctxt)
165: d(new QQmlPropertyPrivate)
166{
167 if (ctxt) {
168 d->context = QQmlContextData::get(ctxt);
169 d->engine = ctxt->engine();
170 }
171
172 d->initProperty(obj, name);
173 if (!isValid()) {
174 d->object = nullptr;
175 d->context.reset();
176 d->engine = nullptr;
177 }
178}
179
180/*!
181 Creates a QQmlProperty for the property \a name of \a obj
182 using the environment for instantiating QML components that is
183 provided by \a engine.
184 */
185QQmlProperty::QQmlProperty(QObject *obj, const QString &name, QQmlEngine *engine)
186: d(new QQmlPropertyPrivate)
187{
188 d->engine = engine;
189 d->initProperty(obj, name);
190 if (!isValid()) {
191 d->object = nullptr;
192 d->context.reset();
193 d->engine = nullptr;
194 }
195}
196
197QQmlProperty QQmlPropertyPrivate::create(QObject *target, const QString &propertyName,
198 const QQmlRefPointer<QQmlContextData> &context,
199 QQmlPropertyPrivate::InitFlags flags)
200{
201 QQmlProperty result;
202 auto d = new QQmlPropertyPrivate;
203 result.d = d;
204 d->context = context;
205 d->engine = context ? context->engine() : nullptr;
206 d->initProperty(target, propertyName, flags);
207 if (!result.isValid()) {
208 d->object = nullptr;
209 d->context.reset();
210 d->engine = nullptr;
211 }
212 return result;
213}
214
215bool QQmlPropertyPrivate::resolveUrlsOnAssignment()
216{
217 return ::compatResolveUrlsOnAssigment();
218}
219
220QQmlRefPointer<QQmlContextData> QQmlPropertyPrivate::effectiveContext() const
221{
222 if (context)
223 return context;
224 else if (engine)
225 return QQmlContextData::get(engine->rootContext());
226 else
227 return nullptr;
228}
229
230// ### Qt7: Do not accept the "onFoo" syntax for signals anymore, and change the flags accordingly.
231void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name,
232 QQmlPropertyPrivate::InitFlags flags)
233{
234 QQmlRefPointer<QQmlTypeNameCache> typeNameCache = context ? context->imports() : nullptr;
235
236 QObject *currentObject = obj;
237 QList<QStringView> path;
238 QStringView terminal(name);
239
240 if (name.contains(QLatin1Char('.'))) {
241 path = QStringView{name}.split(QLatin1Char('.'));
242 if (path.isEmpty()) return;
243
244 // Everything up to the last property must be an "object type" property
245 for (int ii = 0; ii < path.size() - 1; ++ii) {
246 const QStringView &pathName = path.at(ii);
247
248 // Types must begin with an uppercase letter (see checkRegistration()
249 // in qqmlmetatype.cpp for the enforcement of this).
250 if (typeNameCache && !pathName.isEmpty() && pathName.at(0).isUpper()) {
251 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
252 QQmlTypeLoader *typeLoader = QQmlTypeLoader::get(enginePrivate);
253 QQmlTypeNameCache::Result r = typeNameCache->query(pathName, typeLoader);
254 if (r.isValid()) {
255 if (r.type.isValid()) {
256 QQmlAttachedPropertiesFunc func = r.type.attachedPropertiesFunction(enginePrivate);
257 if (!func) return; // Not an attachable type
258
259 currentObject = qmlAttachedPropertiesObject(currentObject, func);
260 if (!currentObject) return; // Something is broken with the attachable type
261 } else if (r.importNamespace) {
262 if (++ii == path.size())
263 return; // No type following the namespace
264
265 // TODO: Do we really _not_ want to query the namespaced types here?
266 r = typeNameCache->query<QQmlTypeNameCache::QueryNamespaced::No>(
267 path.at(ii), r.importNamespace, typeLoader);
268
269 if (!r.type.isValid())
270 return; // Invalid type in namespace
271
272 QQmlAttachedPropertiesFunc func = r.type.attachedPropertiesFunction(enginePrivate);
273 if (!func)
274 return; // Not an attachable type
275
276 currentObject = qmlAttachedPropertiesObject(currentObject, func);
277 if (!currentObject)
278 return; // Something is broken with the attachable type
279
280 } else if (r.scriptIndex != -1) {
281 return; // Not a type
282 } else {
283 Q_ASSERT(!"Unreachable");
284 }
285 continue;
286 }
287
288 }
289
290 QQmlPropertyData local;
291 const QQmlPropertyData *property = currentObject
292 ? QQmlPropertyCache::property(currentObject, pathName, context, &local)
293 : nullptr;
294
295 if (!property) {
296 // Not a property; Might be an ID
297 // You can't look up an ID on a non-null object, though.
298 if (currentObject || !(flags & InitFlag::AllowId))
299 return;
300
301 for (auto idContext = context; idContext; idContext = idContext->parent()) {
302 const int objectId = idContext->propertyIndex(pathName.toString());
303 if (objectId != -1 && objectId < idContext->numIdValues()) {
304 currentObject = context->idValue(objectId);
305 break;
306 }
307 }
308
309 if (!currentObject)
310 return;
311
312 continue;
313 } else if (property->isFunction()) {
314 return; // Not an object property
315 }
316
317 if (ii == (path.size() - 2) && QQmlMetaType::isValueType(property->propType())) {
318 // We're now at a value type property
319 const QMetaObject *valueTypeMetaObject = QQmlMetaType::metaObjectForValueType(property->propType());
320 if (!valueTypeMetaObject) return; // Not a value type
321
322 int idx = valueTypeMetaObject->indexOfProperty(path.last().toUtf8().constData());
323 if (idx == -1) return; // Value type property does not exist
324
325 QMetaProperty vtProp = valueTypeMetaObject->property(idx);
326
327 Q_ASSERT(idx <= 0x0000FFFF);
328
329 object = currentObject;
330 core = *property;
331 valueTypeData.setFlags(QQmlPropertyData::flagsForProperty(vtProp));
332 valueTypeData.setPropType(vtProp.metaType());
333 valueTypeData.setCoreIndex(idx);
334
335 return;
336 } else {
337 if (!property->isQObject()) {
338 if (auto asPropertyMap = qobject_cast<QQmlPropertyMap*>(currentObject))
339 currentObject = asPropertyMap->value(path.at(ii).toString()).value<QObject*>();
340 else
341 return; // Not an object property, and not a property map
342 } else {
343 property->readProperty(currentObject, &currentObject);
344 }
345
346 if (!currentObject) return; // No value
347
348 }
349
350 }
351
352 terminal = path.last();
353 } else if (!currentObject) {
354 return;
355 }
356
357 auto findSignalInMetaObject = [&](const QByteArray &signalName) {
358 const QMetaMethod method = findSignalByName(currentObject->metaObject(), signalName);
359 if (!method.isValid())
360 return false;
361
362 object = currentObject;
363 core.load(method);
364 return true;
365 };
366
367 QQmlData *ddata = QQmlData::get(currentObject, false);
368 auto findChangeSignal = [&](QStringView signalName) {
369 if (auto propName = QQmlSignalNames::changedSignalNameToPropertyName(signalName)) {
370 const QQmlPropertyData *d =
371 ddata->propertyCache->property(*propName, currentObject, context);
372 while (d && d->isFunction())
373 d = ddata->propertyCache->overrideData(d);
374
375 if (d && d->notifyIndex() != -1) {
376 object = currentObject;
377 core = *ddata->propertyCache->signal(d->notifyIndex());
378 return true;
379 }
380 }
381 return false;
382 };
383
384 const auto findSignal = [&](const QString &signalName) {
385 if (ddata && ddata->propertyCache) {
386 // Try method
387 const QQmlPropertyData *d
388 = ddata->propertyCache->property(signalName, currentObject, context);
389
390 // ### Qt7: This code treats methods as signals. It should use d->isSignal().
391 // That would be a change in behavior, though. Right now you can construct a
392 // QQmlProperty from such a thing.
393 while (d && !d->isFunction())
394 d = ddata->propertyCache->overrideData(d);
395
396 if (d) {
397 object = currentObject;
398 core = *d;
399 return true;
400 }
401
402 return findChangeSignal(signalName);
403 }
404
405 return findSignalInMetaObject(signalName.toUtf8());
406 };
407
408 auto signalName = QQmlSignalNames::handlerNameToSignalName(terminal);
409 if (signalName) {
410 if (findSignal(*signalName))
411 return;
412 } else {
413 signalName = QQmlSignalNames::badHandlerNameToSignalName(terminal);
414 if (signalName) {
415 if (findSignal(*signalName)) {
416 qWarning()
417 << terminal
418 << "is not a properly capitalized signal handler name."
419 << QQmlSignalNames::signalNameToHandlerName(*signalName)
420 << "would be correct.";
421 return;
422 }
423 }
424 }
425
426 if (ddata && ddata->propertyCache) {
427 const QQmlPropertyData *property = ddata->propertyCache->property(
428 terminal, currentObject, context);
429
430 // Technically, we might find an override that is not a function.
431 while (property && !property->isSignal()) {
432 if (!property->isFunction()) {
433 object = currentObject;
434 core = *property;
435 nameCache = terminal.toString();
436 return;
437 }
438 property = ddata->propertyCache->overrideData(property);
439 }
440
441 if (!(flags & InitFlag::AllowSignal))
442 return;
443
444 if (property) {
445 Q_ASSERT(property->isSignal());
446 object = currentObject;
447 core = *property;
448 return;
449 }
450
451 // At last: Try the change signal.
452 findChangeSignal(terminal);
453 } else {
454 // We might still find the property in the metaobject, even without property cache.
455 const QByteArray propertyName = terminal.toUtf8();
456 const QMetaProperty prop = findPropertyByName(currentObject->metaObject(), propertyName);
457
458 if (prop.isValid()) {
459 object = currentObject;
460 core.load(prop);
461 return;
462 }
463
464 if (flags & InitFlag::AllowSignal)
465 findSignalInMetaObject(terminal.toUtf8());
466 }
467}
468
469/*! \internal
470 Returns the index of this property's signal, in the signal index range
471 (see QObjectPrivate::signalIndex()). This is different from
472 QMetaMethod::methodIndex().
473*/
474int QQmlPropertyPrivate::signalIndex() const
475{
476 Q_ASSERT(type() == QQmlProperty::SignalProperty);
477 QMetaMethod m = object->metaObject()->method(core.coreIndex());
478 return QMetaObjectPrivate::signalIndex(m);
479}
480
481/*!
482 Create a copy of \a other.
483*/
484QQmlProperty::QQmlProperty(const QQmlProperty &other)
485{
486 d = other.d;
487 if (d)
488 d->addref();
489}
490
491/*!
492 \enum QQmlProperty::PropertyTypeCategory
493
494 This enum specifies a category of QML property.
495
496 \value InvalidCategory The property is invalid, or is a signal property.
497 \value List The property is a QQmlListProperty list property
498 \value Object The property is a QObject derived type pointer
499 \value Normal The property is a normal value property.
500 */
501
502/*!
503 \enum QQmlProperty::Type
504
505 This enum specifies a type of QML property.
506
507 \value Invalid The property is invalid.
508 \value Property The property is a regular Qt property.
509 \value SignalProperty The property is a signal property.
510*/
511
512/*!
513 Returns the property category.
514*/
515QQmlProperty::PropertyTypeCategory QQmlProperty::propertyTypeCategory() const
516{
517 return d ? d->propertyTypeCategory() : InvalidCategory;
518}
519
520QQmlProperty::PropertyTypeCategory
521QQmlPropertyPrivate::propertyTypeCategory() const
522{
523 uint type = this->type();
524
525 if (isValueType()) {
526 return QQmlProperty::Normal;
527 } else if (type & QQmlProperty::Property) {
528 QMetaType type = propertyType();
529 if (!type.isValid())
530 return QQmlProperty::InvalidCategory;
531 else if (QQmlMetaType::isValueType(type))
532 return QQmlProperty::Normal;
533 else if (core.isQObject())
534 return QQmlProperty::Object;
535 else if (core.isQList())
536 return QQmlProperty::List;
537 else
538 return QQmlProperty::Normal;
539 }
540
541 return QQmlProperty::InvalidCategory;
542}
543
544/*!
545 Returns the type name of the property, or 0 if the property has no type
546 name.
547*/
548const char *QQmlProperty::propertyTypeName() const
549{
550 if (!d)
551 return nullptr;
552 if (d->isValueType()) {
553 const QMetaObject *valueTypeMetaObject = QQmlMetaType::metaObjectForValueType(d->core.propType());
554 Q_ASSERT(valueTypeMetaObject);
555 return valueTypeMetaObject->property(d->valueTypeData.coreIndex()).typeName();
556 } else if (d->object && type() & Property && d->core.isValid()) {
557 return d->object->metaObject()->property(d->core.coreIndex()).typeName();
558 } else {
559 return nullptr;
560 }
561}
562
563/*!
564 Returns true if \a other and this QQmlProperty represent the same
565 property.
566*/
567bool QQmlProperty::operator==(const QQmlProperty &other) const
568{
569 if (!d || !other.d)
570 return false;
571 // category is intentially omitted here as it is generated
572 // from the other members
573 return d->object == other.d->object &&
574 d->core.coreIndex() == other.d->core.coreIndex() &&
575 d->valueTypeData.coreIndex() == other.d->valueTypeData.coreIndex();
576}
577
578/*!
579 Returns the metatype id of the property, or QMetaType::UnknownType if the
580 property has no metatype.
581
582 \sa propertyMetaType
583*/
584int QQmlProperty::propertyType() const
585{
586 return d ? d->propertyType().id() : int(QMetaType::UnknownType);
587}
588
589/*!
590 Returns the metatype of the property.
591
592 \sa propertyType
593 */
594QMetaType QQmlProperty::propertyMetaType() const
595{
596 return d ? d->propertyType() : QMetaType {};
597}
598
599bool QQmlPropertyPrivate::isValueType() const
600{
601 return valueTypeData.isValid();
602}
603
604QMetaType QQmlPropertyPrivate::propertyType() const
605{
606 uint type = this->type();
607 if (isValueType()) {
608 return valueTypeData.propType();
609 } else if (type & QQmlProperty::Property) {
610 return core.propType();
611 } else {
612 return QMetaType();
613 }
614}
615
616QQmlProperty::Type QQmlPropertyPrivate::type() const
617{
618 if (core.isFunction())
619 return QQmlProperty::SignalProperty;
620 else if (core.isValid())
621 return QQmlProperty::Property;
622 else
623 return QQmlProperty::Invalid;
624}
625
626/*!
627 Returns the type of the property.
628*/
629QQmlProperty::Type QQmlProperty::type() const
630{
631 return d ? d->type() : Invalid;
632}
633
634/*!
635 Returns true if this QQmlProperty represents a regular Qt property.
636*/
637bool QQmlProperty::isProperty() const
638{
639 return type() & Property;
640}
641
642/*!
643 Returns true if this QQmlProperty represents a QML signal property.
644*/
645bool QQmlProperty::isSignalProperty() const
646{
647 return type() & SignalProperty;
648}
649
650/*!
651 Returns the QQmlProperty's QObject.
652*/
653QObject *QQmlProperty::object() const
654{
655 return d ? d->object : nullptr;
656}
657
658/*!
659 Assign \a other to this QQmlProperty.
660*/
661QQmlProperty &QQmlProperty::operator=(const QQmlProperty &other)
662{
663 QQmlProperty copied(other);
664 qSwap(d, copied.d);
665 return *this;
666}
667
668/*!
669 Returns true if the property is writable, otherwise false.
670*/
671bool QQmlProperty::isWritable() const
672{
673 if (!d)
674 return false;
675 if (!d->object)
676 return false;
677 if (d->core.isQList()) //list
678 return true;
679 else if (d->core.isFunction()) //signal handler
680 return false;
681 else if (d->core.isValid()) //normal property
682 return d->core.isWritable();
683 else
684 return false;
685}
686
687/*!
688 \internal
689 Returns true if the property is bindable, otherwise false.
690 */
691bool QQmlProperty::isBindable() const
692{
693 if (!d)
694 return false;
695 if (!d->object)
696 return false;
697 if (d->core.isValid())
698 return d->core.isBindable();
699 return false;
700}
701
702/*!
703 Returns true if the property is designable, otherwise false.
704*/
705bool QQmlProperty::isDesignable() const
706{
707 if (!d)
708 return false;
709 if (type() & Property && d->core.isValid() && d->object)
710 return d->object->metaObject()->property(d->core.coreIndex()).isDesignable();
711 else
712 return false;
713}
714
715/*!
716 Returns true if the property is resettable, otherwise false.
717*/
718bool QQmlProperty::isResettable() const
719{
720 if (!d)
721 return false;
722 if (type() & Property && d->core.isValid() && d->object)
723 return d->core.isResettable();
724 else
725 return false;
726}
727
728/*!
729 Returns true if the QQmlProperty refers to a valid property, otherwise
730 false.
731*/
732bool QQmlProperty::isValid() const
733{
734 if (!d)
735 return false;
736 return type() != Invalid;
737}
738
739/*!
740 Return the name of this QML property.
741*/
742QString QQmlProperty::name() const
743{
744 if (!d)
745 return QString();
746 if (d->nameCache.isEmpty()) {
747 if (!d->object) {
748 } else if (d->isValueType()) {
749 const QMetaObject *valueTypeMetaObject = QQmlMetaType::metaObjectForValueType(d->core.propType());
750 Q_ASSERT(valueTypeMetaObject);
751
752 const char *vtName = valueTypeMetaObject->property(d->valueTypeData.coreIndex()).name();
753 d->nameCache = d->core.name(d->object) + QLatin1Char('.') + QString::fromUtf8(vtName);
754 } else if (type() & SignalProperty) {
755 // ### Qt7: Return the original signal name here. Do not prepend "on"
756 d->nameCache = QQmlSignalNames::signalNameToHandlerName(d->core.name(d->object));
757 } else {
758 d->nameCache = d->core.name(d->object);
759 }
760 }
761
762 return d->nameCache;
763}
764
765/*!
766 Returns the \l{QMetaProperty} {Qt property} associated with
767 this QML property.
768 */
769QMetaProperty QQmlProperty::property() const
770{
771 if (!d)
772 return QMetaProperty();
773 if (type() & Property && d->core.isValid() && d->object)
774 return d->object->metaObject()->property(d->core.coreIndex());
775 else
776 return QMetaProperty();
777}
778
779/*!
780 Return the QMetaMethod for this property if it is a SignalProperty,
781 otherwise returns an invalid QMetaMethod.
782*/
783QMetaMethod QQmlProperty::method() const
784{
785 if (!d)
786 return QMetaMethod();
787 if (type() & SignalProperty && d->object)
788 return d->object->metaObject()->method(d->core.coreIndex());
789 else
790 return QMetaMethod();
791}
792
793/*!
794 Returns the binding associated with this property, or 0 if no binding
795 exists.
796*/
797QQmlAbstractBinding *
798QQmlPropertyPrivate::binding(const QQmlProperty &that)
799{
800 if (!that.d || !that.isProperty() || !that.d->object)
801 return nullptr;
802
803 QQmlPropertyIndex thatIndex(that.d->core.coreIndex(), that.d->valueTypeData.coreIndex());
804 return binding(that.d->object, thatIndex);
805}
806
807/*!
808 Set the binding associated with this property to \a newBinding. Returns
809 the existing binding (if any), otherwise 0.
810
811 \a newBinding will be enabled, and the returned binding (if any) will be
812 disabled.
813
814 Ownership of \a newBinding transfers to QML. Ownership of the return value
815 is assumed by the caller.
816*/
817void
818QQmlPropertyPrivate::setBinding(const QQmlProperty &that, QQmlAbstractBinding *newBinding)
819{
820 if (!newBinding) {
821 removeBinding(that);
822 return;
823 }
824
825 if (!that.d || !that.isProperty() || !that.d->object) {
826 if (!newBinding->ref)
827 delete newBinding;
828 return;
829 }
830 setBinding(newBinding);
831}
832
833static void removeOldBinding(QObject *object, QQmlPropertyIndex index, QQmlPropertyPrivate::BindingFlags flags = QQmlPropertyPrivate::None)
834{
835 int coreIndex = index.coreIndex();
836 int valueTypeIndex = index.valueTypeIndex();
837
838 QQmlData *data = QQmlData::get(object, false);
839
840 if (!data || !data->hasBindingBit(coreIndex))
841 return;
842
843 QQmlAbstractBinding::Ptr oldBinding;
844 oldBinding = data->bindings;
845
846 while (oldBinding && (oldBinding->targetPropertyIndex().coreIndex() != coreIndex ||
847 oldBinding->targetPropertyIndex().hasValueTypeIndex())) {
848 oldBinding = oldBinding->nextBinding();
849 }
850
851 if (!oldBinding) {
852 // Clear the binding bit so that the binding doesn't appear later for any reason
853 data->clearBindingBit(coreIndex);
854 return;
855 }
856
857 if (valueTypeIndex != -1 && oldBinding->kind() == QQmlAbstractBinding::ValueTypeProxy) {
858 oldBinding = static_cast<QQmlValueTypeProxyBinding *>(oldBinding.data())->binding(index);
859 if (!oldBinding)
860 return;
861 }
862
863 if (!(flags & QQmlPropertyPrivate::DontEnable))
864 oldBinding->setEnabled(false, {});
865 oldBinding->removeFromObject();
866}
867
868void QQmlPropertyPrivate::removeBinding(QQmlAbstractBinding *b)
869{
870 removeBinding(b->targetObject(), b->targetPropertyIndex());
871}
872
873void QQmlPropertyPrivate::removeBinding(QObject *o, QQmlPropertyIndex index)
874{
875 Q_ASSERT(o);
876
877 auto [target, targetIndex] = findAliasTarget(o, index);
878 removeOldBinding(target, targetIndex);
879}
880
881void QQmlPropertyPrivate::removeBinding(const QQmlProperty &that)
882{
883 if (!that.d || !that.isProperty() || !that.d->object)
884 return;
885
886 removeBinding(that.d->object, that.d->encodedIndex());
887}
888
889QQmlAbstractBinding *
890QQmlPropertyPrivate::binding(QObject *object, QQmlPropertyIndex index)
891{
892 auto aliasTarget = findAliasTarget(object, index);
893 object = aliasTarget.targetObject;
894 index = aliasTarget.targetIndex;
895
896 QQmlData *data = QQmlData::get(object);
897 if (!data)
898 return nullptr;
899
900 const int coreIndex = index.coreIndex();
901 const int valueTypeIndex = index.valueTypeIndex();
902
903 if (coreIndex < 0 || !data->hasBindingBit(coreIndex))
904 return nullptr;
905
906 QQmlAbstractBinding *binding = data->bindings;
907 while (binding && (binding->targetPropertyIndex().coreIndex() != coreIndex ||
908 binding->targetPropertyIndex().hasValueTypeIndex()))
909 binding = binding->nextBinding();
910
911 if (binding && valueTypeIndex != -1) {
912 if (binding->kind() == QQmlAbstractBinding::ValueTypeProxy)
913 binding = static_cast<QQmlValueTypeProxyBinding *>(binding)->binding(index);
914 }
915
916 return binding;
917}
918
919void QQmlPropertyPrivate::findAliasTarget(QObject *object, QQmlPropertyIndex bindingIndex,
920 QObject **targetObject,
921 QQmlPropertyIndex *targetBindingIndex)
922{
923 QQmlData *data = QQmlData::get(object, false);
924 if (data) {
925 int coreIndex = bindingIndex.coreIndex();
926 int valueTypeIndex = bindingIndex.valueTypeIndex();
927
928 const QQmlPropertyData *propertyData =
929 data->propertyCache?data->propertyCache->property(coreIndex):nullptr;
930 if (propertyData && propertyData->isAlias()) {
931 QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex);
932
933 QObject *aObject = nullptr; int aCoreIndex = -1; int aValueTypeIndex = -1;
934 if (vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex)) {
935 // This will either be a value type sub-reference or an alias to a value-type sub-reference not both
936 Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1);
937
938 QQmlPropertyIndex aBindingIndex(aCoreIndex);
939 if (aValueTypeIndex != -1) {
940 aBindingIndex = QQmlPropertyIndex(aCoreIndex, aValueTypeIndex);
941 } else if (valueTypeIndex != -1) {
942 aBindingIndex = QQmlPropertyIndex(aCoreIndex, valueTypeIndex);
943 }
944
945 findAliasTarget(aObject, aBindingIndex, targetObject, targetBindingIndex);
946 return;
947 }
948 }
949 }
950
951 *targetObject = object;
952 *targetBindingIndex = bindingIndex;
953}
954
955QQmlPropertyPrivate::ResolvedAlias QQmlPropertyPrivate::findAliasTarget(QObject *baseObject, QQmlPropertyIndex baseIndex)
956{
957 ResolvedAlias resolved;
958 findAliasTarget(baseObject, baseIndex, &resolved.targetObject, &resolved.targetIndex);
959 return resolved;
960}
961
962
963
964void QQmlPropertyPrivate::setBinding(QQmlAbstractBinding *binding, BindingFlags flags, QQmlPropertyData::WriteFlags writeFlags)
965{
966 Q_ASSERT(binding);
967 Q_ASSERT(binding->targetObject());
968
969 QObject *object = binding->targetObject();
970 const QQmlPropertyIndex index = binding->targetPropertyIndex();
971
972#ifndef QT_NO_DEBUG
973 int coreIndex = index.coreIndex();
974 QQmlData *data = QQmlData::get(object, true);
975 if (data->propertyCache) {
976 const QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex);
977 Q_ASSERT(propertyData);
978 }
979#endif
980
981 removeOldBinding(object, index, flags);
982
983 binding->addToObject();
984 if (!(flags & DontEnable))
985 binding->setEnabled(true, writeFlags);
986
987}
988
989/*!
990 Returns the expression associated with this signal property, or 0 if no
991 signal expression exists.
992*/
993QQmlBoundSignalExpression *
994QQmlPropertyPrivate::signalExpression(const QQmlProperty &that)
995{
996 if (!(that.type() & QQmlProperty::SignalProperty))
997 return nullptr;
998
999 if (!that.d->object)
1000 return nullptr;
1001 QQmlData *data = QQmlData::get(that.d->object);
1002 if (!data)
1003 return nullptr;
1004
1005 QQmlBoundSignal *signalHandler = data->signalHandlers;
1006
1007 while (signalHandler && signalHandler->signalIndex() != QQmlPropertyPrivate::get(that)->signalIndex())
1008 signalHandler = signalHandler->m_nextSignal;
1009
1010 if (signalHandler)
1011 return signalHandler->expression();
1012
1013 return nullptr;
1014}
1015
1016/*!
1017 Set the signal expression associated with this signal property to \a expr.
1018 A reference to \a expr will be added by QML.
1019*/
1020void QQmlPropertyPrivate::setSignalExpression(const QQmlProperty &that, QQmlBoundSignalExpression *expr)
1021{
1022 if (expr)
1023 expr->addref();
1024 QQmlPropertyPrivate::takeSignalExpression(that, expr);
1025}
1026
1027/*!
1028 Set the signal expression associated with this signal property to \a expr.
1029 Ownership of \a expr transfers to QML.
1030*/
1031void QQmlPropertyPrivate::takeSignalExpression(const QQmlProperty &that,
1032 QQmlBoundSignalExpression *expr)
1033{
1034 if (!(that.type() & QQmlProperty::SignalProperty)) {
1035 if (expr)
1036 expr->release();
1037 return;
1038 }
1039
1040 if (!that.d->object)
1041 return;
1042 QQmlData *data = QQmlData::get(that.d->object, nullptr != expr);
1043 if (!data)
1044 return;
1045
1046 QQmlBoundSignal *signalHandler = data->signalHandlers;
1047
1048 while (signalHandler && signalHandler->signalIndex() != QQmlPropertyPrivate::get(that)->signalIndex())
1049 signalHandler = signalHandler->m_nextSignal;
1050
1051 if (signalHandler) {
1052 signalHandler->takeExpression(expr);
1053 return;
1054 }
1055
1056 if (expr) {
1057 int signalIndex = QQmlPropertyPrivate::get(that)->signalIndex();
1058 QQmlBoundSignal *signal = new QQmlBoundSignal(that.d->object, signalIndex, that.d->object,
1059 expr->engine());
1060 signal->takeExpression(expr);
1061 }
1062}
1063
1064/*!
1065 Returns the property value.
1066*/
1067QVariant QQmlProperty::read() const
1068{
1069 if (!d)
1070 return QVariant();
1071 if (!d->object)
1072 return QVariant();
1073
1074 if (type() & SignalProperty) {
1075
1076 return QVariant();
1077
1078 } else if (type() & Property) {
1079
1080 return d->readValueProperty();
1081
1082 }
1083 return QVariant();
1084}
1085
1086/*!
1087Return the \a name property value of \a object. This method is equivalent to:
1088\code
1089 QQmlProperty p(object, name);
1090 p.read();
1091\endcode
1092*/
1093QVariant QQmlProperty::read(const QObject *object, const QString &name)
1094{
1095 QQmlProperty p(const_cast<QObject *>(object), name);
1096 return p.read();
1097}
1098
1099/*!
1100 Return the \a name property value of \a object using the
1101 \l{QQmlContext} {context} \a ctxt. This method is
1102 equivalent to:
1103
1104 \code
1105 QQmlProperty p(object, name, context);
1106 p.read();
1107 \endcode
1108*/
1109QVariant QQmlProperty::read(const QObject *object, const QString &name, QQmlContext *ctxt)
1110{
1111 QQmlProperty p(const_cast<QObject *>(object), name, ctxt);
1112 return p.read();
1113}
1114
1115/*!
1116
1117 Return the \a name property value of \a object using the environment
1118 for instantiating QML components that is provided by \a engine. .
1119 This method is equivalent to:
1120
1121 \code
1122 QQmlProperty p(object, name, engine);
1123 p.read();
1124 \endcode
1125*/
1126QVariant QQmlProperty::read(const QObject *object, const QString &name, QQmlEngine *engine)
1127{
1128 QQmlProperty p(const_cast<QObject *>(object), name, engine);
1129 return p.read();
1130}
1131
1132QVariant QQmlPropertyPrivate::readValueProperty()
1133{
1134 auto doRead = [&](QQmlGadgetPtrWrapper *wrapper) {
1135 wrapper->read(object, core.coreIndex());
1136 return wrapper->readOnGadget(wrapper->property(valueTypeData.coreIndex()));
1137 };
1138
1139 if (isValueType()) {
1140 if (QQmlGadgetPtrWrapper *wrapper = QQmlGadgetPtrWrapper::instance(engine, core.propType()))
1141 return doRead(wrapper);
1142 if (QQmlValueType *valueType = QQmlMetaType::valueType(core.propType())) {
1143 QQmlGadgetPtrWrapper wrapper(valueType, nullptr);
1144 return doRead(&wrapper);
1145 }
1146 return QVariant();
1147 } else if (core.isQList()) {
1148
1149 QQmlListProperty<QObject> prop;
1150 core.readProperty(object, &prop);
1151 return QVariant::fromValue(QQmlListReferencePrivate::init(prop, core.propType()));
1152
1153 } else if (core.isQObject()) {
1154
1155 QObject *rv = nullptr;
1156 core.readProperty(object, &rv);
1157 return QVariant::fromValue(rv);
1158
1159 } else {
1160
1161 if (!core.propType().isValid()) // Unregistered type
1162 return object->metaObject()->property(core.coreIndex()).read(object);
1163
1164 QVariant value;
1165 int status = -1;
1166 void *args[] = { nullptr, &value, &status };
1167 if (core.propType() == QMetaType::fromType<QVariant>()) {
1168 args[0] = &value;
1169 } else {
1170 value = QVariant(core.propType(), (void*)nullptr);
1171 args[0] = value.data();
1172 }
1173 core.readPropertyWithArgs(object, args);
1174 if (core.propType() != QMetaType::fromType<QVariant>() && args[0] != value.data())
1175 return QVariant(QMetaType(core.propType()), args[0]);
1176
1177 return value;
1178 }
1179}
1180
1181// helper function to allow assignment / binding to QList<QUrl> properties.
1182QList<QUrl> QQmlPropertyPrivate::urlSequence(const QVariant &value)
1183{
1184 if (value.metaType() == QMetaType::fromType<QList<QUrl>>())
1185 return value.value<QList<QUrl> >();
1186
1187 QList<QUrl> urls;
1188 if (value.metaType() == QMetaType::fromType<QUrl>()) {
1189 urls.append(value.toUrl());
1190 } else if (value.metaType() == QMetaType::fromType<QString>()) {
1191 urls.append(QUrl(value.toString()));
1192 } else if (value.metaType() == QMetaType::fromType<QByteArray>()) {
1193 urls.append(QUrl(QString::fromUtf8(value.toByteArray())));
1194 } else if (value.metaType() == QMetaType::fromType<QStringList>()) {
1195 QStringList urlStrings = value.value<QStringList>();
1196 const int urlStringsSize = urlStrings.size();
1197 urls.reserve(urlStringsSize);
1198 for (int i = 0; i < urlStringsSize; ++i)
1199 urls.append(QUrl(urlStrings.at(i)));
1200 } // note: QList<QByteArray> is not currently supported.
1201 return urls;
1202}
1203
1204// ### Qt7: Get rid of this
1205QList<QUrl> QQmlPropertyPrivate::urlSequence(
1206 const QVariant &value, const QQmlRefPointer<QQmlContextData> &ctxt)
1207{
1208 QList<QUrl> urls = urlSequence(value);
1209
1210 for (auto urlIt = urls.begin(); urlIt != urls.end(); ++urlIt)
1211 *urlIt = ctxt->resolvedUrl(*urlIt);
1212
1213 return urls;
1214}
1215
1216//writeEnumProperty MIRRORS the relelvant bit of QMetaProperty::write AND MUST BE KEPT IN SYNC!
1217bool QQmlPropertyPrivate::writeEnumProperty(const QMetaProperty &prop, int idx, QObject *object, const QVariant &value, int flags)
1218{
1219 if (!object || !prop.isWritable())
1220 return false;
1221
1222 QVariant v = value;
1223 if (prop.isEnumType() && v.metaType() != prop.metaType()) {
1224 QMetaEnum menum = prop.enumerator();
1225 if (v.userType() == QMetaType::QString) {
1226 bool ok;
1227 if (prop.isFlagType())
1228 v = QVariant(menum.keysToValue(value.toByteArray(), &ok));
1229 else
1230 v = QVariant(menum.keyToValue(value.toByteArray(), &ok));
1231 if (!ok)
1232 return false;
1233 }
1234 if (!v.convert(prop.metaType())) // ### TODO: underlyingType might be faster?
1235 return false;
1236 }
1237
1238 // the status variable is changed by qt_metacall to indicate what it did
1239 // this feature is currently only used by QtDBus and should not be depended
1240 // upon. Don't change it without looking into QDBusAbstractInterface first
1241 // -1 (unchanged): normal qt_metacall, result stored in argv[0]
1242 // changed: result stored directly in value, return the value of status
1243 int status = -1;
1244 void *argv[] = { v.data(), &v, &status, &flags };
1245 QMetaObject::metacall(object, QMetaObject::WriteProperty, idx, argv);
1246 return status;
1247}
1248
1249bool QQmlPropertyPrivate::writeValueProperty(const QVariant &value, QQmlPropertyData::WriteFlags flags)
1250{
1251 return writeValueProperty(object, core, valueTypeData, value, effectiveContext(), flags);
1252}
1253
1255 QObject *object, const QQmlPropertyData &core,
1256 const QQmlPropertyData &valueTypeData, QQmlPropertyData::WriteFlags flags)
1257{
1258 // Remove any existing bindings on this property
1259 if (!(flags & QQmlPropertyData::DontRemoveBinding) && object) {
1260 QQmlPropertyPrivate::removeBinding(
1261 object, QQmlPropertyPrivate::encodedIndex(core, valueTypeData));
1262 }
1263}
1264
1265template<typename Op>
1267 QObject *object, int coreIndex, QQmlGadgetPtrWrapper *wrapper,
1268 QQmlPropertyData::WriteFlags flags, int internalIndex, Op op)
1269{
1270 wrapper->read(object, coreIndex);
1271 const bool rv = op(wrapper);
1272 wrapper->write(object, coreIndex, flags, internalIndex);
1273 return rv;
1274}
1275
1276template<typename Op>
1278 QObject *object, const QQmlPropertyData &core,
1279 const QQmlRefPointer<QQmlContextData> &context, QQmlPropertyData::WriteFlags flags,
1280 int internalIndex, Op op)
1281{
1282 if (QQmlGadgetPtrWrapper *wrapper = context
1283 ? QQmlGadgetPtrWrapper::instance(context->engine(), core.propType())
1284 : nullptr) {
1285 return changePropertyAndWriteBack(
1286 object, core.coreIndex(), wrapper, flags, internalIndex, op);
1287 }
1288
1289 if (QQmlValueType *valueType = QQmlMetaType::valueType(core.propType())) {
1290 QQmlGadgetPtrWrapper wrapper(valueType, nullptr);
1291 return changePropertyAndWriteBack(
1292 object, core.coreIndex(), &wrapper, flags, internalIndex, op);
1293 }
1294
1295 return false;
1296}
1297
1298bool QQmlPropertyPrivate::writeValueProperty(
1299 QObject *object, const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData,
1300 const QVariant &value, const QQmlRefPointer<QQmlContextData> &context,
1301 QQmlPropertyData::WriteFlags flags)
1302{
1303 removeValuePropertyBinding(object, core, valueTypeData, flags);
1304
1305 if (!valueTypeData.isValid())
1306 return write(object, core, value, context, flags);
1307
1308 return changeThroughGadgetPtrWrapper(
1309 object, core, context, flags | QQmlPropertyData::HasInternalIndex,
1310 valueTypeData.coreIndex(), [&](QQmlGadgetPtrWrapper *wrapper) {
1311 return write(wrapper, valueTypeData, value, context, flags);
1312 });
1313}
1314
1315bool QQmlPropertyPrivate::resetValueProperty(
1316 QObject *object, const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData,
1317 const QQmlRefPointer<QQmlContextData> &context, QQmlPropertyData::WriteFlags flags)
1318{
1319 removeValuePropertyBinding(object, core, valueTypeData, flags);
1320
1321 if (!valueTypeData.isValid())
1322 return reset(object, core, flags);
1323
1324 return changeThroughGadgetPtrWrapper(
1325 object, core, context, flags | QQmlPropertyData::HasInternalIndex,
1326 valueTypeData.coreIndex(), [&](QQmlGadgetPtrWrapper *wrapper) {
1327 return reset(wrapper, valueTypeData, flags);
1328 });
1329}
1330
1331// We need to prevent new-style bindings from being removed.
1333{
1335
1336 BindingFixer(QObject *object, const QQmlPropertyData &property,
1337 QQmlPropertyData::WriteFlags flags)
1338 {
1339 if (!property.isBindable() || !(flags & QQmlPropertyData::DontRemoveBinding))
1340 return;
1341
1342 QUntypedBindable bindable;
1343 void *argv[] = {&bindable};
1344 QMetaObject::metacall(object, QMetaObject::BindableProperty, property.coreIndex(), argv);
1345 untypedBinding = bindable.binding();
1346 if (auto priv = QPropertyBindingPrivate::get(untypedBinding))
1347 priv->setSticky(true);
1348 }
1349
1351 {
1352 if (untypedBinding.isNull())
1353 return;
1354 auto priv = QPropertyBindingPrivate::get(untypedBinding);
1355 priv->setSticky(false);
1356 }
1357
1358private:
1359 QUntypedPropertyBinding untypedBinding;
1360};
1361
1363 bool couldConvert = false;
1364 bool couldWrite = false;
1365
1366 operator bool() const { return couldConvert; }
1367};
1368
1370 QObject *object, const QQmlPropertyData &property, const QVariant &value,
1371 QQmlPropertyData::WriteFlags flags, QMetaType propertyMetaType, QMetaType variantMetaType,
1372 bool isUrl, QQmlEnginePrivate *enginePriv) {
1373
1374 if (isUrl
1375 || variantMetaType == QMetaType::fromType<QString>()
1376 || propertyMetaType == QMetaType::fromType<QList<QUrl>>()
1377 || property.isQList()) {
1378 return {false, false};
1379 }
1380
1381 // common cases:
1382 switch (propertyMetaType.id()) {
1383 case QMetaType::Bool:
1384 if (value.canConvert(propertyMetaType)) {
1385 bool b = value.toBool();
1386 return {true, property.writeProperty(object, &b, flags)};
1387 }
1388 return {false, false};
1389 case QMetaType::Int: {
1390 bool ok = false;
1391 int i = value.toInt(&ok);
1392 return {ok, ok && property.writeProperty(object, &i, flags)};
1393 }
1394 case QMetaType::UInt: {
1395 bool ok = false;
1396 uint u = value.toUInt(&ok);
1397 return {ok, ok && property.writeProperty(object, &u, flags)};
1398 }
1399 case QMetaType::Double: {
1400 bool ok = false;
1401 double d = value.toDouble(&ok);
1402 return {ok, ok && property.writeProperty(object, &d, flags)};
1403 }
1404 case QMetaType::Float: {
1405 bool ok = false;
1406 float f = value.toFloat(&ok);
1407 return {ok, ok && property.writeProperty(object, &f, flags)};
1408 }
1409 case QMetaType::QString:
1410 if (value.canConvert(propertyMetaType)) {
1411 QString s = value.toString();
1412 return {true, property.writeProperty(object, &s, flags)};
1413 }
1414 return {false, false};
1415 case QMetaType::QVariantMap:
1416 if (value.canConvert(propertyMetaType)) {
1417 QVariantMap m = value.toMap();
1418 return {true, property.writeProperty(object, &m, flags)};
1419 }
1420 return {false, false};
1421 default: {
1422 break;
1423 }
1424 }
1425
1426 QVariant converted = QQmlValueTypeProvider::createValueType(
1427 value, propertyMetaType, enginePriv ? enginePriv->v4engine() : nullptr);
1428 if (!converted.isValid()) {
1429 converted = QVariant(propertyMetaType);
1430 if (!QMetaType::convert(value.metaType(), value.constData(),
1431 propertyMetaType, converted.data())) {
1432 return {false, false};
1433 }
1434 }
1435 return {true, property.writeProperty(object, converted.data(), flags)};
1436};
1437
1438template<typename Op>
1439bool iterateQObjectContainer(QMetaType metaType, const void *data, Op op)
1440{
1441 QSequentialIterable iterable;
1442 if (!QMetaType::convert(metaType, data, QMetaType::fromType<QSequentialIterable>(), &iterable))
1443 return false;
1444
1445 const QMetaSequence metaSequence = iterable.metaContainer();
1446
1447 if (!metaSequence.hasConstIterator()
1448 || !metaSequence.canGetValueAtConstIterator()
1449 || !iterable.valueMetaType().flags().testFlag(QMetaType::PointerToQObject)) {
1450 return false;
1451 }
1452
1453 const void *container = iterable.constIterable();
1454 void *it = metaSequence.constBegin(container);
1455 const void *end = metaSequence.constEnd(container);
1456 QObject *o = nullptr;
1457 while (!metaSequence.compareConstIterator(it, end)) {
1458 metaSequence.valueAtConstIterator(it, &o);
1459 op(o);
1460 metaSequence.advanceConstIterator(it, 1);
1461 }
1462 metaSequence.destroyConstIterator(it);
1463 metaSequence.destroyConstIterator(end);
1464 return true;
1465}
1466
1468 QObject *object, const QQmlPropertyData &property, const QVariant &value,
1469 QMetaType variantMetaType) {
1470 if (variantMetaType != QMetaType::fromType<QJSValue>())
1471 return false;
1472
1473 const QJSValue &jsValue = get<QJSValue>(value);
1474 const QV4::FunctionObject *f = QJSValuePrivate::asManagedType<QV4::FunctionObject>(&jsValue);
1475 if (!f || !f->isBinding())
1476 return false;
1477
1478 QV4::QObjectWrapper::setProperty(f->engine(), object, &property, f->asReturnedValue());
1479 return true;
1480}
1481
1482bool QQmlPropertyPrivate::write(
1483 QObject *object, const QQmlPropertyData &property, const QVariant &value,
1484 const QQmlRefPointer<QQmlContextData> &context, QQmlPropertyData::WriteFlags flags)
1485{
1486 const QMetaType propertyMetaType = property.propType();
1487 const QMetaType variantMetaType = value.metaType();
1488
1489 const BindingFixer bindingFixer(object, property, flags);
1490
1491 if (property.isEnum()) {
1492 QMetaProperty prop = object->metaObject()->property(property.coreIndex());
1493 QVariant v = value;
1494 // Enum values come through the script engine as doubles
1495 if (variantMetaType == QMetaType::fromType<double>()) {
1496 double integral;
1497 double fractional = std::modf(value.toDouble(), &integral);
1498 if (qFuzzyIsNull(fractional))
1499 v.convert(QMetaType::fromType<qint32>());
1500 }
1501 return writeEnumProperty(prop, property.coreIndex(), object, v, flags);
1502 }
1503
1504 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(context);
1505 const bool isUrl = propertyMetaType == QMetaType::fromType<QUrl>(); // handled separately
1506
1507 // Handle Qt.binding bindings here to avoid mistaken conversion below
1508 if (tryAssignBinding(object, property, value, variantMetaType))
1509 return true;
1510
1511 // The cases below are in approximate order of likelyhood:
1512 if (propertyMetaType == variantMetaType && !isUrl
1513 && propertyMetaType != QMetaType::fromType<QList<QUrl>>() && !property.isQList()) {
1514 return property.writeProperty(object, const_cast<void *>(value.constData()), flags);
1515 } else if (property.isQObject()) {
1516 QVariant val = value;
1517 QMetaType varType;
1518 if (variantMetaType == QMetaType::fromType<std::nullptr_t>()) {
1519 // This reflects the fact that you can assign a nullptr to a QObject pointer
1520 // Without the change to QObjectStar, rawMetaObjectForType would not give us a QQmlMetaObject
1521 varType = QMetaType::fromType<QObject*>();
1522 val = QVariant(varType, nullptr);
1523 } else {
1524 varType = variantMetaType;
1525 }
1526 QQmlMetaObject valMo = rawMetaObjectForType(varType);
1527 if (valMo.isNull() || !varType.flags().testFlag(QMetaType::PointerToQObject))
1528 return false;
1529 QObject *o = *static_cast<QObject *const *>(val.constData());
1530 QQmlMetaObject propMo = rawMetaObjectForType(propertyMetaType);
1531
1532 if (o)
1533 valMo = o;
1534
1535 if (QQmlMetaObject::canConvert(valMo, propMo)) {
1536 return property.writeProperty(object, &o, flags);
1537 } else if (!o && QQmlMetaObject::canConvert(propMo, valMo)) {
1538 // In the case of a null QObject, we assign the null if there is
1539 // any change that the null variant type could be up or down cast to
1540 // the property type.
1541 return property.writeProperty(object, &o, flags);
1542 } else {
1543 return false;
1544 }
1545 } else if (ConvertAndAssignResult result = tryConvertAndAssign(
1546 object, property, value, flags, propertyMetaType, variantMetaType, isUrl,
1547 enginePriv)) {
1548 return result.couldWrite;
1549 } else if (propertyMetaType == QMetaType::fromType<QVariant>()) {
1550 return property.writeProperty(object, const_cast<QVariant *>(&value), flags);
1551 } else if (isUrl) {
1552 QUrl u;
1553 if (variantMetaType == QMetaType::fromType<QUrl>()) {
1554 u = value.toUrl();
1555 if (compatResolveUrlsOnAssigment() && context && u.isRelative() && !u.isEmpty())
1556 u = context->resolvedUrl(u);
1557 }
1558 else if (variantMetaType == QMetaType::fromType<QByteArray>())
1559 u = QUrl(QString::fromUtf8(value.toByteArray()));
1560 else if (variantMetaType == QMetaType::fromType<QString>())
1561 u = QUrl(value.toString());
1562 else
1563 return false;
1564
1565 return property.writeProperty(object, &u, flags);
1566 } else if (propertyMetaType == QMetaType::fromType<QList<QUrl>>()) {
1567 QList<QUrl> urlSeq = compatResolveUrlsOnAssigment()
1568 ? urlSequence(value, context)
1569 : urlSequence(value);
1570 return property.writeProperty(object, &urlSeq, flags);
1571 } else if (property.isQList()) {
1572 if (propertyMetaType.flags() & QMetaType::IsQmlList) {
1573 QMetaType listValueType = QQmlMetaType::listValueType(propertyMetaType);
1574 QQmlMetaObject valueMetaObject = QQmlMetaType::rawMetaObjectForType(listValueType);
1575 if (valueMetaObject.isNull())
1576 return false;
1577
1578 QQmlListProperty<QObject> prop;
1579 property.readProperty(object, &prop);
1580
1581 if (!prop.clear || !prop.append)
1582 return false;
1583
1584 const bool useNonsignalingListOps = prop.clear == &QQmlVMEMetaObject::list_clear
1585 && prop.append == &QQmlVMEMetaObject::list_append;
1586
1587 auto propClear =
1588 useNonsignalingListOps ? &QQmlVMEMetaObject::list_clear_nosignal : prop.clear;
1589 auto propAppend =
1590 useNonsignalingListOps ? &QQmlVMEMetaObject::list_append_nosignal : prop.append;
1591
1592 propClear(&prop);
1593
1594 const auto doAppend = [&](QObject *o) {
1595 if (Q_UNLIKELY(o && !QQmlMetaObject::canConvert(o, valueMetaObject))) {
1596 qCWarning(lcIncompatibleElement)
1597 << "Cannot append" << o << "to a QML list of" << listValueType.name();
1598 o = nullptr;
1599 }
1600 propAppend(&prop, o);
1601 };
1602
1603 if (variantMetaType == QMetaType::fromType<QQmlListReference>()) {
1604 QQmlListReference qdlr = value.value<QQmlListReference>();
1605 for (qsizetype ii = 0; ii < qdlr.count(); ++ii)
1606 doAppend(qdlr.at(ii));
1607 } else if (variantMetaType == QMetaType::fromType<QList<QObject *>>()) {
1608 const QList<QObject *> &list = qvariant_cast<QList<QObject *> >(value);
1609 for (qsizetype ii = 0; ii < list.size(); ++ii)
1610 doAppend(list.at(ii));
1611 } else if (variantMetaType == QMetaType::fromType<QList<QVariant>>()) {
1612 const QList<QVariant> &list
1613 = *static_cast<const QList<QVariant> *>(value.constData());
1614 for (const QVariant &entry : list)
1615 doAppend(QQmlMetaType::toQObject(entry));
1616 } else if (!iterateQObjectContainer(variantMetaType, value.data(), doAppend)) {
1617 doAppend(QQmlMetaType::toQObject(value));
1618 }
1619 if (useNonsignalingListOps) {
1620 Q_ASSERT(QQmlVMEMetaObject::get(object));
1621 QQmlVMEResolvedList(&prop).activateSignal();
1622 }
1623 } else if (variantMetaType == propertyMetaType) {
1624 QVariant v = value;
1625 property.writeProperty(object, v.data(), flags);
1626 } else {
1627 QVariant list(propertyMetaType);
1628 const QQmlType type = QQmlMetaType::qmlType(propertyMetaType);
1629 const QMetaSequence sequence = type.listMetaSequence();
1630 if (sequence.canAddValue())
1631 sequence.addValue(list.data(), value.data());
1632 property.writeProperty(object, list.data(), flags);
1633 }
1634 } else if (enginePriv && propertyMetaType == QMetaType::fromType<QJSValue>()) {
1635 // We can convert everything into a QJSValue if we have an engine.
1636 QJSValue jsValue = QJSValuePrivate::fromReturnedValue(
1637 enginePriv->v4engine()->metaTypeToJS(variantMetaType, value.constData()));
1638 return property.writeProperty(object, &jsValue, flags);
1639 } else {
1640 Q_ASSERT(variantMetaType != propertyMetaType);
1641
1642 bool ok = false;
1643 QVariant v;
1644 if (variantMetaType == QMetaType::fromType<QString>())
1645 v = QQmlStringConverters::variantFromString(value.toString(), propertyMetaType, &ok);
1646
1647 if (!ok) {
1648 v = value;
1649 if (v.convert(propertyMetaType)) {
1650 ok = true;
1651 }
1652 }
1653 if (!ok) {
1654 // the only other options are that they are assigning a single value
1655 // or a QVariantList to a sequence type property (eg, an int to a
1656 // QList<int> property) or that we encountered an interface type.
1657 // Note that we've already handled single-value assignment to QList<QUrl> properties.
1658 QSequentialIterable iterable;
1659 v = QVariant(propertyMetaType);
1660 if (QMetaType::view(
1661 propertyMetaType, v.data(),
1662 QMetaType::fromType<QSequentialIterable>(),
1663 &iterable)) {
1664 const QMetaSequence propertyMetaSequence = iterable.metaContainer();
1665 if (propertyMetaSequence.canAddValueAtEnd()) {
1666 const QMetaType elementMetaType = iterable.valueMetaType();
1667 void *propertyContainer = iterable.mutableIterable();
1668
1669 if (variantMetaType == elementMetaType) {
1670 propertyMetaSequence.addValueAtEnd(propertyContainer, value.constData());
1671 ok = true;
1672 } else if (variantMetaType == QMetaType::fromType<QVariantList>()) {
1673 const QVariantList list = value.value<QVariantList>();
1674 for (const QVariant &valueElement : list) {
1675 if (valueElement.metaType() == elementMetaType) {
1676 propertyMetaSequence.addValueAtEnd(
1677 propertyContainer, valueElement.constData());
1678 } else {
1679 QVariant converted(elementMetaType);
1680 QMetaType::convert(
1681 valueElement.metaType(), valueElement.constData(),
1682 elementMetaType, converted.data());
1683 propertyMetaSequence.addValueAtEnd(
1684 propertyContainer, converted.constData());
1685 }
1686 }
1687 ok = true;
1688 } else if (elementMetaType.flags().testFlag(QMetaType::PointerToQObject)) {
1689 const QMetaObject *elementMetaObject = elementMetaType.metaObject();
1690 Q_ASSERT(elementMetaObject);
1691
1692 const auto doAppend = [&](QObject *o) {
1693 QObject *casted = elementMetaObject->cast(o);
1694 propertyMetaSequence.addValueAtEnd(propertyContainer, &casted);
1695 };
1696
1697 if (variantMetaType.flags().testFlag(QMetaType::PointerToQObject)) {
1698 doAppend(*static_cast<QObject *const *>(value.data()));
1699 ok = true;
1700 } else if (variantMetaType == QMetaType::fromType<QQmlListReference>()) {
1701 const QQmlListReference *reference
1702 = static_cast<const QQmlListReference *>(value.constData());
1703 Q_ASSERT(elementMetaObject);
1704 for (int i = 0, end = reference->size(); i < end; ++i)
1705 doAppend(reference->at(i));
1706 ok = true;
1707 } else if (!iterateQObjectContainer(
1708 variantMetaType, value.data(), doAppend)) {
1709 doAppend(QQmlMetaType::toQObject(value));
1710 }
1711 } else {
1712 QVariant converted = value;
1713 if (converted.convert(elementMetaType)) {
1714 propertyMetaSequence.addValueAtEnd(propertyContainer, converted.constData());
1715 ok = true;
1716 }
1717 }
1718 }
1719 }
1720 }
1721
1722 if (!ok && QQmlMetaType::isInterface(propertyMetaType)) {
1723 auto valueAsQObject = qvariant_cast<QObject *>(value);
1724
1725 if (void *iface = valueAsQObject
1726 ? valueAsQObject->qt_metacast(QQmlMetaType::interfaceIId(propertyMetaType))
1727 : nullptr;
1728 iface) {
1729 // this case can occur when object has an interface type
1730 // and the variant contains a type implementing the interface
1731 return property.writeProperty(object, &iface, flags);
1732 }
1733 }
1734
1735 if (ok) {
1736 return property.writeProperty(object, const_cast<void *>(v.constData()), flags);
1737 } else {
1738 return false;
1739 }
1740 }
1741
1742 return true;
1743}
1744
1745bool QQmlPropertyPrivate::reset(
1746 QObject *object, const QQmlPropertyData &property,
1747 QQmlPropertyData::WriteFlags flags)
1748{
1749 const BindingFixer bindingFixer(object, property, flags);
1750 property.resetProperty(object, flags);
1751 return true;
1752}
1753
1754QQmlMetaObject QQmlPropertyPrivate::rawMetaObjectForType(QMetaType metaType)
1755{
1756 if (metaType.flags() & QMetaType::PointerToQObject) {
1757 if (const QMetaObject *metaObject = metaType.metaObject())
1758 return metaObject;
1759 }
1760 return QQmlMetaType::rawMetaObjectForType(metaType);
1761}
1762
1763/*!
1764 Sets the property value to \a value. Returns \c true on success, or
1765 \c false if the property can't be set because the \a value is the
1766 wrong type, for example.
1767 */
1768bool QQmlProperty::write(const QVariant &value) const
1769{
1770 return QQmlPropertyPrivate::write(*this, value, {});
1771}
1772
1773/*!
1774 Writes \a value to the \a name property of \a object. This method
1775 is equivalent to:
1776
1777 \code
1778 QQmlProperty p(object, name);
1779 p.write(value);
1780 \endcode
1781
1782 Returns \c true on success, \c false otherwise.
1783*/
1784bool QQmlProperty::write(QObject *object, const QString &name, const QVariant &value)
1785{
1786 QQmlProperty p(object, name);
1787 return p.write(value);
1788}
1789
1790/*!
1791 Writes \a value to the \a name property of \a object using the
1792 \l{QQmlContext} {context} \a ctxt. This method is
1793 equivalent to:
1794
1795 \code
1796 QQmlProperty p(object, name, ctxt);
1797 p.write(value);
1798 \endcode
1799
1800 Returns \c true on success, \c false otherwise.
1801*/
1802bool QQmlProperty::write(QObject *object,
1803 const QString &name,
1804 const QVariant &value,
1805 QQmlContext *ctxt)
1806{
1807 QQmlProperty p(object, name, ctxt);
1808 return p.write(value);
1809}
1810
1811/*!
1812
1813 Writes \a value to the \a name property of \a object using the
1814 environment for instantiating QML components that is provided by
1815 \a engine. This method is equivalent to:
1816
1817 \code
1818 QQmlProperty p(object, name, engine);
1819 p.write(value);
1820 \endcode
1821
1822 Returns \c true on success, \c false otherwise.
1823*/
1824bool QQmlProperty::write(QObject *object, const QString &name, const QVariant &value,
1825 QQmlEngine *engine)
1826{
1827 QQmlProperty p(object, name, engine);
1828 return p.write(value);
1829}
1830
1831/*!
1832 Resets the property and returns true if the property is
1833 resettable. If the property is not resettable, nothing happens
1834 and false is returned.
1835*/
1836bool QQmlProperty::reset() const
1837{
1838 if (isResettable()) {
1839 void *args[] = { nullptr };
1840 QMetaObject::metacall(d->object, QMetaObject::ResetProperty, d->core.coreIndex(), args);
1841 return true;
1842 } else {
1843 return false;
1844 }
1845}
1846
1847bool QQmlPropertyPrivate::write(const QQmlProperty &that,
1848 const QVariant &value, QQmlPropertyData::WriteFlags flags)
1849{
1850 if (!that.d)
1851 return false;
1852 if (that.d->object && that.type() & QQmlProperty::Property &&
1853 that.d->core.isValid() && that.isWritable())
1854 return that.d->writeValueProperty(value, flags);
1855 else
1856 return false;
1857}
1858
1859/*!
1860 Returns true if the property has a change notifier signal, otherwise false.
1861*/
1862bool QQmlProperty::hasNotifySignal() const
1863{
1864 if (type() & Property && d->object) {
1865 return d->object->metaObject()->property(d->core.coreIndex()).hasNotifySignal();
1866 }
1867 return false;
1868}
1869
1870/*!
1871 Returns true if the property needs a change notifier signal for bindings
1872 to remain upto date, false otherwise.
1873
1874 Some properties, such as attached properties or those whose value never
1875 changes, do not require a change notifier.
1876*/
1877bool QQmlProperty::needsNotifySignal() const
1878{
1879 return type() & Property && !property().isConstant();
1880}
1881
1882/*!
1883 Connects the property's change notifier signal to the
1884 specified \a method of the \a dest object and returns
1885 true. Returns false if this metaproperty does not
1886 represent a regular Qt property or if it has no
1887 change notifier signal, or if the \a dest object does
1888 not have the specified \a method.
1889*/
1890bool QQmlProperty::connectNotifySignal(QObject *dest, int method) const
1891{
1892 if (!(type() & Property) || !d->object)
1893 return false;
1894
1895 QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex());
1896 if (prop.hasNotifySignal()) {
1897 return QQmlPropertyPrivate::connect(d->object, prop.notifySignalIndex(), dest, method, Qt::DirectConnection);
1898 } else {
1899 return false;
1900 }
1901}
1902
1903/*!
1904 Connects the property's change notifier signal to the
1905 specified \a slot of the \a dest object and returns
1906 true. Returns false if this metaproperty does not
1907 represent a regular Qt property or if it has no
1908 change notifier signal, or if the \a dest object does
1909 not have the specified \a slot.
1910
1911 \note \a slot should be passed using the SLOT() macro so it is
1912 correctly identified.
1913*/
1914bool QQmlProperty::connectNotifySignal(QObject *dest, const char *slot) const
1915{
1916 if (!(type() & Property) || !d->object)
1917 return false;
1918
1919 QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex());
1920 if (prop.hasNotifySignal()) {
1921 QByteArray signal('2' + prop.notifySignal().methodSignature());
1922 return QObject::connect(d->object, signal.constData(), dest, slot);
1923 } else {
1924 return false;
1925 }
1926}
1927
1928/*!
1929 Return the Qt metaobject index of the property.
1930*/
1931int QQmlProperty::index() const
1932{
1933 return d ? d->core.coreIndex() : -1;
1934}
1935
1936QQmlPropertyIndex QQmlPropertyPrivate::propertyIndex(const QQmlProperty &that)
1937{
1938 return that.d ? that.d->encodedIndex() : QQmlPropertyIndex();
1939}
1940
1941QQmlProperty
1942QQmlPropertyPrivate::restore(QObject *object, const QQmlPropertyData &data,
1943 const QQmlPropertyData *valueTypeData,
1944 const QQmlRefPointer<QQmlContextData> &ctxt)
1945{
1946 QQmlProperty prop;
1947
1948 prop.d = new QQmlPropertyPrivate;
1949 prop.d->object = object;
1950 prop.d->context = ctxt;
1951 prop.d->engine = ctxt ? ctxt->engine() : nullptr;
1952
1953 prop.d->core = data;
1954 if (valueTypeData)
1955 prop.d->valueTypeData = *valueTypeData;
1956
1957 return prop;
1958}
1959
1960/*!
1961 Return the signal corresponding to \a name
1962*/
1963QMetaMethod QQmlPropertyPrivate::findSignalByName(const QMetaObject *mo, const QByteArray &name)
1964{
1965 Q_ASSERT(mo);
1966 int methods = mo->methodCount();
1967 for (int ii = methods - 1; ii >= 2; --ii) { // >= 2 to block the destroyed signal
1968 QMetaMethod method = mo->method(ii);
1969
1970 if (method.name() == name && (method.methodType() & QMetaMethod::Signal))
1971 return method;
1972 }
1973
1974 // If no signal is found, but the signal is of the form "onBlahChanged",
1975 // return the notify signal for the property "Blah"
1976 if (auto propName = QQmlSignalNames::changedSignalNameToPropertyName(name)) {
1977 int propIdx = mo->indexOfProperty(propName->constData());
1978 if (propIdx >= 0) {
1979 QMetaProperty prop = mo->property(propIdx);
1980 if (prop.hasNotifySignal())
1981 return prop.notifySignal();
1982 }
1983 }
1984
1985 return QMetaMethod();
1986}
1987
1988/*!
1989 Return the property corresponding to \a name
1990*/
1991QMetaProperty QQmlPropertyPrivate::findPropertyByName(const QMetaObject *mo, const QByteArray &name)
1992{
1993 Q_ASSERT(mo);
1994 const int i = mo->indexOfProperty(name);
1995 return i < 0 ? QMetaProperty() : mo->property(i);
1996}
1997
1998/*! \internal
1999 If \a indexInSignalRange is true, \a index is treated as a signal index
2000 (see QObjectPrivate::signalIndex()), otherwise it is treated as a
2001 method index (QMetaMethod::methodIndex()).
2002*/
2003static inline void flush_vme_signal(const QObject *object, int index, bool indexInSignalRange)
2004{
2005 QQmlData *data = QQmlData::get(object);
2006 if (data && data->propertyCache) {
2007 const QQmlPropertyData *property = indexInSignalRange ? data->propertyCache->signal(index)
2008 : data->propertyCache->method(index);
2009
2010 if (property && property->isVMESignal()) {
2011 QQmlVMEMetaObject *vme;
2012 if (indexInSignalRange)
2013 vme = QQmlVMEMetaObject::getForSignal(const_cast<QObject *>(object), index);
2014 else
2015 vme = QQmlVMEMetaObject::getForMethod(const_cast<QObject *>(object), index);
2016 vme->connectAliasSignal(index, indexInSignalRange);
2017 }
2018 }
2019}
2020
2021/*!
2022Connect \a sender \a signal_index to \a receiver \a method_index with the specified
2023\a type and \a types. This behaves identically to QMetaObject::connect() except that
2024it connects any lazy "proxy" signal connections set up by QML.
2025
2026It is possible that this logic should be moved to QMetaObject::connect().
2027*/
2028bool QQmlPropertyPrivate::connect(const QObject *sender, int signal_index,
2029 const QObject *receiver, int method_index,
2030 int type, int *types)
2031{
2032 static const bool indexInSignalRange = false;
2033 flush_vme_signal(sender, signal_index, indexInSignalRange);
2034 flush_vme_signal(receiver, method_index, indexInSignalRange);
2035
2036 return QMetaObject::connect(sender, signal_index, receiver, method_index, type, types);
2037}
2038
2039/*! \internal
2040 \a signal_index MUST be in the signal index range (see QObjectPrivate::signalIndex()).
2041 This is different from QMetaMethod::methodIndex().
2042*/
2043void QQmlPropertyPrivate::flushSignal(const QObject *sender, int signal_index)
2044{
2045 static const bool indexInSignalRange = true;
2046 flush_vme_signal(sender, signal_index, indexInSignalRange);
2047}
2048
2049QT_END_NAMESPACE
2050
2051#include "moc_qqmlproperty.cpp"
bool changeThroughGadgetPtrWrapper(QObject *object, const QQmlPropertyData &core, const QQmlRefPointer< QQmlContextData > &context, QQmlPropertyData::WriteFlags flags, int internalIndex, Op op)
static ConvertAndAssignResult tryConvertAndAssign(QObject *object, const QQmlPropertyData &property, const QVariant &value, QQmlPropertyData::WriteFlags flags, QMetaType propertyMetaType, QMetaType variantMetaType, bool isUrl, QQmlEnginePrivate *enginePriv)
static void removeOldBinding(QObject *object, QQmlPropertyIndex index, QQmlPropertyPrivate::BindingFlags flags=QQmlPropertyPrivate::None)
static void flush_vme_signal(const QObject *object, int index, bool indexInSignalRange)
static bool tryAssignBinding(QObject *object, const QQmlPropertyData &property, const QVariant &value, QMetaType variantMetaType)
static void removeValuePropertyBinding(QObject *object, const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData, QQmlPropertyData::WriteFlags flags)
bool changePropertyAndWriteBack(QObject *object, int coreIndex, QQmlGadgetPtrWrapper *wrapper, QQmlPropertyData::WriteFlags flags, int internalIndex, Op op)
bool iterateQObjectContainer(QMetaType metaType, const void *data, Op op)
DEFINE_BOOL_CONFIG_OPTION(forceDiskCache, QML_FORCE_DISK_CACHE)
BindingFixer(QObject *object, const QQmlPropertyData &property, QQmlPropertyData::WriteFlags flags)