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 "qqmllist.h"
5#include "qqmllist_p.h"
6#include <QtQml/private/qqmlproperty_p.h>
7
8QT_BEGIN_NAMESPACE
9
10static bool isObjectCompatible(QObject *object, QQmlListReferencePrivate *d)
11{
12 if (object) {
13 const QQmlMetaObject elementType = d->elementType();
14 if (elementType.isNull() || !QQmlMetaObject::canConvert(from: object, to: elementType))
15 return false;
16 }
17 return true;
18}
19
20QQmlListReferencePrivate::QQmlListReferencePrivate()
21: refCount(1)
22{
23}
24
25QQmlListReference QQmlListReferencePrivate::init(
26 const QQmlListProperty<QObject> &prop, QMetaType propType)
27{
28 QQmlListReference rv;
29
30 if (!prop.object) return rv;
31
32 rv.d = new QQmlListReferencePrivate;
33 rv.d->object = prop.object;
34 rv.d->property = prop;
35 rv.d->propertyType = propType;
36
37 return rv;
38}
39
40void QQmlListReferencePrivate::addref()
41{
42 Q_ASSERT(refCount > 0);
43 ++refCount;
44}
45
46void QQmlListReferencePrivate::release()
47{
48 Q_ASSERT(refCount > 0);
49 --refCount;
50 if (!refCount)
51 delete this;
52}
53
54/*!
55\class QQmlListReference
56\since 5.0
57\inmodule QtQml
58\brief The QQmlListReference class allows the manipulation of QQmlListProperty properties.
59
60QQmlListReference allows C++ programs to read from, and assign values to a QML list property in a
61simple and type-safe way. A QQmlListReference can be created by passing an object and property
62name or through a QQmlProperty instance. These two are equivalent:
63
64\code
65QQmlListReference ref1(object, "children");
66
67QQmlProperty ref2(object, "children");
68QQmlListReference ref2 = qvariant_cast<QQmlListReference>(ref2.read());
69\endcode
70
71Not all QML list properties support all operations. A set of methods, canAppend(), canAt(), canClear() and
72canCount() allow programs to query whether an operation is supported on a given property.
73
74QML list properties are type-safe. Only QObject's that derive from the correct base class can be assigned to
75the list. The listElementType() method can be used to query the QMetaObject of the QObject type supported.
76Attempting to add objects of the incorrect type to a list property will fail.
77
78Like with normal lists, when accessing a list element by index, it is the callers responsibility to ensure
79that it does not request an out of range element using the count() method before calling at().
80*/
81
82/*!
83Constructs an invalid instance.
84*/
85QQmlListReference::QQmlListReference()
86: d(nullptr)
87{
88}
89
90#if QT_DEPRECATED_SINCE(6, 4)
91/*!
92\since 6.1
93\obsolete [6.4] Use the constructors without QQmlEngine argument instead.
94
95Constructs a QQmlListReference from a QVariant \a variant containing a QQmlListProperty. If
96\a variant does not contain a list property, an invalid QQmlListReference is created. If the object
97owning the list property is destroyed after the reference is constructed, it will automatically
98become invalid. That is, it is safe to hold QQmlListReference instances even after the object is
99deleted.
100
101The \a engine is unused.
102*/
103QQmlListReference::QQmlListReference(const QVariant &variant, [[maybe_unused]] QQmlEngine *engine)
104 : QQmlListReference(variant)
105{}
106
107/*!
108\obsolete [6.4] Use the constructors without QQmlEngine argument instead.
109
110Constructs a QQmlListReference for \a object's \a property. If \a property is not a list
111property, an invalid QQmlListReference is created. If \a object is destroyed after
112the reference is constructed, it will automatically become invalid. That is, it is safe to hold
113QQmlListReference instances even after \a object is deleted.
114
115The \a engine is unused.
116*/
117QQmlListReference::QQmlListReference(QObject *object, const char *property,
118 [[maybe_unused]] QQmlEngine *engine)
119 : QQmlListReference(object, property)
120{}
121#endif
122
123/*!
124\since 6.1
125
126Constructs a QQmlListReference from a QVariant \a variant containing a QQmlListProperty. If
127\a variant does not contain a list property, an invalid QQmlListReference is created. If the object
128owning the list property is destroyed after the reference is constructed, it will automatically
129become invalid. That is, it is safe to hold QQmlListReference instances even after the object is
130deleted.
131*/
132QQmlListReference::QQmlListReference(const QVariant &variant)
133 : d(nullptr)
134{
135 const QMetaType t = variant.metaType();
136 if (!(t.flags() & QMetaType::IsQmlList))
137 return;
138
139 d = new QQmlListReferencePrivate;
140 d->propertyType = t;
141
142 d->property.~QQmlListProperty();
143 t.construct(where: &d->property, copy: variant.constData());
144
145 d->object = d->property.object;
146}
147
148/*!
149Constructs a QQmlListReference for \a object's \a property. If \a property is not a list
150property, an invalid QQmlListReference is created. If \a object is destroyed after
151the reference is constructed, it will automatically become invalid. That is, it is safe to hold
152QQmlListReference instances even after \a object is deleted.
153*/
154QQmlListReference::QQmlListReference(QObject *object, const char *property)
155 : d(nullptr)
156{
157 if (!object || !property) return;
158
159 QQmlPropertyData local;
160 const QQmlPropertyData *data =
161 QQmlPropertyCache::property(object, QLatin1String(property), nullptr, &local);
162
163 if (!data || !data->isQList()) return;
164
165 d = new QQmlListReferencePrivate;
166 d->object = object;
167 d->propertyType = data->propType();
168
169 void *args[] = { &d->property, nullptr };
170 QMetaObject::metacall(object, QMetaObject::ReadProperty, data->coreIndex(), args);
171}
172
173/*! \internal */
174QQmlListReference::QQmlListReference(const QQmlListReference &o)
175: d(o.d)
176{
177 if (d) d->addref();
178}
179
180/*! \internal */
181QQmlListReference &QQmlListReference::operator=(const QQmlListReference &o)
182{
183 if (o.d) o.d->addref();
184 if (d) d->release();
185 d = o.d;
186 return *this;
187}
188
189/*! \internal */
190QQmlListReference::~QQmlListReference()
191{
192 if (d) d->release();
193}
194
195/*!
196Returns true if the instance refers to a valid list property, otherwise false.
197*/
198bool QQmlListReference::isValid() const
199{
200 return d && d->object;
201}
202
203/*!
204Returns the list property's object. Returns \nullptr if the reference is invalid.
205*/
206QObject *QQmlListReference::object() const
207{
208 if (isValid()) return d->object;
209 else return nullptr;
210}
211
212/*!
213Returns the QMetaObject for the elements stored in the list property,
214or \nullptr if the reference is invalid.
215
216The QMetaObject can be used ahead of time to determine whether a given instance can be added
217to a list. If you didn't pass an engine on construction this may return nullptr.
218*/
219const QMetaObject *QQmlListReference::listElementType() const
220{
221 return isValid() ? d->elementType() : nullptr;
222}
223
224/*!
225Returns true if the list property can be appended to, otherwise false. Returns false if the
226reference is invalid.
227
228\sa append()
229*/
230bool QQmlListReference::canAppend() const
231{
232 return (isValid() && d->property.append);
233}
234
235/*!
236Returns true if the list property can queried by index, otherwise false. Returns false if the
237reference is invalid.
238
239\sa at()
240*/
241bool QQmlListReference::canAt() const
242{
243 return (isValid() && d->property.at);
244}
245
246/*!
247Returns true if the list property can be cleared, otherwise false. Returns false if the
248reference is invalid.
249
250\sa clear()
251*/
252bool QQmlListReference::canClear() const
253{
254 return (isValid() && d->property.clear);
255}
256
257/*!
258Returns true if the list property can be queried for its element count, otherwise false.
259Returns false if the reference is invalid.
260
261\sa count()
262*/
263bool QQmlListReference::canCount() const
264{
265 return (isValid() && d->property.count);
266}
267
268/*!
269Returns true if items in the list property can be replaced, otherwise false.
270Returns false if the reference is invalid.
271
272\sa replace()
273*/
274bool QQmlListReference::canReplace() const
275{
276 return (isValid() && d->property.replace);
277}
278
279/*!
280Returns true if the last item can be removed from the list property, otherwise false.
281Returns false if the reference is invalid.
282
283\sa removeLast()
284*/
285bool QQmlListReference::canRemoveLast() const
286{
287 return (isValid() && d->property.removeLast);
288}
289
290/*!
291 Return true if at(), count(), append(), and either clear() or removeLast()
292 are implemented, so you can manipulate the list.
293
294 Mind that replace() and removeLast() can be emulated by stashing all
295 items and rebuilding the list using clear() and append(). Therefore,
296 they are not required for the list to be manipulable. Furthermore,
297 clear() can be emulated using removeLast().
298
299\sa isReadable(), at(), count(), append(), clear(), replace(), removeLast()
300*/
301bool QQmlListReference::isManipulable() const
302{
303 return (isValid()
304 && d->property.append
305 && d->property.count
306 && d->property.at
307 && d->property.clear);
308}
309
310
311/*!
312 Return true if at() and count() are implemented, so you can access the elements.
313
314\sa isManipulable(), at(), count()
315*/
316bool QQmlListReference::isReadable() const
317{
318 return (isValid() && d->property.count && d->property.at);
319}
320
321/*!
322Appends \a object to the list. Returns true if the operation succeeded, otherwise false.
323
324\sa canAppend()
325*/
326bool QQmlListReference::append(QObject *object) const
327{
328 if (!canAppend()) return false;
329
330 if (!isObjectCompatible(object, d))
331 return false;
332
333 d->property.append(&d->property, object);
334
335 return true;
336}
337
338/*!
339Returns the list element at \a index, or 0 if the operation failed.
340
341\sa canAt()
342*/
343QObject *QQmlListReference::at(qsizetype index) const
344{
345 if (!canAt()) return nullptr;
346
347 return d->property.at(&d->property, index);
348}
349
350/*!
351Clears the list. Returns true if the operation succeeded, otherwise false.
352
353\sa canClear()
354*/
355bool QQmlListReference::clear() const
356{
357 if (!canClear()) return false;
358
359 d->property.clear(&d->property);
360
361 return true;
362}
363
364/*!
365Returns the number of objects in the list, or 0 if the operation failed.
366*/
367qsizetype QQmlListReference::count() const
368{
369 if (!canCount()) return 0;
370
371 return d->property.count(&d->property);
372}
373
374/*!
375\fn qsizetype QQmlListReference::size() const
376\since 6.2
377Returns the number of objects in the list, or 0 if the operation failed.
378*/
379
380/*!
381Replaces the item at \a index in the list with \a object.
382Returns true if the operation succeeded, otherwise false.
383
384\sa canReplace()
385*/
386bool QQmlListReference::replace(qsizetype index, QObject *object) const
387{
388 if (!canReplace())
389 return false;
390
391 if (!isObjectCompatible(object, d))
392 return false;
393
394 d->property.replace(&d->property, index, object);
395 return true;
396}
397
398/*!
399Removes the last item in the list.
400Returns true if the operation succeeded, otherwise false.
401
402\sa canRemoveLast()
403*/
404bool QQmlListReference::removeLast() const
405{
406 if (!canRemoveLast())
407 return false;
408
409 d->property.removeLast(&d->property);
410 return true;
411}
412
413/*!
414\class QQmlListProperty
415\since 5.0
416\inmodule QtQml
417\brief The QQmlListProperty class allows applications to expose list-like
418properties of QObject-derived classes to QML.
419
420QML has many list properties, where more than one object value can be assigned.
421The use of a list property from QML looks like this:
422
423\code
424FruitBasket {
425 fruit: [
426 Apple {},
427 Orange{},
428 Banana{}
429 ]
430}
431\endcode
432
433The QQmlListProperty encapsulates a group of function pointers that represent the
434set of actions QML can perform on the list - adding items, retrieving items and
435clearing the list. In the future, additional operations may be supported. All
436list properties must implement the append operation, but the rest are optional.
437
438To provide a list property, a C++ class must implement the operation callbacks,
439and then return an appropriate QQmlListProperty value from the property getter.
440List properties should have no setter. In the example above, the Q_PROPERTY()
441declarative will look like this:
442
443\code
444Q_PROPERTY(QQmlListProperty<Fruit> fruit READ fruit)
445\endcode
446
447QML list properties are type-safe - in this case \c {Fruit} is a QObject type that
448\c {Apple}, \c {Orange} and \c {Banana} all derive from.
449
450\sa {Chapter 5: Using List Property Types}
451*/
452
453/*!
454 \macro QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_APPEND
455 \relates QQmlListProperty
456
457 This macro defines the behavior of the list properties of this class to Append.
458 When assigning the property in a derived type, the values are appended
459 to those of the base class. This is the default behavior.
460
461 \snippet code/src_qml_qqmllist.cpp 0
462
463 \sa QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE_IF_NOT_DEFAULT
464 \sa QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE
465 \sa {Defining Object Types through QML Documents}
466*/
467
468/*!
469 \macro QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE_IF_NOT_DEFAULT
470 \relates QQmlListProperty
471
472 This macro defines the behavior of the list properties of this class to
473 ReplaceIfNotDefault.
474 When assigning the property in a derived type, the values replace those of
475 the base class unless it's the default property.
476 In the case of the default property, values are appended to those of the base class.
477
478 \snippet code/src_qml_qqmllist.cpp 1
479
480 \sa QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_APPEND
481 \sa QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE
482 \sa {Defining Object Types through QML Documents}
483*/
484
485/*!
486 \macro QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE
487 \relates QQmlListProperty
488
489 This macro defines the behavior of the list properties of this class to Replace.
490 When assigning the property in a derived type, the values replace those
491 of the base class.
492
493 \snippet code/src_qml_qqmllist.cpp 2
494
495 \sa QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_APPEND
496 \sa QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE_IF_NOT_DEFAULT
497 \sa {Defining Object Types through QML Documents}
498*/
499
500/*!
501\fn template<typename T> QQmlListProperty<T>::QQmlListProperty()
502\internal
503*/
504
505/*!
506\fn template<typename T> QQmlListProperty<T>::QQmlListProperty(QObject *object, QList<T *> &list)
507\deprecated
508
509Convenience constructor for making a QQmlListProperty value from an existing
510QList \a list. The \a list reference must remain valid for as long as \a object
511exists. \a object must be provided.
512
513This constructor synthesizes the removeLast() and replace() methods
514introduced in Qt 5.15, using count(), at(), clear(), and append(). This is slow.
515If you intend to manipulate the list beyond clearing it, you should explicitly
516provide these methods.
517*/
518
519/*!
520\fn template<typename T> QQmlListProperty<T>::QQmlListProperty(QObject *object, QList<T *> *list)
521\since 5.15
522
523Convenience constructor for making a QQmlListProperty value from an existing
524QList \a list. The \a list reference must remain valid for as long as \a object
525exists. \a object must be provided.
526*/
527
528/*!
529\fn template<typename T> QQmlListProperty<T>::QQmlListProperty(QObject *object, void *data,
530 CountFunction count, AtFunction at)
531
532Construct a readonly QQmlListProperty from a set of operation functions
533\a count and \a at. An opaque \a data handle may be passed which can be
534accessed from within the operation functions. The list property
535remains valid while \a object exists.
536*/
537
538/*!
539\fn template<typename T> QQmlListProperty<T>::QQmlListProperty(QObject *object, void *data, AppendFunction append,
540 CountFunction count, AtFunction at,
541 ClearFunction clear)
542
543Construct a QQmlListProperty from a set of operation functions \a append,
544\a count, \a at, and \a clear. An opaque \a data handle may be passed which
545can be accessed from within the operation functions. The list property
546remains valid while \a object exists.
547
548Null pointers can be passed for any function. If any null pointers are passed in, the list
549will be neither designable nor alterable by the debugger. It is recommended to provide valid
550pointers for all functions.
551
552\note The resulting QQmlListProperty will synthesize the removeLast() and
553replace() methods using \a count, \a at, \a clear, and \a append if all of those
554are given. This is slow. If you intend to manipulate the list beyond clearing it,
555you should explicitly provide these methods.
556*/
557
558/*!
559\fn template<typename T> QQmlListProperty<T>::QQmlListProperty(
560 QObject *object, void *data, AppendFunction append, CountFunction count,
561 AtFunction at, ClearFunction clear, ReplaceFunction replace,
562 RemoveLastFunction removeLast)
563
564Construct a QQmlListProperty from a set of operation functions \a append,
565\a count, \a at, \a clear, \a replace, and \removeLast. An opaque \a data handle
566may be passed which can be accessed from within the operation functions. The
567list property remains valid while \a object exists.
568
569Null pointers can be passed for any function, causing the respective function to
570be synthesized using the others, if possible. QQmlListProperty can synthesize
571\list
572 \li \a clear using \a count and \a removeLast
573 \li \a replace using \a count, \a at, \a clear, and \a append
574 \li \a replace using \a count, \a at, \a removeLast, and \a append
575 \li \a removeLast using \a count, \a at, \a clear, and \a append
576\endlist
577if those are given. This is slow, but if your list does not natively provide
578faster options for these primitives, you may want to use the synthesized ones.
579
580Furthermore, if either of \a count, \a at, \a append, and \a clear are neither
581given explicitly nor synthesized, the list will be neither designable nor
582alterable by the debugger. It is recommended to provide enough valid pointers
583to avoid this situation.
584*/
585
586/*!
587\typedef QQmlListProperty::AppendFunction
588
589Synonym for \c {void (*)(QQmlListProperty<T> *property, T *value)}.
590
591Append the \a value to the list \a property.
592*/
593
594/*!
595\typedef QQmlListProperty::CountFunction
596
597Synonym for \c {qsizetype (*)(QQmlListProperty<T> *property)}.
598
599Return the number of elements in the list \a property.
600*/
601
602/*!
603\fn template<typename T> bool QQmlListProperty<T>::operator==(const QQmlListProperty &other) const
604
605Returns true if this QQmlListProperty is equal to \a other, otherwise false.
606*/
607
608/*!
609\typedef QQmlListProperty::AtFunction
610
611Synonym for \c {T *(*)(QQmlListProperty<T> *property, qsizetype index)}.
612
613Return the element at position \a index in the list \a property.
614*/
615
616/*!
617\typedef QQmlListProperty::ClearFunction
618
619Synonym for \c {void (*)(QQmlListProperty<T> *property)}.
620
621Clear the list \a property.
622*/
623
624/*!
625\typedef QQmlListProperty::ReplaceFunction
626
627Synonym for \c {void (*)(QQmlListProperty<T> *property, qsizetype index, T *value)}.
628
629Replace the element at position \a index in the list \a property with \a value.
630*/
631
632/*!
633\typedef QQmlListProperty::RemoveLastFunction
634
635Synonym for \c {void (*)(QQmlListProperty<T> *property)}.
636
637Remove the last element from the list \a property.
638*/
639
640/*!
641\fn bool QQmlListReference::operator==(const QQmlListReference &other) const
642
643Compares this QQmlListReference to \a other, and returns \c true if they are
644equal. The two are only considered equal if one was created from the other
645via copy assignment or copy construction.
646
647\note Independently created references to the same object are not considered
648to be equal.
649*/
650
651QT_END_NAMESPACE
652

source code of qtdeclarative/src/qml/qml/qqmllist.cpp