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