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