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 | |
8 | QT_BEGIN_NAMESPACE |
9 | |
10 | static 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 | |
20 | QQmlListReferencePrivate::QQmlListReferencePrivate() |
21 | : refCount(1) |
22 | { |
23 | } |
24 | |
25 | QQmlListReference 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 | |
40 | void QQmlListReferencePrivate::addref() |
41 | { |
42 | Q_ASSERT(refCount > 0); |
43 | ++refCount; |
44 | } |
45 | |
46 | void 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 | |
60 | QQmlListReference allows C++ programs to read from, and assign values to a QML list property in a |
61 | simple and type-safe way. A QQmlListReference can be created by passing an object and property |
62 | name or through a QQmlProperty instance. These two are equivalent: |
63 | |
64 | \code |
65 | QQmlListReference ref1(object, "children"); |
66 | |
67 | QQmlProperty ref2(object, "children"); |
68 | QQmlListReference ref2 = qvariant_cast<QQmlListReference>(ref2.read()); |
69 | \endcode |
70 | |
71 | Not all QML list properties support all operations. A set of methods, canAppend(), canAt(), canClear() and |
72 | canCount() allow programs to query whether an operation is supported on a given property. |
73 | |
74 | QML list properties are type-safe. Only QObject's that derive from the correct base class can be assigned to |
75 | the list. The listElementType() method can be used to query the QMetaObject of the QObject type supported. |
76 | Attempting to add objects of the incorrect type to a list property will fail. |
77 | |
78 | Like with normal lists, when accessing a list element by index, it is the callers responsibility to ensure |
79 | that it does not request an out of range element using the count() method before calling at(). |
80 | */ |
81 | |
82 | /*! |
83 | Constructs an invalid instance. |
84 | */ |
85 | QQmlListReference::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 | |
95 | Constructs 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 |
97 | owning the list property is destroyed after the reference is constructed, it will automatically |
98 | become invalid. That is, it is safe to hold QQmlListReference instances even after the object is |
99 | deleted. |
100 | |
101 | The \a engine is unused. |
102 | */ |
103 | QQmlListReference::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 | |
110 | Constructs a QQmlListReference for \a object's \a property. If \a property is not a list |
111 | property, an invalid QQmlListReference is created. If \a object is destroyed after |
112 | the reference is constructed, it will automatically become invalid. That is, it is safe to hold |
113 | QQmlListReference instances even after \a object is deleted. |
114 | |
115 | The \a engine is unused. |
116 | */ |
117 | QQmlListReference::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 | |
126 | Constructs 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 |
128 | owning the list property is destroyed after the reference is constructed, it will automatically |
129 | become invalid. That is, it is safe to hold QQmlListReference instances even after the object is |
130 | deleted. |
131 | */ |
132 | QQmlListReference::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 | /*! |
149 | Constructs a QQmlListReference for \a object's \a property. If \a property is not a list |
150 | property, an invalid QQmlListReference is created. If \a object is destroyed after |
151 | the reference is constructed, it will automatically become invalid. That is, it is safe to hold |
152 | QQmlListReference instances even after \a object is deleted. |
153 | */ |
154 | QQmlListReference::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 */ |
174 | QQmlListReference::QQmlListReference(const QQmlListReference &o) |
175 | : d(o.d) |
176 | { |
177 | if (d) d->addref(); |
178 | } |
179 | |
180 | /*! \internal */ |
181 | QQmlListReference &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 */ |
190 | QQmlListReference::~QQmlListReference() |
191 | { |
192 | if (d) d->release(); |
193 | } |
194 | |
195 | /*! |
196 | Returns true if the instance refers to a valid list property, otherwise false. |
197 | */ |
198 | bool QQmlListReference::isValid() const |
199 | { |
200 | return d && d->object; |
201 | } |
202 | |
203 | /*! |
204 | Returns the list property's object. Returns \nullptr if the reference is invalid. |
205 | */ |
206 | QObject *QQmlListReference::object() const |
207 | { |
208 | if (isValid()) return d->object; |
209 | else return nullptr; |
210 | } |
211 | |
212 | /*! |
213 | Returns the QMetaObject for the elements stored in the list property, |
214 | or \nullptr if the reference is invalid. |
215 | |
216 | The QMetaObject can be used ahead of time to determine whether a given instance can be added |
217 | to a list. If you didn't pass an engine on construction this may return nullptr. |
218 | */ |
219 | const QMetaObject *QQmlListReference::listElementType() const |
220 | { |
221 | return isValid() ? d->elementType() : nullptr; |
222 | } |
223 | |
224 | /*! |
225 | Returns true if the list property can be appended to, otherwise false. Returns false if the |
226 | reference is invalid. |
227 | |
228 | \sa append() |
229 | */ |
230 | bool QQmlListReference::canAppend() const |
231 | { |
232 | return (isValid() && d->property.append); |
233 | } |
234 | |
235 | /*! |
236 | Returns true if the list property can queried by index, otherwise false. Returns false if the |
237 | reference is invalid. |
238 | |
239 | \sa at() |
240 | */ |
241 | bool QQmlListReference::canAt() const |
242 | { |
243 | return (isValid() && d->property.at); |
244 | } |
245 | |
246 | /*! |
247 | Returns true if the list property can be cleared, otherwise false. Returns false if the |
248 | reference is invalid. |
249 | |
250 | \sa clear() |
251 | */ |
252 | bool QQmlListReference::canClear() const |
253 | { |
254 | return (isValid() && d->property.clear); |
255 | } |
256 | |
257 | /*! |
258 | Returns true if the list property can be queried for its element count, otherwise false. |
259 | Returns false if the reference is invalid. |
260 | |
261 | \sa count() |
262 | */ |
263 | bool QQmlListReference::canCount() const |
264 | { |
265 | return (isValid() && d->property.count); |
266 | } |
267 | |
268 | /*! |
269 | Returns true if items in the list property can be replaced, otherwise false. |
270 | Returns false if the reference is invalid. |
271 | |
272 | \sa replace() |
273 | */ |
274 | bool QQmlListReference::canReplace() const |
275 | { |
276 | return (isValid() && d->property.replace); |
277 | } |
278 | |
279 | /*! |
280 | Returns true if the last item can be removed from the list property, otherwise false. |
281 | Returns false if the reference is invalid. |
282 | |
283 | \sa removeLast() |
284 | */ |
285 | bool 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 | */ |
301 | bool 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 | */ |
316 | bool QQmlListReference::isReadable() const |
317 | { |
318 | return (isValid() && d->property.count && d->property.at); |
319 | } |
320 | |
321 | /*! |
322 | Appends \a object to the list. Returns true if the operation succeeded, otherwise false. |
323 | |
324 | \sa canAppend() |
325 | */ |
326 | bool 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 | /*! |
339 | Returns the list element at \a index, or 0 if the operation failed. |
340 | |
341 | \sa canAt() |
342 | */ |
343 | QObject *QQmlListReference::at(qsizetype index) const |
344 | { |
345 | if (!canAt()) return nullptr; |
346 | |
347 | return d->property.at(&d->property, index); |
348 | } |
349 | |
350 | /*! |
351 | Clears the list. Returns true if the operation succeeded, otherwise false. |
352 | |
353 | \sa canClear() |
354 | */ |
355 | bool QQmlListReference::clear() const |
356 | { |
357 | if (!canClear()) return false; |
358 | |
359 | d->property.clear(&d->property); |
360 | |
361 | return true; |
362 | } |
363 | |
364 | /*! |
365 | Returns the number of objects in the list, or 0 if the operation failed. |
366 | */ |
367 | qsizetype 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 |
377 | Returns the number of objects in the list, or 0 if the operation failed. |
378 | */ |
379 | |
380 | /*! |
381 | Replaces the item at \a index in the list with \a object. |
382 | Returns true if the operation succeeded, otherwise false. |
383 | |
384 | \sa canReplace() |
385 | */ |
386 | bool 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 | /*! |
399 | Removes the last item in the list. |
400 | Returns true if the operation succeeded, otherwise false. |
401 | |
402 | \sa canRemoveLast() |
403 | */ |
404 | bool 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 |
418 | properties of QObject-derived classes to QML. |
419 | |
420 | QML has many list properties, where more than one object value can be assigned. |
421 | The use of a list property from QML looks like this: |
422 | |
423 | \code |
424 | FruitBasket { |
425 | fruit: [ |
426 | Apple {}, |
427 | Orange{}, |
428 | Banana{} |
429 | ] |
430 | } |
431 | \endcode |
432 | |
433 | The QQmlListProperty encapsulates a group of function pointers that represent the |
434 | set of actions QML can perform on the list - adding items, retrieving items and |
435 | clearing the list. In the future, additional operations may be supported. All |
436 | list properties must implement the append operation, but the rest are optional. |
437 | |
438 | To provide a list property, a C++ class must implement the operation callbacks, |
439 | and then return an appropriate QQmlListProperty value from the property getter. |
440 | List properties should have no setter. In the example above, the Q_PROPERTY() |
441 | declarative will look like this: |
442 | |
443 | \code |
444 | Q_PROPERTY(QQmlListProperty<Fruit> fruit READ fruit) |
445 | \endcode |
446 | |
447 | QML 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 | |
509 | Convenience constructor for making a QQmlListProperty value from an existing |
510 | QList \a list. The \a list reference must remain valid for as long as \a object |
511 | exists. \a object must be provided. |
512 | |
513 | This constructor synthesizes the removeLast() and replace() methods |
514 | introduced in Qt 5.15, using count(), at(), clear(), and append(). This is slow. |
515 | If you intend to manipulate the list beyond clearing it, you should explicitly |
516 | provide these methods. |
517 | */ |
518 | |
519 | /*! |
520 | \fn template<typename T> QQmlListProperty<T>::QQmlListProperty(QObject *object, QList<T *> *list) |
521 | \since 5.15 |
522 | |
523 | Convenience constructor for making a QQmlListProperty value from an existing |
524 | QList \a list. The \a list reference must remain valid for as long as \a object |
525 | exists. \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 | |
532 | Construct 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 |
534 | accessed from within the operation functions. The list property |
535 | remains 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 | |
543 | Construct 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 |
545 | can be accessed from within the operation functions. The list property |
546 | remains valid while \a object exists. |
547 | |
548 | Null pointers can be passed for any function. If any null pointers are passed in, the list |
549 | will be neither designable nor alterable by the debugger. It is recommended to provide valid |
550 | pointers for all functions. |
551 | |
552 | \note The resulting QQmlListProperty will synthesize the removeLast() and |
553 | replace() methods using \a count, \a at, \a clear, and \a append if all of those |
554 | are given. This is slow. If you intend to manipulate the list beyond clearing it, |
555 | you 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 | |
564 | Construct 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 |
566 | may be passed which can be accessed from within the operation functions. The |
567 | list property remains valid while \a object exists. |
568 | |
569 | Null pointers can be passed for any function, causing the respective function to |
570 | be 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 |
577 | if those are given. This is slow, but if your list does not natively provide |
578 | faster options for these primitives, you may want to use the synthesized ones. |
579 | |
580 | Furthermore, if either of \a count, \a at, \a append, and \a clear are neither |
581 | given explicitly nor synthesized, the list will be neither designable nor |
582 | alterable by the debugger. It is recommended to provide enough valid pointers |
583 | to avoid this situation. |
584 | */ |
585 | |
586 | /*! |
587 | \typedef QQmlListProperty::AppendFunction |
588 | |
589 | Synonym for \c {void (*)(QQmlListProperty<T> *property, T *value)}. |
590 | |
591 | Append the \a value to the list \a property. |
592 | */ |
593 | |
594 | /*! |
595 | \typedef QQmlListProperty::CountFunction |
596 | |
597 | Synonym for \c {qsizetype (*)(QQmlListProperty<T> *property)}. |
598 | |
599 | Return 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 | |
605 | Returns true if this QQmlListProperty is equal to \a other, otherwise false. |
606 | */ |
607 | |
608 | /*! |
609 | \typedef QQmlListProperty::AtFunction |
610 | |
611 | Synonym for \c {T *(*)(QQmlListProperty<T> *property, qsizetype index)}. |
612 | |
613 | Return the element at position \a index in the list \a property. |
614 | */ |
615 | |
616 | /*! |
617 | \typedef QQmlListProperty::ClearFunction |
618 | |
619 | Synonym for \c {void (*)(QQmlListProperty<T> *property)}. |
620 | |
621 | Clear the list \a property. |
622 | */ |
623 | |
624 | /*! |
625 | \typedef QQmlListProperty::ReplaceFunction |
626 | |
627 | Synonym for \c {void (*)(QQmlListProperty<T> *property, qsizetype index, T *value)}. |
628 | |
629 | Replace the element at position \a index in the list \a property with \a value. |
630 | */ |
631 | |
632 | /*! |
633 | \typedef QQmlListProperty::RemoveLastFunction |
634 | |
635 | Synonym for \c {void (*)(QQmlListProperty<T> *property)}. |
636 | |
637 | Remove the last element from the list \a property. |
638 | */ |
639 | |
640 | /*! |
641 | \fn bool QQmlListReference::operator==(const QQmlListReference &other) const |
642 | |
643 | Compares this QQmlListReference to \a other, and returns \c true if they are |
644 | equal. The two are only considered equal if one was created from the other |
645 | via copy assignment or copy construction. |
646 | |
647 | \note Independently created references to the same object are not considered |
648 | to be equal. |
649 | */ |
650 | |
651 | QT_END_NAMESPACE |
652 | |