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