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