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