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
966 QObject *aObject = nullptr; int aCoreIndex = -1; int aValueTypeIndex = -1;
967 if (QQmlVMEMetaObject::aliasTarget(object, coreIndex, &aObject, &aCoreIndex,
968 &aValueTypeIndex)) {
969 // This will either be a value type sub-reference
970 // or an alias to a value-type sub-reference not both
971 Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1);
972
973 QQmlPropertyIndex aBindingIndex(aCoreIndex);
974 if (aValueTypeIndex != -1) {
975 aBindingIndex = QQmlPropertyIndex(aCoreIndex, aValueTypeIndex);
976 } else if (valueTypeIndex != -1) {
977 aBindingIndex = QQmlPropertyIndex(aCoreIndex, valueTypeIndex);
978 }
979
980 findAliasTarget(aObject, aBindingIndex, targetObject, targetBindingIndex);
981 return;
982 }
983 }
984 }
985
986 *targetObject = object;
987 *targetBindingIndex = bindingIndex;
988}
989
990QQmlPropertyPrivate::ResolvedAlias QQmlPropertyPrivate::findAliasTarget(QObject *baseObject, QQmlPropertyIndex baseIndex)
991{
992 ResolvedAlias resolved;
993 findAliasTarget(baseObject, baseIndex, &resolved.targetObject, &resolved.targetIndex);
994 return resolved;
995}
996
997
998
999void QQmlPropertyPrivate::setBinding(QQmlAbstractBinding *binding, BindingFlags flags, QQmlPropertyData::WriteFlags writeFlags)
1000{
1001 Q_ASSERT(binding);
1002 Q_ASSERT(binding->targetObject());
1003
1004 QObject *object = binding->targetObject();
1005 const QQmlPropertyIndex index = binding->targetPropertyIndex();
1006
1007#ifndef QT_NO_DEBUG
1008 int coreIndex = index.coreIndex();
1009 QQmlData *data = QQmlData::get(object, true);
1010 if (data->propertyCache) {
1011 const QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex);
1012 Q_ASSERT(propertyData);
1013 }
1014#endif
1015
1016 removeOldBinding(object, index, flags | OverrideSticky);
1017
1018 binding->addToObject();
1019 if (!(flags & DontEnable))
1020 binding->setEnabled(true, writeFlags);
1021}
1022
1023/*!
1024 Returns the expression associated with this signal property, or 0 if no
1025 signal expression exists.
1026*/
1027QQmlBoundSignalExpression *
1028QQmlPropertyPrivate::signalExpression(const QQmlProperty &that)
1029{
1030 if (!(that.type() & QQmlProperty::SignalProperty))
1031 return nullptr;
1032
1033 if (!that.d->object)
1034 return nullptr;
1035 QQmlData *data = QQmlData::get(that.d->object);
1036 if (!data)
1037 return nullptr;
1038
1039 QQmlBoundSignal *signalHandler = data->signalHandlers;
1040
1041 while (signalHandler && signalHandler->signalIndex() != QQmlPropertyPrivate::get(that)->signalIndex())
1042 signalHandler = signalHandler->m_nextSignal;
1043
1044 if (signalHandler)
1045 return signalHandler->expression();
1046
1047 return nullptr;
1048}
1049
1050/*!
1051 Set the signal expression associated with this signal property to \a expr.
1052 A reference to \a expr will be added by QML.
1053*/
1054void QQmlPropertyPrivate::setSignalExpression(const QQmlProperty &that, QQmlBoundSignalExpression *expr)
1055{
1056 if (expr)
1057 expr->addref();
1058 QQmlPropertyPrivate::takeSignalExpression(that, expr);
1059}
1060
1061/*!
1062 Set the signal expression associated with this signal property to \a expr.
1063 Ownership of \a expr transfers to QML.
1064*/
1065void QQmlPropertyPrivate::takeSignalExpression(const QQmlProperty &that,
1066 QQmlBoundSignalExpression *expr)
1067{
1068 if (!(that.type() & QQmlProperty::SignalProperty)) {
1069 if (expr)
1070 expr->release();
1071 return;
1072 }
1073
1074 if (!that.d->object)
1075 return;
1076 QQmlData *data = QQmlData::get(that.d->object, nullptr != expr);
1077 if (!data)
1078 return;
1079
1080 QQmlBoundSignal *signalHandler = data->signalHandlers;
1081
1082 while (signalHandler && signalHandler->signalIndex() != QQmlPropertyPrivate::get(that)->signalIndex())
1083 signalHandler = signalHandler->m_nextSignal;
1084
1085 if (signalHandler) {
1086 signalHandler->takeExpression(expr);
1087 return;
1088 }
1089
1090 if (expr) {
1091 int signalIndex = QQmlPropertyPrivate::get(that)->signalIndex();
1092 QQmlBoundSignal *signal = new QQmlBoundSignal(that.d->object, signalIndex, that.d->object,
1093 expr->engine());
1094 signal->takeExpression(expr);
1095 }
1096}
1097
1098/*!
1099 Returns the property value.
1100*/
1101QVariant QQmlProperty::read() const
1102{
1103 if (!d)
1104 return QVariant();
1105 if (!d->object)
1106 return QVariant();
1107
1108 if (type() & SignalProperty) {
1109
1110 return QVariant();
1111
1112 } else if (type() & Property) {
1113
1114 return d->readValueProperty();
1115
1116 }
1117 return QVariant();
1118}
1119
1120/*!
1121Return the \a name property value of \a object. This method is equivalent to:
1122\code
1123 QQmlProperty p(object, name);
1124 p.read();
1125\endcode
1126*/
1127QVariant QQmlProperty::read(const QObject *object, const QString &name)
1128{
1129 QQmlProperty p(const_cast<QObject *>(object), name);
1130 return p.read();
1131}
1132
1133/*!
1134 Return the \a name property value of \a object using the
1135 \l{QQmlContext} {context} \a ctxt. This method is
1136 equivalent to:
1137
1138 \code
1139 QQmlProperty p(object, name, context);
1140 p.read();
1141 \endcode
1142*/
1143QVariant QQmlProperty::read(const QObject *object, const QString &name, QQmlContext *ctxt)
1144{
1145 QQmlProperty p(const_cast<QObject *>(object), name, ctxt);
1146 return p.read();
1147}
1148
1149/*!
1150
1151 Return the \a name property value of \a object using the environment
1152 for instantiating QML components that is provided by \a engine. .
1153 This method is equivalent to:
1154
1155 \code
1156 QQmlProperty p(object, name, engine);
1157 p.read();
1158 \endcode
1159*/
1160QVariant QQmlProperty::read(const QObject *object, const QString &name, QQmlEngine *engine)
1161{
1162 QQmlProperty p(const_cast<QObject *>(object), name, engine);
1163 return p.read();
1164}
1165
1166QVariant QQmlPropertyPrivate::readValueProperty()
1167{
1168 auto doRead = [&](QQmlGadgetPtrWrapper *wrapper) {
1169 wrapper->read(object, core.coreIndex());
1170 return wrapper->readOnGadget(wrapper->property(valueTypeData.coreIndex()));
1171 };
1172
1173 if (isValueType()) {
1174 if (QQmlGadgetPtrWrapper *wrapper = QQmlGadgetPtrWrapper::instance(engine, core.propType()))
1175 return doRead(wrapper);
1176 if (QQmlValueType *valueType = QQmlMetaType::valueType(core.propType())) {
1177 QQmlGadgetPtrWrapper wrapper(valueType, nullptr);
1178 return doRead(&wrapper);
1179 }
1180 return QVariant();
1181 } else if (core.isQList()) {
1182 auto coreMetaType = core.propType();
1183
1184 // IsQmlList is set for QQmlListPropery and list<ObjectType>
1185 if (coreMetaType.flags() & QMetaType::IsQmlList) {
1186 QQmlListProperty<QObject> prop;
1187 core.readProperty(object, &prop);
1188 return QVariant::fromValue(QQmlListReferencePrivate::init(prop, coreMetaType));
1189 } else {
1190 // but not for lists of value types
1191 QVariant result(coreMetaType);
1192 // TODO: ideally, we would not default construct and copy assign,
1193 // but do a single copy-construct; we don't have API for that, though
1194 coreMetaType.construct(result.data());
1195 core.readProperty(object, result.data());
1196 return result;
1197 }
1198
1199 } else if (core.isQObject()) {
1200
1201 QObject *rv = nullptr;
1202 core.readProperty(object, &rv);
1203 return QVariant::fromValue(rv);
1204
1205 } else {
1206
1207 if (!core.propType().isValid()) // Unregistered type
1208 return object->metaObject()->property(core.coreIndex()).read(object);
1209
1210 QVariant value;
1211 int status = -1;
1212 void *args[] = { nullptr, &value, &status };
1213 if (core.propType() == QMetaType::fromType<QVariant>()) {
1214 args[0] = &value;
1215 } else {
1216 value = QVariant(core.propType(), (void*)nullptr);
1217 args[0] = value.data();
1218 }
1219 core.readPropertyWithArgs(object, args);
1220 if (core.propType() != QMetaType::fromType<QVariant>() && args[0] != value.data())
1221 return QVariant(QMetaType(core.propType()), args[0]);
1222
1223 return value;
1224 }
1225}
1226
1227// helper function to allow assignment / binding to QList<QUrl> properties.
1228QList<QUrl> QQmlPropertyPrivate::urlSequence(const QVariant &value)
1229{
1230 if (value.metaType() == QMetaType::fromType<QList<QUrl>>())
1231 return value.value<QList<QUrl> >();
1232
1233 QList<QUrl> urls;
1234 if (value.metaType() == QMetaType::fromType<QUrl>()) {
1235 urls.append(value.toUrl());
1236 } else if (value.metaType() == QMetaType::fromType<QString>()) {
1237 urls.append(QUrl(value.toString()));
1238 } else if (value.metaType() == QMetaType::fromType<QByteArray>()) {
1239 urls.append(QUrl(QString::fromUtf8(value.toByteArray())));
1240 } else if (value.metaType() == QMetaType::fromType<QStringList>()) {
1241 QStringList urlStrings = value.value<QStringList>();
1242 const int urlStringsSize = urlStrings.size();
1243 urls.reserve(urlStringsSize);
1244 for (int i = 0; i < urlStringsSize; ++i)
1245 urls.append(QUrl(urlStrings.at(i)));
1246 } // note: QList<QByteArray> is not currently supported.
1247 return urls;
1248}
1249
1250// ### Qt7: Get rid of this
1251QList<QUrl> QQmlPropertyPrivate::urlSequence(
1252 const QVariant &value, const QQmlRefPointer<QQmlContextData> &ctxt)
1253{
1254 QList<QUrl> urls = urlSequence(value);
1255
1256 for (auto urlIt = urls.begin(); urlIt != urls.end(); ++urlIt)
1257 *urlIt = ctxt->resolvedUrl(*urlIt);
1258
1259 return urls;
1260}
1261
1262//writeEnumProperty MIRRORS the relelvant bit of QMetaProperty::write AND MUST BE KEPT IN SYNC!
1263bool QQmlPropertyPrivate::writeEnumProperty(const QMetaProperty &prop, int idx, QObject *object, const QVariant &value, int flags)
1264{
1265 if (!object || !prop.isWritable())
1266 return false;
1267
1268 QVariant v = value;
1269 if (prop.isEnumType() && v.metaType() != prop.metaType()) {
1270 QMetaEnum menum = prop.enumerator();
1271 if (v.userType() == QMetaType::QString) {
1272 bool ok;
1273 if (prop.isFlagType())
1274 v = QVariant(menum.keysToValue(value.toByteArray(), &ok));
1275 else
1276 v = QVariant(menum.keyToValue(value.toByteArray(), &ok));
1277 if (!ok)
1278 return false;
1279 }
1280 if (!v.convert(prop.metaType())) // ### TODO: underlyingType might be faster?
1281 return false;
1282 }
1283
1284 // the status variable is changed by qt_metacall to indicate what it did
1285 // this feature is currently only used by QtDBus and should not be depended
1286 // upon. Don't change it without looking into QDBusAbstractInterface first
1287 // -1 (unchanged): normal qt_metacall, result stored in argv[0]
1288 // changed: result stored directly in value, return the value of status
1289 int status = -1;
1290 void *argv[] = { v.data(), &v, &status, &flags };
1291 QMetaObject::metacall(object, QMetaObject::WriteProperty, idx, argv);
1292 return status;
1293}
1294
1295bool QQmlPropertyPrivate::writeValueProperty(const QVariant &value, QQmlPropertyData::WriteFlags flags)
1296{
1297 return writeValueProperty(object, core, valueTypeData, value, effectiveContext(), flags);
1298}
1299
1301 QObject *object, const QQmlPropertyData &core,
1302 const QQmlPropertyData &valueTypeData, QQmlPropertyData::WriteFlags flags)
1303{
1304 // Remove any existing bindings on this property
1305 if (!(flags & QQmlPropertyData::DontRemoveBinding) && object) {
1306 QQmlPropertyPrivate::removeBinding(
1307 object, QQmlPropertyPrivate::encodedIndex(core, valueTypeData),
1308 QQmlPropertyPrivate::OverrideSticky);
1309 }
1310}
1311
1312template<typename Op>
1314 QObject *object, int coreIndex, QQmlGadgetPtrWrapper *wrapper,
1315 QQmlPropertyData::WriteFlags flags, int internalIndex, Op op)
1316{
1317 wrapper->read(object, coreIndex);
1318 const bool rv = op(wrapper);
1319 wrapper->write(object, coreIndex, flags, internalIndex);
1320 return rv;
1321}
1322
1323template<typename Op>
1325 QObject *object, const QQmlPropertyData &core,
1326 const QQmlRefPointer<QQmlContextData> &context, QQmlPropertyData::WriteFlags flags,
1327 int internalIndex, Op op)
1328{
1329 if (QQmlGadgetPtrWrapper *wrapper = context
1330 ? QQmlGadgetPtrWrapper::instance(context->engine(), core.propType())
1331 : nullptr) {
1332 return changePropertyAndWriteBack(
1333 object, core.coreIndex(), wrapper, flags, internalIndex, op);
1334 }
1335
1336 if (QQmlValueType *valueType = QQmlMetaType::valueType(core.propType())) {
1337 QQmlGadgetPtrWrapper wrapper(valueType, nullptr);
1338 return changePropertyAndWriteBack(
1339 object, core.coreIndex(), &wrapper, flags, internalIndex, op);
1340 }
1341
1342 return false;
1343}
1344
1345bool QQmlPropertyPrivate::writeValueProperty(
1346 QObject *object, const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData,
1347 const QVariant &value, const QQmlRefPointer<QQmlContextData> &context,
1348 QQmlPropertyData::WriteFlags flags)
1349{
1350 removeValuePropertyBinding(object, core, valueTypeData, flags);
1351
1352 if (!valueTypeData.isValid())
1353 return write(object, core, value, context, flags);
1354
1355 return changeThroughGadgetPtrWrapper(
1356 object, core, context, flags | QQmlPropertyData::HasInternalIndex,
1357 valueTypeData.coreIndex(), [&](QQmlGadgetPtrWrapper *wrapper) {
1358 return write(wrapper, valueTypeData, value, context, flags);
1359 });
1360}
1361
1362bool QQmlPropertyPrivate::resetValueProperty(
1363 QObject *object, const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData,
1364 const QQmlRefPointer<QQmlContextData> &context, QQmlPropertyData::WriteFlags flags)
1365{
1366 removeValuePropertyBinding(object, core, valueTypeData, flags);
1367
1368 if (!valueTypeData.isValid())
1369 return reset(object, core, flags);
1370
1371 return changeThroughGadgetPtrWrapper(
1372 object, core, context, flags | QQmlPropertyData::HasInternalIndex,
1373 valueTypeData.coreIndex(), [&](QQmlGadgetPtrWrapper *wrapper) {
1374 return reset(wrapper, valueTypeData, flags);
1375 });
1376}
1377
1378// We need to prevent new-style bindings from being removed.
1380{
1382
1383 BindingFixer(QObject *object, const QQmlPropertyData &property,
1384 QQmlPropertyData::WriteFlags flags)
1385 {
1386 // Even if QML cannot install bindings on this property, there may be a C++-created binding.
1387 // If the property can notify via a bindable, there is a bindable that can hold a binding.
1388 if (!property.notifiesViaBindable() || !(flags & QQmlPropertyData::DontRemoveBinding))
1389 return;
1390
1391 QUntypedBindable bindable;
1392 void *argv[] = {&bindable};
1393 QMetaObject::metacall(object, QMetaObject::BindableProperty, property.coreIndex(), argv);
1394 untypedBinding = bindable.binding();
1395 if (auto priv = QPropertyBindingPrivate::get(untypedBinding)) {
1396 wasSticky = priv->isSticky();
1397 priv->setSticky(true);
1398 }
1399 }
1400
1402 {
1403 if (untypedBinding.isNull())
1404 return;
1405 auto priv = QPropertyBindingPrivate::get(untypedBinding);
1406 priv->setSticky(wasSticky);
1407 }
1408
1409private:
1410 QUntypedPropertyBinding untypedBinding;
1411 bool wasSticky = false;
1412};
1413
1415 bool couldConvert = false;
1416 bool couldWrite = false;
1417
1418 operator bool() const { return couldConvert; }
1419};
1420
1422 QObject *object, const QQmlPropertyData &property, const QVariant &value,
1423 QQmlPropertyData::WriteFlags flags, QMetaType propertyMetaType, QMetaType variantMetaType,
1424 bool isUrl, QQmlEnginePrivate *enginePriv) {
1425
1426 if (isUrl
1427 || variantMetaType == QMetaType::fromType<QString>()
1428 || propertyMetaType == QMetaType::fromType<QList<QUrl>>()
1429 || property.isQList()) {
1430 return {false, false};
1431 }
1432
1433 // common cases:
1434 switch (propertyMetaType.id()) {
1435 case QMetaType::Bool:
1436 if (value.canConvert(propertyMetaType)) {
1437 bool b = value.toBool();
1438 return {true, property.writeProperty(object, &b, flags)};
1439 }
1440 return {false, false};
1441 case QMetaType::Int: {
1442 bool ok = false;
1443 int i = value.toInt(&ok);
1444 return {ok, ok && property.writeProperty(object, &i, flags)};
1445 }
1446 case QMetaType::UInt: {
1447 bool ok = false;
1448 uint u = value.toUInt(&ok);
1449 return {ok, ok && property.writeProperty(object, &u, flags)};
1450 }
1451 case QMetaType::Double: {
1452 bool ok = false;
1453 double d = value.toDouble(&ok);
1454 return {ok, ok && property.writeProperty(object, &d, flags)};
1455 }
1456 case QMetaType::Float: {
1457 bool ok = false;
1458 float f = value.toFloat(&ok);
1459 return {ok, ok && property.writeProperty(object, &f, flags)};
1460 }
1461 case QMetaType::QString:
1462 if (value.canConvert(propertyMetaType)) {
1463 QString s = value.toString();
1464 return {true, property.writeProperty(object, &s, flags)};
1465 }
1466 return {false, false};
1467 case QMetaType::QVariantMap:
1468 if (value.canConvert(propertyMetaType)) {
1469 QVariantMap m = value.toMap();
1470 return {true, property.writeProperty(object, &m, flags)};
1471 }
1472 return {false, false};
1473 default: {
1474 break;
1475 }
1476 }
1477
1478 QVariant converted = QQmlValueTypeProvider::createValueType(
1479 value, propertyMetaType, enginePriv ? enginePriv->v4Engine.get() : nullptr);
1480 if (!converted.isValid()) {
1481 converted = QVariant(propertyMetaType);
1482 if (!QMetaType::convert(value.metaType(), value.constData(),
1483 propertyMetaType, converted.data())) {
1484 return {false, false};
1485 }
1486 }
1487 return {true, property.writeProperty(object, converted.data(), flags)};
1488};
1489
1490template<typename Op>
1491bool iterateQObjectContainer(QMetaType metaType, const void *data, Op op)
1492{
1493 QMetaSequence::Iterable iterable;
1494 if (!QMetaType::convert(metaType, data, QMetaType::fromType<QMetaSequence::Iterable>(), &iterable))
1495 return false;
1496
1497 const QMetaSequence metaSequence = iterable.metaContainer();
1498
1499 if (!metaSequence.hasConstIterator()
1500 || !metaSequence.canGetValueAtConstIterator()
1501 || !metaSequence.valueMetaType().flags().testFlag(QMetaType::PointerToQObject)) {
1502 return false;
1503 }
1504
1505 const void *container = iterable.constIterable();
1506 void *it = metaSequence.constBegin(container);
1507 const void *end = metaSequence.constEnd(container);
1508 QObject *o = nullptr;
1509 while (!metaSequence.compareConstIterator(it, end)) {
1510 metaSequence.valueAtConstIterator(it, &o);
1511 op(o);
1512 metaSequence.advanceConstIterator(it, 1);
1513 }
1514 metaSequence.destroyConstIterator(it);
1515 metaSequence.destroyConstIterator(end);
1516 return true;
1517}
1518
1520 QObject *object, const QQmlPropertyData &property, const QVariant &value,
1521 QMetaType variantMetaType) {
1522 if (variantMetaType != QMetaType::fromType<QJSValue>())
1523 return false;
1524
1525 const QJSValue &jsValue = get<QJSValue>(value);
1526 const QV4::FunctionObject *f = QJSValuePrivate::asManagedType<QV4::FunctionObject>(&jsValue);
1527 if (!f || !f->isBinding())
1528 return false;
1529
1530 // fromReturnedValue is safe! f is stored in the QJSValue, so htere's already a persistent reference to it
1531 QV4::QObjectWrapper::setProperty(f->engine(), object, &property, QV4::Value::fromReturnedValue(f->asReturnedValue()));
1532 return true;
1533}
1534
1535template<typename L>
1536qsizetype listCount(const L *list)
1537{
1538 if constexpr (std::is_same_v<L, QQmlListReference>)
1539 return list->count();
1540 if constexpr (std::is_same_v<L, QObjectList>)
1541 return list->count();
1542 if constexpr (std::is_same_v<L, QVariantList>)
1543 return list->count();
1544 return -1;
1545}
1546
1547template<typename L, typename F>
1548bool iterateList(const L *list, qsizetype size, F &&callback)
1549{
1550 for (qsizetype i = 0; i < size; ++i){
1551 if (!callback(i, list->at(i)))
1552 return false;
1553 }
1554
1555 return true;
1556}
1557
1558static QObject *extractObject(const QVariant &variant)
1559{
1560 return QQmlMetaType::toQObject(variant);
1561}
1562
1563static QObject *extractObject(QObject *object)
1564{
1565 return object;
1566}
1567
1568using AssignResult = QQmlPropertyPrivate::ListCopyResult;
1569
1570template<typename L, typename DoAppend>
1572 QQmlListProperty<QObject> *prop, const L *list, DoAppend &&doAppend)
1573{
1574 const qsizetype newSize = listCount(list);
1575 if (prop->at && prop->count && newSize == prop->count(prop)) {
1576 if (iterateList(list, newSize, [prop](qsizetype i, const auto &element) {
1577 return (extractObject(element) == prop->at(prop, i));
1578 })) {
1579 // Nothing to do, the lists are the same
1580 return AssignResult::WasEqual;
1581 }
1582 }
1583
1584 prop->clear(prop);
1585 iterateList(list, newSize, [&](qsizetype, const auto &element) {
1586 return doAppend(prop, extractObject(element));
1587 });
1588
1589 return AssignResult::Copied;
1590}
1591
1592template<typename DoAppend>
1594 QQmlListProperty<QObject> *prop, QMetaType metaType, const void *data, DoAppend &&doAppend)
1595{
1596 QMetaSequence::Iterable iterable;
1597 if (!QMetaType::convert(metaType, data, QMetaType::fromType<QMetaSequence::Iterable>(), &iterable))
1598 return AssignResult::TypeMismatch;
1599
1600 const QMetaSequence metaSequence = iterable.metaContainer();
1601 if (!metaSequence.hasConstIterator()
1602 || !metaSequence.canGetValueAtConstIterator()
1603 || !metaSequence.valueMetaType().flags().testFlag(QMetaType::PointerToQObject)) {
1604 return AssignResult::TypeMismatch;
1605 }
1606
1607 const void *container = iterable.constIterable();
1608 const void *end = metaSequence.constEnd(container);
1609
1610 QObject *o = nullptr;
1611 bool same = false;
1612 if (prop->at && prop->count && metaSequence.hasSize()
1613 && metaSequence.size(container) == prop->count(prop)) {
1614 void *it = metaSequence.constBegin(container);
1615 same = true;
1616 qsizetype i = -1;
1617 while (!metaSequence.compareConstIterator(it, end)) {
1618 metaSequence.valueAtConstIterator(it, &o);
1619 if (o != prop->at(prop, ++i)) {
1620 same = false;
1621 break;
1622 }
1623 metaSequence.advanceConstIterator(it, 1);
1624 }
1625 metaSequence.destroyConstIterator(it);
1626 }
1627
1628 if (!same) {
1629 prop->clear(prop);
1630 void *it = metaSequence.constBegin(container);
1631 while (!metaSequence.compareConstIterator(it, end)) {
1632 metaSequence.valueAtConstIterator(it, &o);
1633 doAppend(prop, o);
1634 metaSequence.advanceConstIterator(it, 1);
1635 }
1636 metaSequence.destroyConstIterator(it);
1637 }
1638
1639 metaSequence.destroyConstIterator(end);
1640 return same ? AssignResult::WasEqual : AssignResult::Copied;
1641}
1642
1643/*!
1644 \internal
1645 Attempts to convert \a value to a QQmlListProperty.
1646 The existing \a listProperty will be modified (so use a temporary one if that
1647 is not desired). \a listProperty is passed as a QQmlListProperty<QObject>,
1648 but might actually be a list property of a more specific type. The actual
1649 type of the list property is given by \a actualListType.
1650*/
1651QQmlPropertyPrivate::ListCopyResult QQmlPropertyPrivate::convertToQQmlListProperty(QQmlListProperty<QObject> *listProperty, QMetaType actualListType, const QVariant &value) {
1652 QQmlListProperty<QObject> &prop = *listProperty;
1653 QMetaType listValueType = QQmlMetaType::listValueType(actualListType);
1654 // elementMetaObject may be null. That means we haven't loaded the type, and the given value
1655 // can't be of this type. That's what the warning in doAppend below is for.
1656 QQmlMetaObject elementMetaObject = QQmlMetaType::rawMetaObjectForType(listValueType);
1657
1658 auto doAppend = [&](QQmlListProperty<QObject> *propPtr, QObject *o) {
1659 if (Q_UNLIKELY(o && (elementMetaObject.isNull()
1660 || !QQmlMetaObject::canConvert(o, elementMetaObject)))) {
1661 qCWarning(lcIncompatibleElement)
1662 << "Cannot append" << o << "to a QML list of" << listValueType.name();
1663 o = nullptr;
1664 }
1665 propPtr->append(propPtr, o);
1666 return true;
1667 };
1668
1669 AssignResult result = AssignResult::TypeMismatch;
1670 QMetaType variantMetaType = value.metaType();
1671 if (variantMetaType == QMetaType::fromType<QQmlListReference>()) {
1672 result = assignListToListProperty(
1673 &prop, static_cast<const QQmlListReference *>(value.constData()),
1674 std::move(doAppend));
1675 } else if (variantMetaType == QMetaType::fromType<QObjectList>()) {
1676 result = assignListToListProperty(
1677 &prop, static_cast<const QObjectList *>(value.constData()),
1678 std::move(doAppend));
1679 } else if (variantMetaType == QMetaType::fromType<QVariantList>()) {
1680 result = assignListToListProperty(
1681 &prop, static_cast<const QVariantList *>(value.constData()),
1682 std::move(doAppend));
1683 } else {
1684 result = assignMetaContainerToListProperty(
1685 &prop, variantMetaType, value.data(), doAppend);
1686 if (result == AssignResult::TypeMismatch) {
1687 prop.clear(&prop);
1688 doAppend(&prop, QQmlMetaType::toQObject(value));
1689 result = AssignResult::Copied;
1690 }
1691 }
1692
1693 return result;
1694}
1695
1696// handles only properties backed by QQmlListProperty
1698 QObject *object,
1699 const QQmlPropertyData &property,
1700 const QMetaType propertyMetaType, const QVariant &value)
1701{
1702 Q_ASSERT(propertyMetaType.flags() & QMetaType::IsQmlList);
1703 QQmlListProperty<QObject> prop;
1704 property.readProperty(object, &prop);
1705
1706 // clear and append are the minimum operations we need to perform an assignment.
1707 if (!prop.clear || !prop.append)
1708 return false;
1709
1710 const bool useNonsignalingListOps = prop.clear == &QQmlVMEMetaObject::list_clear
1711 && prop.append == &QQmlVMEMetaObject::list_append;
1712 if (useNonsignalingListOps) {
1713 prop.clear = &QQmlVMEMetaObject::list_clear_nosignal;
1714 prop.append = &QQmlVMEMetaObject::list_append_nosignal;
1715 }
1716
1717 auto result = QQmlPropertyPrivate::convertToQQmlListProperty(&prop, propertyMetaType, value);
1718
1719 if (useNonsignalingListOps && result == QQmlPropertyPrivate::ListCopyResult::Copied) {
1720 Q_ASSERT(QQmlData::get(object)->hasVMEMetaObject);
1721 QQmlVMEResolvedList(&prop).activateSignal();
1722 }
1723
1724 return result != QQmlPropertyPrivate::ListCopyResult::TypeMismatch;
1725}
1726
1727// handles only properties not backed by QQmlListProperty - both list of value types
1728// and containers of object types
1730 const QQmlPropertyData &property, QQmlPropertyData::WriteFlags flags,
1731 const QMetaType propertyMetaType, const QMetaType variantMetaType, const QVariant &value,
1732 QObject *object)
1733{
1734 Q_ASSERT(!(propertyMetaType.flags() & QMetaType::IsQmlList));
1735 if (variantMetaType == propertyMetaType) {
1736 QVariant v = value;
1737 return property.writeProperty(object, v.data(), flags);
1738 } else {
1739 QVariant outputList(propertyMetaType);
1740 const QQmlType type = QQmlMetaType::qmlListType(propertyMetaType);
1741 const QMetaSequence outputSequence = type.listMetaSequence();
1742 if (!outputSequence.canAddValue())
1743 return property.writeProperty(object, outputList.data(), flags);
1744
1745 const QMetaType outputElementMetaType = outputSequence.valueMetaType();
1746 const bool outputIsQVariant = (outputElementMetaType == QMetaType::fromType<QVariant>());
1747
1748 QMetaSequence::Iterable inputIterable;
1749 QVariant inputList = value;
1750 if (QMetaType::view(
1751 inputList.metaType(), inputList.data(),
1752 QMetaType::fromType<QMetaSequence::Iterable>(), &inputIterable)) {
1753
1754 const QMetaSequence inputSequence = inputIterable.metaContainer();
1755 const QMetaType inputElementMetaType = inputSequence.valueMetaType();
1756 const bool inputIsQVariant = (inputElementMetaType == QMetaType::fromType<QVariant>());
1757
1758 QVariant outputElement
1759 = outputIsQVariant ? QVariant() : QVariant(outputElementMetaType);
1760 QVariant inputElement
1761 = inputIsQVariant ? QVariant() : QVariant(inputElementMetaType);
1762
1763 void *it = inputSequence.constBegin(inputList.constData());
1764 void *end = inputSequence.constEnd(inputList.constData());
1765
1766 for (; !inputSequence.compareConstIterator(it, end);
1767 inputSequence.advanceConstIterator(it, 1)) {
1768
1769 if (inputIsQVariant)
1770 inputSequence.valueAtIterator(it, &inputElement);
1771 else
1772 inputSequence.valueAtIterator(it, inputElement.data());
1773
1774 if (outputIsQVariant) {
1775 outputSequence.addValue(outputList.data(), &inputElement);
1776 } else if (inputElement.metaType() == outputElement.metaType()) {
1777 outputSequence.addValue(outputList.data(), inputElement.constData());
1778 } else {
1779 QMetaType::convert(
1780 inputElement.metaType(), inputElement.constData(),
1781 outputElementMetaType, outputElement.data());
1782 outputSequence.addValue(outputList.data(), outputElement.constData());
1783 }
1784 }
1785
1786 inputSequence.destroyConstIterator(it);
1787 inputSequence.destroyConstIterator(end);
1788 } else if (outputIsQVariant) {
1789 outputSequence.addValue(outputList.data(), &value);
1790 } else if (outputElementMetaType == value.metaType()){
1791 outputSequence.addValue(outputList.data(), value.constData());
1792 } else {
1793 QVariant output(outputElementMetaType);
1794 QMetaType::convert(
1795 value.metaType(), value.constData(), outputElementMetaType, output.data());
1796 outputSequence.addValue(outputList.data(), output.constData());
1797 }
1798
1799 return property.writeProperty(object, outputList.data(), flags);
1800 }
1801}
1802
1803QVariant QQmlPropertyPrivate::convertToWriteTargetType(const QVariant &value, QMetaType targetMetaType){
1804 QMetaType sourceMetaType = value.metaType();
1805 Q_ASSERT(sourceMetaType != targetMetaType);
1806
1807 // handle string converters
1808 if (sourceMetaType == QMetaType::fromType<QString>()) {
1809 bool ok = false;
1810 QVariant converted = QQmlStringConverters::variantFromString(value.toString(), targetMetaType, &ok);
1811 if (ok)
1812 return converted;
1813 }
1814
1815 // try a plain QVariant conversion
1816 // Note that convert clears the old value, and can fail even if canConvert returns true
1817 if (QMetaType::canConvert(sourceMetaType, targetMetaType))
1818 if (QVariant copy = value; copy.convert(targetMetaType))
1819 return copy;
1820
1821 // the only other options are that they are assigning a single value
1822 // or a QVariantList to a sequence type property (eg, an int to a
1823 // QList<int> property) or that we encountered an interface type.
1824
1825 /* Note that we've already handled single-value assignment to QList<QUrl> properties in write,
1826 before calling this function but the generic code still handles them, which is important for
1827 other places*/
1828 QMetaSequence::Iterable iterable;
1829 QVariant sequenceVariant = QVariant(targetMetaType);
1830 if (QMetaType::view(
1831 targetMetaType, sequenceVariant.data(),
1832 QMetaType::fromType<QMetaSequence::Iterable>(),
1833 &iterable)) {
1834 const QMetaSequence propertyMetaSequence = iterable.metaContainer();
1835 if (propertyMetaSequence.canAddValueAtEnd()) {
1836 const QMetaType elementMetaType = propertyMetaSequence.valueMetaType();
1837 void *propertyContainer = iterable.mutableIterable();
1838
1839 if (sourceMetaType == elementMetaType) {
1840 propertyMetaSequence.addValueAtEnd(propertyContainer, value.constData());
1841 return sequenceVariant;
1842 } else if (sourceMetaType == QMetaType::fromType<QVariantList>()) {
1843 const QVariantList list = value.value<QVariantList>();
1844 for (const QVariant &valueElement : list) {
1845 if (valueElement.metaType() == elementMetaType) {
1846 propertyMetaSequence.addValueAtEnd(
1847 propertyContainer, valueElement.constData());
1848 } else {
1849 QVariant converted(elementMetaType);
1850 QMetaType::convert(
1851 valueElement.metaType(), valueElement.constData(),
1852 elementMetaType, converted.data());
1853 propertyMetaSequence.addValueAtEnd(
1854 propertyContainer, converted.constData());
1855 }
1856 }
1857 return sequenceVariant;
1858 } else if (elementMetaType.flags().testFlag(QMetaType::PointerToQObject)) {
1859 const QMetaObject *elementMetaObject = elementMetaType.metaObject();
1860 Q_ASSERT(elementMetaObject);
1861
1862 const auto doAppend = [&](QObject *o) {
1863 QObject *casted = elementMetaObject->cast(o);
1864 propertyMetaSequence.addValueAtEnd(propertyContainer, &casted);
1865 };
1866
1867 if (sourceMetaType.flags().testFlag(QMetaType::PointerToQObject)) {
1868 doAppend(*static_cast<QObject *const *>(value.data()));
1869 return sequenceVariant;
1870 } else if (sourceMetaType == QMetaType::fromType<QQmlListReference>()) {
1871 const QQmlListReference *reference
1872 = static_cast<const QQmlListReference *>(value.constData());
1873 Q_ASSERT(elementMetaObject);
1874 for (int i = 0, end = reference->size(); i < end; ++i)
1875 doAppend(reference->at(i));
1876 return sequenceVariant;
1877 } else if (!iterateQObjectContainer(
1878 sourceMetaType, value.data(), doAppend)) {
1879 doAppend(QQmlMetaType::toQObject(value));
1880 }
1881 } else {
1882 QVariant converted = value;
1883 if (converted.convert(elementMetaType)) {
1884 propertyMetaSequence.addValueAtEnd(propertyContainer, converted.constData());
1885 return sequenceVariant;
1886 }
1887 }
1888 }
1889 }
1890
1891
1892 if (QQmlMetaType::isInterface(targetMetaType)) {
1893 auto valueAsQObject = qvariant_cast<QObject *>(value);
1894
1895 if (void *iface = valueAsQObject
1896 ? valueAsQObject->qt_metacast(QQmlMetaType::interfaceIId(targetMetaType))
1897 : nullptr;
1898 iface) {
1899 // this case can occur when object has an interface type
1900 // and the variant contains a type implementing the interface
1901 return QVariant(targetMetaType, &iface);
1902 }
1903 }
1904 return QVariant();
1905}
1906
1907bool QQmlPropertyPrivate::write(
1908 QObject *object, const QQmlPropertyData &property, const QVariant &value,
1909 const QQmlRefPointer<QQmlContextData> &context, QQmlPropertyData::WriteFlags flags)
1910{
1911 const QMetaType propertyMetaType = property.propType();
1912 const QMetaType variantMetaType = value.metaType();
1913
1914 const BindingFixer bindingFixer(object, property, flags);
1915
1916 // handle property resets here to avoid duplciating code for QObject and other properties
1917 if (property.isResettable() && !value.isValid()) {
1918 property.resetProperty(object, flags);
1919 return true;
1920 }
1921
1922 if (property.isEnum()) {
1923 QMetaProperty prop = object->metaObject()->property(property.coreIndex());
1924 QVariant v = value;
1925 // Enum values come through the script engine as doubles
1926 if (variantMetaType == QMetaType::fromType<double>()) {
1927 double integral;
1928 double fractional = std::modf(value.toDouble(), &integral);
1929 if (qFuzzyIsNull(fractional))
1930 v.convert(QMetaType::fromType<qint32>());
1931 }
1932 return writeEnumProperty(prop, property.coreIndex(), object, v, flags);
1933 }
1934
1935 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(context);
1936 const bool isUrl = propertyMetaType == QMetaType::fromType<QUrl>(); // handled separately
1937
1938 // Handle Qt.binding bindings here to avoid mistaken conversion below
1939 if (tryAssignBinding(object, property, value, variantMetaType))
1940 return true;
1941
1942 // The cases below are in approximate order of likelyhood:
1943 if (propertyMetaType == variantMetaType && !isUrl
1944 && propertyMetaType != QMetaType::fromType<QList<QUrl>>() && !property.isQList()) {
1945 return property.writeProperty(object, const_cast<void *>(value.constData()), flags);
1946 } else if (property.isQObject()) {
1947 QVariant val = value;
1948 QMetaType varType;
1949 if (variantMetaType == QMetaType::fromType<std::nullptr_t>()) {
1950 // This reflects the fact that you can assign a nullptr to a QObject pointer
1951 // Without the change to QObjectStar, rawMetaObjectForType would not give us a QQmlMetaObject
1952 varType = QMetaType::fromType<QObject*>();
1953 val = QVariant(varType, nullptr);
1954 } else {
1955 varType = variantMetaType;
1956 }
1957
1958 if (!varType.flags().testFlag(QMetaType::PointerToQObject))
1959 return false;
1960
1961 QObject *o = *static_cast<QObject *const *>(val.constData());
1962 if (o) {
1963 // If we have an object, we can use that to obtain and check the metaobject.
1964 // Composite property type. We can check all candidate composite types in one go.
1965 return QQmlMetaType::canConvert(o, propertyMetaType)
1966 && property.writeProperty(object, &o, flags);
1967 }
1968
1969 // In the case of a null QObject, we assign the null if there is
1970 // any chance that the variant type could be up or down cast to
1971 // the property type.
1972 const QQmlMetaObject valMo = rawMetaObjectForType(varType);
1973 const QQmlMetaObject propMo = rawMetaObjectForType(propertyMetaType);
1974 if (QQmlMetaObject::canConvert(valMo, propMo) || QQmlMetaObject::canConvert(propMo, valMo))
1975 return property.writeProperty(object, &o, flags);
1976 return false;
1977 } else if (ConvertAndAssignResult result = tryConvertAndAssign(
1978 object, property, value, flags, propertyMetaType, variantMetaType, isUrl,
1979 enginePriv)) {
1980 return result.couldWrite;
1981 } else if (propertyMetaType == QMetaType::fromType<QVariant>()) {
1982 return property.writeProperty(object, const_cast<QVariant *>(&value), flags);
1983 } else if (isUrl) {
1984 QUrl u;
1985 if (variantMetaType == QMetaType::fromType<QUrl>()) {
1986 u = value.toUrl();
1987 if (compatResolveUrlsOnAssigment() && context && u.isRelative() && !u.isEmpty())
1988 u = context->resolvedUrl(u);
1989 }
1990 else if (variantMetaType == QMetaType::fromType<QByteArray>())
1991 u = QUrl(QString::fromUtf8(value.toByteArray()));
1992 else if (variantMetaType == QMetaType::fromType<QString>())
1993 u = QUrl(value.toString());
1994 else
1995 return false;
1996
1997 return property.writeProperty(object, &u, flags);
1998 } else if (propertyMetaType == QMetaType::fromType<QList<QUrl>>()) {
1999 QList<QUrl> urlSeq = compatResolveUrlsOnAssigment()
2000 ? urlSequence(value, context)
2001 : urlSequence(value);
2002 return property.writeProperty(object, &urlSeq, flags);
2003 } else if (property.isQList()) {
2004 if (propertyMetaType.flags() & QMetaType::IsQmlList) {
2005 return assignToQQmlListProperty(object, property, propertyMetaType, value);
2006 } else {
2007 return assignToListProperty(property, flags, propertyMetaType, variantMetaType, value, object);
2008 }
2009 } else if (enginePriv && propertyMetaType == QMetaType::fromType<QJSValue>()) {
2010 // We can convert everything into a QJSValue if we have an engine.
2011 QJSValue jsValue = QJSValuePrivate::fromReturnedValue(
2012 enginePriv->v4Engine->metaTypeToJS(variantMetaType, value.constData()));
2013 return property.writeProperty(object, &jsValue, flags);
2014 } else {
2015 Q_ASSERT(variantMetaType != propertyMetaType);
2016
2017 QVariant converted = convertToWriteTargetType(value, propertyMetaType);
2018 if (converted.isValid()) {
2019 return property.writeProperty(object, const_cast<void *>(converted.constData()), flags);
2020 } else {
2021 return false;
2022 }
2023 }
2024
2025 return true;
2026}
2027
2028bool QQmlPropertyPrivate::reset(
2029 QObject *object, const QQmlPropertyData &property,
2030 QQmlPropertyData::WriteFlags flags)
2031{
2032 const BindingFixer bindingFixer(object, property, flags);
2033 property.resetProperty(object, flags);
2034 return true;
2035}
2036
2037QQmlMetaObject QQmlPropertyPrivate::rawMetaObjectForType(QMetaType metaType)
2038{
2039 if (metaType.flags() & QMetaType::PointerToQObject) {
2040 if (const QMetaObject *metaObject = metaType.metaObject())
2041 return metaObject;
2042 }
2043 return QQmlMetaType::rawMetaObjectForType(metaType);
2044}
2045
2046/*!
2047 Sets the property value to \a value. Returns \c true on success, or
2048 \c false if the property can't be set because the \a value is the
2049 wrong type, for example.
2050 */
2051bool QQmlProperty::write(const QVariant &value) const
2052{
2053 return QQmlPropertyPrivate::write(*this, value, {});
2054}
2055
2056/*!
2057 Writes \a value to the \a name property of \a object. This method
2058 is equivalent to:
2059
2060 \code
2061 QQmlProperty p(object, name);
2062 p.write(value);
2063 \endcode
2064
2065 Returns \c true on success, \c false otherwise.
2066*/
2067bool QQmlProperty::write(QObject *object, const QString &name, const QVariant &value)
2068{
2069 QQmlProperty p(object, name);
2070 return p.write(value);
2071}
2072
2073/*!
2074 Writes \a value to the \a name property of \a object using the
2075 \l{QQmlContext} {context} \a ctxt. This method is
2076 equivalent to:
2077
2078 \code
2079 QQmlProperty p(object, name, ctxt);
2080 p.write(value);
2081 \endcode
2082
2083 Returns \c true on success, \c false otherwise.
2084*/
2085bool QQmlProperty::write(QObject *object,
2086 const QString &name,
2087 const QVariant &value,
2088 QQmlContext *ctxt)
2089{
2090 QQmlProperty p(object, name, ctxt);
2091 return p.write(value);
2092}
2093
2094/*!
2095
2096 Writes \a value to the \a name property of \a object using the
2097 environment for instantiating QML components that is provided by
2098 \a engine. This method is equivalent to:
2099
2100 \code
2101 QQmlProperty p(object, name, engine);
2102 p.write(value);
2103 \endcode
2104
2105 Returns \c true on success, \c false otherwise.
2106*/
2107bool QQmlProperty::write(QObject *object, const QString &name, const QVariant &value,
2108 QQmlEngine *engine)
2109{
2110 QQmlProperty p(object, name, engine);
2111 return p.write(value);
2112}
2113
2114/*!
2115 Resets the property and returns true if the property is
2116 resettable. If the property is not resettable, nothing happens
2117 and false is returned.
2118*/
2119bool QQmlProperty::reset() const
2120{
2121 if (isResettable()) {
2122 void *args[] = { nullptr };
2123 QMetaObject::metacall(d->object, QMetaObject::ResetProperty, d->core.coreIndex(), args);
2124 return true;
2125 } else {
2126 return false;
2127 }
2128}
2129
2130bool QQmlPropertyPrivate::write(const QQmlProperty &that,
2131 const QVariant &value, QQmlPropertyData::WriteFlags flags)
2132{
2133 if (!that.d)
2134 return false;
2135 if (that.d->object && that.type() & QQmlProperty::Property &&
2136 that.d->core.isValid() && that.isWritable())
2137 return that.d->writeValueProperty(value, flags);
2138 else
2139 return false;
2140}
2141
2142/*!
2143 Returns true if the property has a change notifier signal, otherwise false.
2144*/
2145bool QQmlProperty::hasNotifySignal() const
2146{
2147 if (type() & Property && d->object) {
2148 return d->object->metaObject()->property(d->core.coreIndex()).hasNotifySignal();
2149 }
2150 return false;
2151}
2152
2153/*!
2154 Returns true if the property needs a change notifier signal for bindings
2155 to remain upto date, false otherwise.
2156
2157 Some properties, such as attached properties or those whose value never
2158 changes, do not require a change notifier.
2159*/
2160bool QQmlProperty::needsNotifySignal() const
2161{
2162 return type() & Property && !property().isConstant();
2163}
2164
2165/*!
2166 Connects the property's change notifier signal to the
2167 specified \a method of the \a dest object and returns
2168 true. Returns false if this metaproperty does not
2169 represent a regular Qt property or if it has no
2170 change notifier signal, or if the \a dest object does
2171 not have the specified \a method.
2172*/
2173bool QQmlProperty::connectNotifySignal(QObject *dest, int method) const
2174{
2175 if (!(type() & Property) || !d->object)
2176 return false;
2177
2178 QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex());
2179 if (prop.hasNotifySignal()) {
2180 return QQmlPropertyPrivate::connect(d->object, prop.notifySignalIndex(), dest, method, Qt::DirectConnection);
2181 } else {
2182 return false;
2183 }
2184}
2185
2186/*!
2187 Connects the property's change notifier signal to the
2188 specified \a slot of the \a dest object and returns
2189 true. Returns false if this metaproperty does not
2190 represent a regular Qt property or if it has no
2191 change notifier signal, or if the \a dest object does
2192 not have the specified \a slot.
2193
2194 \note \a slot should be passed using the SLOT() macro so it is
2195 correctly identified.
2196*/
2197bool QQmlProperty::connectNotifySignal(QObject *dest, const char *slot) const
2198{
2199 if (!(type() & Property) || !d->object)
2200 return false;
2201
2202 QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex());
2203 if (prop.hasNotifySignal()) {
2204 QByteArray signal('2' + prop.notifySignal().methodSignature());
2205 return QObject::connect(d->object, signal.constData(), dest, slot);
2206 } else {
2207 return false;
2208 }
2209}
2210
2211/*!
2212 Return the Qt metaobject index of the property.
2213*/
2214int QQmlProperty::index() const
2215{
2216 return d ? d->core.coreIndex() : -1;
2217}
2218
2219QQmlPropertyIndex QQmlPropertyPrivate::propertyIndex(const QQmlProperty &that)
2220{
2221 return that.d ? that.d->encodedIndex() : QQmlPropertyIndex();
2222}
2223
2224QQmlProperty
2225QQmlPropertyPrivate::restore(QObject *object, const QQmlPropertyData &data,
2226 const QQmlPropertyData *valueTypeData,
2227 const QQmlRefPointer<QQmlContextData> &ctxt)
2228{
2229 QQmlProperty prop;
2230
2231 prop.d = new QQmlPropertyPrivate;
2232 prop.d->object = object;
2233 prop.d->context = ctxt;
2234 prop.d->engine = ctxt ? ctxt->engine() : nullptr;
2235
2236 prop.d->core = data;
2237 if (valueTypeData)
2238 prop.d->valueTypeData = *valueTypeData;
2239
2240 return prop;
2241}
2242
2243/*!
2244 Return the signal corresponding to \a name
2245*/
2246QMetaMethod QQmlPropertyPrivate::findSignalByName(const QMetaObject *mo, const QByteArray &name)
2247{
2248 Q_ASSERT(mo);
2249 int methods = mo->methodCount();
2250 for (int ii = methods - 1; ii >= 2; --ii) { // >= 2 to block the destroyed signal
2251 QMetaMethod method = mo->method(ii);
2252
2253 if (method.name() == name && (method.methodType() & QMetaMethod::Signal))
2254 return method;
2255 }
2256
2257 // If no signal is found, but the signal is of the form "onBlahChanged",
2258 // return the notify signal for the property "Blah"
2259 if (auto propName = QQmlSignalNames::changedSignalNameToPropertyName(name)) {
2260 int propIdx = mo->indexOfProperty(propName->constData());
2261 if (propIdx >= 0) {
2262 QMetaProperty prop = mo->property(propIdx);
2263 if (prop.hasNotifySignal())
2264 return prop.notifySignal();
2265 }
2266 }
2267
2268 return QMetaMethod();
2269}
2270
2271/*!
2272 Return the property corresponding to \a name
2273*/
2274QMetaProperty QQmlPropertyPrivate::findPropertyByName(const QMetaObject *mo, const QByteArray &name)
2275{
2276 Q_ASSERT(mo);
2277 const int i = mo->indexOfProperty(name);
2278 return i < 0 ? QMetaProperty() : mo->property(i);
2279}
2280
2281/*! \internal
2282 If \a indexInSignalRange is true, \a index is treated as a signal index
2283 (see QObjectPrivate::signalIndex()), otherwise it is treated as a
2284 method index (QMetaMethod::methodIndex()).
2285*/
2286static inline void flush_vme_signal(const QObject *object, int index, bool indexInSignalRange)
2287{
2288 QQmlData *data = QQmlData::get(object);
2289 if (data && data->propertyCache) {
2290 const QQmlPropertyData *property = indexInSignalRange ? data->propertyCache->signal(index)
2291 : data->propertyCache->method(index);
2292
2293 if (property && property->isVMESignal()) {
2294 QQmlVMEMetaObject::connectAlias(const_cast<QObject *>(object), index,
2295 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)