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
qqmldelegatemodel.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant
4
6
7#include <QtQml/qqmlinfo.h>
8
9#include <private/qabstractitemmodel_p.h>
10#include <private/qcoreapplication_p.h>
11#include <private/qjsvalue_p.h>
12#include <private/qmetaobjectbuilder_p.h>
13#include <private/qqmlabstractdelegatecomponent_p.h>
14#include <private/qqmladaptormodel_p.h>
15#include <private/qqmlanybinding_p.h>
16#include <private/qqmlchangeset_p.h>
17#include <private/qqmlcomponent_p.h>
18#include <private/qqmlengine_p.h>
19#include <private/qqmlpropertytopropertybinding_p.h>
20#include <private/qqmltableinstancemodel_p.h>
21#include <private/qquickpackage_p.h>
22#include <private/qv4functionobject_p.h>
23#include <private/qv4objectiterator_p.h>
24#include <private/qv4value_p.h>
25
27
28Q_LOGGING_CATEGORY(lcItemViewDelegateRecycling, "qt.qml.delegatemodel.recycling")
29
30class QQmlDelegateModelItem;
31
32namespace QV4 {
33
34namespace Heap {
35
37 void init(ExecutionEngine *engine, uint flag, QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg));
38
39 QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg);
41};
42
44 void init() { Object::init(); }
45
47};
48
50 void init(const QVector<QQmlChangeSet::Change> &changes);
51 void destroy() {
52 delete changes;
53 Object::destroy();
54 }
55
57};
58
59
60}
61
63{
64 V4_OBJECT2(DelegateModelGroupFunction, FunctionObject)
65
72
74 {
78 if (!o)
79 return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
80
82 return f->d()->code(o->d()->item, f->d()->flag, v);
83 }
84};
85
87 QV4::ExecutionEngine *engine, uint flag,
88 QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg))
89{
90 QV4::Heap::FunctionObject::init(engine, QStringLiteral("DelegateModelGroupFunction"));
91 this->flag = flag;
92 this->code = code;
93}
94
95}
96
98
99
100
102{
103public:
104 QQmlDelegateModelEngineData(QV4::ExecutionEngine *v4);
106
107 QV4::ReturnedValue array(QV4::ExecutionEngine *engine,
108 const QVector<QQmlChangeSet::Change> &changes);
109
111};
112
113V4_DEFINE_EXTENSION(QQmlDelegateModelEngineData, qdmEngineData)
114
115
116void QQmlDelegateModelPartsMetaObject::propertyCreated(int, QMetaPropertyBuilder &prop)
117{
118 prop.setWritable(false);
119}
120
122{
123 QQmlDelegateModelParts *parts = static_cast<QQmlDelegateModelParts *>(object());
124 QQmlPartsModel *m = new QQmlPartsModel(
125 parts->model, QString::fromUtf8(name(id)), parts);
126 parts->models.append(m);
127 return QVariant::fromValue(static_cast<QObject *>(m));
128}
129
130QQmlDelegateModelParts::QQmlDelegateModelParts(QQmlDelegateModel *parent)
131: QObject(parent), model(parent)
132{
133 new QQmlDelegateModelPartsMetaObject(this);
134}
135
136//---------------------------------------------------------------------------
137
138/*!
139 \qmltype DelegateModel
140//! \nativetype QQmlDelegateModel
141 \inqmlmodule QtQml.Models
142 \brief Encapsulates a model and delegate.
143
144 The DelegateModel type encapsulates a model and the delegate that will
145 be instantiated for items in the model.
146
147 It is usually not necessary to create a DelegateModel.
148 However, it can be useful for manipulating and accessing the \l modelIndex
149 when a QAbstractItemModel subclass is used as the
150 model. Also, DelegateModel is used together with \l Package to
151 provide delegates to multiple views, and with DelegateModelGroup to sort and filter
152 delegate items.
153
154 DelegateModel only supports one-dimensional models -- assigning a table model to
155 DelegateModel and that to TableView will thus only show one column.
156
157 The example below illustrates using a DelegateModel with a ListView.
158
159 \snippet delegatemodel/delegatemodel.qml 0
160*/
161
162QQmlDelegateModelPrivate::QQmlDelegateModelPrivate(QQmlContext *ctxt)
163 : m_delegateChooser(nullptr)
164 , m_context(ctxt)
165 , m_parts(nullptr)
166 , m_filterGroup(QStringLiteral("items"))
167 , m_count(0)
168 , m_groupCount(Compositor::MinimumGroupCount)
169 , m_compositorGroup(Compositor::Cache)
170 , m_complete(false)
171 , m_delegateValidated(false)
172 , m_reset(false)
173 , m_transaction(false)
175 , m_waitingToFetchMore(false)
176 , m_cacheItems(nullptr)
177 , m_items(nullptr)
178 , m_persistedItems(nullptr)
179{
180}
181
183{
184 qDeleteAll(m_finishedIncubating);
185
186 // Free up all items in the pool
188}
189
191{
192 // QQmlDelegateModel currently only support list models.
193 // So even if a model is a table model, only the first
194 // column will be used.
195 return m_adaptorModel.rowCount();
196}
197
199{
200 Q_Q(QQmlDelegateModel);
201 if (!m_waitingToFetchMore && m_adaptorModel.canFetchMore()) {
203 QCoreApplication::postEvent(q, new QEvent(QEvent::UpdateRequest));
204 }
205}
206
208{
209 Q_Q(QQmlDelegateModel);
210 m_compositor.setRemoveGroups(Compositor::GroupMask & ~Compositor::PersistedFlag);
211
212 m_items = new QQmlDelegateModelGroup(QStringLiteral("items"), q, Compositor::Default, q);
213 m_items->setDefaultInclude(true);
214 m_persistedItems = new QQmlDelegateModelGroup(QStringLiteral("persistedItems"), q, Compositor::Persisted, q);
215 QQmlDelegateModelGroupPrivate::get(m_items)->emitters.insert(this);
216}
217
218QQmlDelegateModel::QQmlDelegateModel()
219 : QQmlDelegateModel(nullptr, nullptr)
220{
221}
222
223QQmlDelegateModel::QQmlDelegateModel(QQmlContext *ctxt, QObject *parent)
224: QQmlInstanceModel(*(new QQmlDelegateModelPrivate(ctxt)), parent)
225{
226 Q_D(QQmlDelegateModel);
227 d->init();
228}
229
230QQmlDelegateModel::~QQmlDelegateModel()
231{
232 Q_D(QQmlDelegateModel);
233 d->disconnectFromAbstractItemModel();
234 d->m_adaptorModel.setObject(nullptr);
235
236 for (QQmlDelegateModelItem *cacheItem : std::as_const(d->m_cache)) {
237 if (cacheItem->object) {
238 delete cacheItem->object;
239
240 cacheItem->object = nullptr;
241 cacheItem->contextData.reset();
242 cacheItem->scriptRef -= 1;
243 } else if (cacheItem->incubationTask) {
244 // Both the incubationTask and the object may hold a scriptRef,
245 // but if both are present, only one scriptRef is held in total.
246 cacheItem->scriptRef -= 1;
247 }
248
249 cacheItem->groups &= ~Compositor::UnresolvedFlag;
250 cacheItem->objectRef = 0;
251
252 if (cacheItem->incubationTask) {
253 d->releaseIncubator(cacheItem->incubationTask);
254 cacheItem->incubationTask->vdm = nullptr;
255 cacheItem->incubationTask = nullptr;
256 }
257
258 if (!cacheItem->isReferenced())
259 delete cacheItem;
260 }
261}
262
263
264void QQmlDelegateModel::classBegin()
265{
266 Q_D(QQmlDelegateModel);
267 if (!d->m_context)
268 d->m_context = qmlContext(this);
269}
270
271void QQmlDelegateModel::componentComplete()
272{
273 Q_D(QQmlDelegateModel);
274 d->m_complete = true;
275
276 int defaultGroups = 0;
277 QStringList groupNames;
278 groupNames.append(QStringLiteral("items"));
279 groupNames.append(QStringLiteral("persistedItems"));
280 if (QQmlDelegateModelGroupPrivate::get(d->m_items)->defaultInclude)
281 defaultGroups |= Compositor::DefaultFlag;
282 if (QQmlDelegateModelGroupPrivate::get(d->m_persistedItems)->defaultInclude)
283 defaultGroups |= Compositor::PersistedFlag;
284 for (int i = Compositor::MinimumGroupCount; i < d->m_groupCount; ++i) {
285 QString name = d->m_groups[i]->name();
286 if (name.isEmpty()) {
287 d->m_groups[i] = d->m_groups[d->m_groupCount - 1];
288 --d->m_groupCount;
289 --i;
290 } else if (name.at(0).isUpper()) {
291 qmlWarning(d->m_groups[i]) << QQmlDelegateModelGroup::tr("Group names must start with a lower case letter");
292 d->m_groups[i] = d->m_groups[d->m_groupCount - 1];
293 --d->m_groupCount;
294 --i;
295 } else {
296 groupNames.append(name);
297
298 QQmlDelegateModelGroupPrivate *group = QQmlDelegateModelGroupPrivate::get(d->m_groups[i]);
299 group->setModel(this, Compositor::Group(i));
300 if (group->defaultInclude)
301 defaultGroups |= (1 << i);
302 }
303 }
304
305 d->m_cacheMetaType = QQml::makeRefPointer<QQmlDelegateModelItemMetaType>(
306 d->m_context->engine()->handle(), this, groupNames);
307
308 d->m_compositor.setGroupCount(d->m_groupCount);
309 d->m_compositor.setDefaultGroups(defaultGroups);
310 d->updateFilterGroup();
311
312 while (!d->m_pendingParts.isEmpty())
313 static_cast<QQmlPartsModel *>(d->m_pendingParts.first())->updateFilterGroup();
314
315 QVector<Compositor::Insert> inserts;
316 d->m_count = d->adaptorModelCount();
317 d->m_compositor.append(
318 &d->m_adaptorModel,
319 0,
320 d->m_count,
321 defaultGroups | Compositor::AppendFlag | Compositor::PrependFlag,
322 &inserts);
323 d->itemsInserted(inserts);
324 d->emitChanges();
325 d->requestMoreIfNecessary();
326}
327
328/*!
329 \qmlproperty model QtQml.Models::DelegateModel::model
330 This property holds the model providing data for the DelegateModel.
331
332 The model provides a set of data that is used to create the items
333 for a view. For large or dynamic datasets the model is usually
334 provided by a C++ model object. The C++ model object must be a \l
335 {QAbstractItemModel} subclass or a simple list.
336
337 Models can also be created directly in QML, for example using
338 ListModel.
339
340 \sa {qml-data-models}{Data Models}
341 \keyword dm-model-property
342*/
343QVariant QQmlDelegateModel::model() const
344{
345 Q_D(const QQmlDelegateModel);
346 return d->m_adaptorModel.model();
347}
348
350{
351 Q_Q(QQmlDelegateModel);
352 if (!m_adaptorModel.adaptsAim())
353 return;
354
355 auto aim = m_adaptorModel.aim();
356
357 QObject::connect(aim, &QAbstractItemModel::rowsInserted, q, &QQmlDelegateModel::_q_rowsInserted);
358 QObject::connect(aim, &QAbstractItemModel::rowsRemoved, q, &QQmlDelegateModel::_q_rowsRemoved);
359 QObject::connect(aim, &QAbstractItemModel::rowsAboutToBeRemoved, q, &QQmlDelegateModel::_q_rowsAboutToBeRemoved);
360 QObject::connect(aim, &QAbstractItemModel::columnsInserted, q, &QQmlDelegateModel::_q_columnsInserted);
361 QObject::connect(aim, &QAbstractItemModel::columnsRemoved, q, &QQmlDelegateModel::_q_columnsRemoved);
362 QObject::connect(aim, &QAbstractItemModel::columnsMoved, q, &QQmlDelegateModel::_q_columnsMoved);
363 QObject::connect(aim, &QAbstractItemModel::dataChanged, q, &QQmlDelegateModel::_q_dataChanged);
364 QObject::connect(aim, &QAbstractItemModel::rowsMoved, q, &QQmlDelegateModel::_q_rowsMoved);
365 QObject::connect(aim, &QAbstractItemModel::modelAboutToBeReset, q, &QQmlDelegateModel::_q_modelAboutToBeReset);
366 QObject::connect(aim, &QAbstractItemModel::layoutChanged, q, &QQmlDelegateModel::_q_layoutChanged);
367}
368
370{
371 Q_Q(QQmlDelegateModel);
372 if (!m_adaptorModel.adaptsAim())
373 return;
374
375 auto aim = m_adaptorModel.aim();
376
377 QObject::disconnect(aim, &QAbstractItemModel::rowsInserted, q, &QQmlDelegateModel::_q_rowsInserted);
378 QObject::disconnect(aim, &QAbstractItemModel::rowsAboutToBeRemoved, q, &QQmlDelegateModel::_q_rowsAboutToBeRemoved);
379 QObject::disconnect(aim, &QAbstractItemModel::rowsRemoved, q, &QQmlDelegateModel::_q_rowsRemoved);
380 QObject::disconnect(aim, &QAbstractItemModel::columnsInserted, q, &QQmlDelegateModel::_q_columnsInserted);
381 QObject::disconnect(aim, &QAbstractItemModel::columnsRemoved, q, &QQmlDelegateModel::_q_columnsRemoved);
382 QObject::disconnect(aim, &QAbstractItemModel::columnsMoved, q, &QQmlDelegateModel::_q_columnsMoved);
383 QObject::disconnect(aim, &QAbstractItemModel::dataChanged, q, &QQmlDelegateModel::_q_dataChanged);
384 QObject::disconnect(aim, &QAbstractItemModel::rowsMoved, q, &QQmlDelegateModel::_q_rowsMoved);
385 QObject::disconnect(aim, &QAbstractItemModel::modelAboutToBeReset, q, &QQmlDelegateModel::_q_modelAboutToBeReset);
386 QObject::disconnect(aim, &QAbstractItemModel::layoutChanged, q, &QQmlDelegateModel::_q_layoutChanged);
387}
388
389void QQmlDelegateModel::setModel(const QVariant &model)
390{
391 Q_D(QQmlDelegateModel);
392
393 if (d->m_adaptorModel.model() == model)
394 return;
395
396 if (d->m_complete)
397 _q_itemsRemoved(0, d->m_count);
398
399 d->disconnectFromAbstractItemModel();
400 d->m_adaptorModel.setModel(model);
401 d->connectToAbstractItemModel();
402
403 d->m_adaptorModel.replaceWatchedRoles(QList<QByteArray>(), d->m_watchedRoles);
404 for (int i = 0; d->m_parts && i < d->m_parts->models.size(); ++i) {
405 d->m_adaptorModel.replaceWatchedRoles(
406 QList<QByteArray>(), d->m_parts->models.at(i)->watchedRoles());
407 }
408
409 if (d->m_complete) {
410 _q_itemsInserted(0, d->adaptorModelCount());
411 d->requestMoreIfNecessary();
412 }
413
414 // Since 837c2f18cd223707e7cedb213257b0158ea07146, we connect to modelAboutToBeReset
415 // rather than modelReset so that we can handle role name changes. _q_modelAboutToBeReset
416 // now connects modelReset to handleModelReset with a single shot connection instead.
417 // However, it's possible for user code to begin the reset before connectToAbstractItemModel is called
418 // (QTBUG-125053), in which case we connect to modelReset too late and handleModelReset is never called,
419 // resulting in delegates not being created in certain cases.
420 // So, we check at the earliest point we can if the model is in the process of being reset,
421 // and if so, connect modelReset to handleModelReset.
422 if (d->m_adaptorModel.adaptsAim()) {
423 auto *aim = d->m_adaptorModel.aim();
424 auto *aimPrivate = QAbstractItemModelPrivate::get(aim);
425 if (aimPrivate->resetting)
426 QObject::connect(aim, &QAbstractItemModel::modelReset, this, &QQmlDelegateModel::handleModelReset, Qt::SingleShotConnection);
427 }
428
429 emit modelChanged();
430}
431
432/*!
433 \qmlproperty Component QtQml.Models::DelegateModel::delegate
434
435 The delegate provides a template defining each item instantiated by a view.
436 The index is exposed as an accessible \c index property. Properties of the
437 model are also available depending upon the type of \l {qml-data-models}{Data Model}.
438*/
439QQmlComponent *QQmlDelegateModel::delegate() const
440{
441 Q_D(const QQmlDelegateModel);
442 return d->m_delegate;
443}
444
445void QQmlDelegateModel::setDelegate(QQmlComponent *delegate)
446{
447 Q_D(QQmlDelegateModel);
448 if (d->m_transaction) {
449 qmlWarning(this) << tr("The delegate of a DelegateModel cannot be changed within onUpdated.");
450 return;
451 }
452 if (d->m_delegate == delegate)
453 return;
454 if (d->m_complete)
455 _q_itemsRemoved(0, d->m_count);
456 d->m_delegate.setObject(delegate, this);
457 d->m_delegateValidated = false;
458 if (d->m_delegateChooser)
459 QObject::disconnect(d->m_delegateChooserChanged);
460
461 d->m_delegateChooser = nullptr;
462 if (delegate) {
463 QQmlAbstractDelegateComponent *adc =
464 qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
465 if (adc) {
466 d->m_delegateChooser = adc;
467 d->m_delegateChooserChanged = connect(adc, &QQmlAbstractDelegateComponent::delegateChanged, this,
468 [d](){ d->delegateChanged(); });
469 }
470 }
471 if (d->m_complete) {
472 _q_itemsInserted(0, d->adaptorModelCount());
473 d->requestMoreIfNecessary();
474 }
475 emit delegateChanged();
476}
477
478/*!
479 \qmlproperty QModelIndex QtQml.Models::DelegateModel::rootIndex
480
481 QAbstractItemModel provides a hierarchical tree of data, whereas
482 QML only operates on list data. \c rootIndex allows the children of
483 any node in a QAbstractItemModel to be provided by this model.
484
485 This property only affects models of type QAbstractItemModel that
486 are hierarchical (e.g, a tree model).
487
488 For example, here is a simple interactive file system browser.
489 When a directory name is clicked, the view's \c rootIndex is set to the
490 QModelIndex node of the clicked directory, thus updating the view to show
491 the new directory's contents.
492
493 \c main.cpp:
494 \snippet delegatemodel/delegatemodel_rootindex/main.cpp 0
495
496 \c view.qml:
497 \snippet delegatemodel/delegatemodel_rootindex/view.qml 0
498
499 If the \l {dm-model-property}{model} is a QAbstractItemModel subclass,
500 the delegate can also reference a \c hasModelChildren property (optionally
501 qualified by a \e model. prefix) that indicates whether the delegate's
502 model item has any child nodes.
503
504 \sa modelIndex(), parentModelIndex()
505*/
506QVariant QQmlDelegateModel::rootIndex() const
507{
508 Q_D(const QQmlDelegateModel);
509 return QVariant::fromValue(QModelIndex(d->m_adaptorModel.rootIndex));
510}
511
512void QQmlDelegateModel::setRootIndex(const QVariant &root)
513{
514 Q_D(QQmlDelegateModel);
515
516 QModelIndex modelIndex = qvariant_cast<QModelIndex>(root);
517 const bool changed = d->m_adaptorModel.rootIndex != modelIndex;
518 if (changed || !d->m_adaptorModel.isValid()) {
519 const int oldCount = d->m_count;
520 d->m_adaptorModel.rootIndex = modelIndex;
521 if (!d->m_adaptorModel.isValid() && d->m_adaptorModel.aim()) {
522 // The previous root index was invalidated, so we need to reconnect the model.
523 d->disconnectFromAbstractItemModel();
524 d->m_adaptorModel.setModel(d->m_adaptorModel.list.list());
525 d->connectToAbstractItemModel();
526 }
527 if (d->m_adaptorModel.canFetchMore())
528 d->m_adaptorModel.fetchMore();
529 if (d->m_complete) {
530 const int newCount = d->adaptorModelCount();
531 if (oldCount)
532 _q_itemsRemoved(0, oldCount);
533 if (newCount)
534 _q_itemsInserted(0, newCount);
535 }
536 if (changed)
537 emit rootIndexChanged();
538 }
539}
540
541/*!
542 \qmlproperty enumeration QtQml.Models::DelegateModel::delegateModelAccess
543 \since 6.10
544
545 \include delegatemodelaccess.qdocinc
546*/
547QQmlDelegateModel::DelegateModelAccess QQmlDelegateModel::delegateModelAccess() const
548{
549 Q_D(const QQmlDelegateModel);
550 return d->m_adaptorModel.delegateModelAccess;
551}
552
553void QQmlDelegateModel::setDelegateModelAccess(
554 QQmlDelegateModel::DelegateModelAccess delegateModelAccess)
555{
556 Q_D(QQmlDelegateModel);
557 if (d->m_adaptorModel.delegateModelAccess == delegateModelAccess)
558 return;
559
560 if (d->m_transaction) {
561 qmlWarning(this) << tr("The delegateModelAccess of a DelegateModel "
562 "cannot be changed within onUpdated.");
563 return;
564 }
565
566 if (d->m_complete) {
567 _q_itemsRemoved(0, d->m_count);
568 d->m_adaptorModel.delegateModelAccess = delegateModelAccess;
569 _q_itemsInserted(0, d->adaptorModelCount());
570 d->requestMoreIfNecessary();
571 } else {
572 d->m_adaptorModel.delegateModelAccess = delegateModelAccess;
573 }
574
575 emit delegateModelAccessChanged();
576}
577
578/*!
579 \qmlmethod QModelIndex QtQml.Models::DelegateModel::modelIndex(int index)
580
581 QAbstractItemModel provides a hierarchical tree of data, whereas
582 QML only operates on list data. This function assists in using
583 tree models in QML.
584
585 Returns a QModelIndex for the specified \a index.
586 This value can be assigned to rootIndex.
587
588 \sa rootIndex
589*/
590QVariant QQmlDelegateModel::modelIndex(int idx) const
591{
592 Q_D(const QQmlDelegateModel);
593 return d->m_adaptorModel.modelIndex(idx);
594}
595
596/*!
597 \qmlmethod QModelIndex QtQml.Models::DelegateModel::parentModelIndex()
598
599 QAbstractItemModel provides a hierarchical tree of data, whereas
600 QML only operates on list data. This function assists in using
601 tree models in QML.
602
603 Returns a QModelIndex for the parent of the current rootIndex.
604 This value can be assigned to rootIndex.
605
606 \sa rootIndex
607*/
608QVariant QQmlDelegateModel::parentModelIndex() const
609{
610 Q_D(const QQmlDelegateModel);
611 return d->m_adaptorModel.parentModelIndex();
612}
613
614/*!
615 \qmlproperty int QtQml.Models::DelegateModel::count
616*/
617
618int QQmlDelegateModel::count() const
619{
620 Q_D(const QQmlDelegateModel);
621 if (!d->m_delegate)
622 return 0;
623 return d->m_compositor.count(d->m_compositorGroup);
624}
625
626QQmlDelegateModel::ReleaseFlags QQmlDelegateModelPrivate::release(QObject *object, QQmlInstanceModel::ReusableFlag reusableFlag)
627{
628 if (!object)
629 return QQmlDelegateModel::ReleaseFlags{};
630
631 QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(object);
632 if (!cacheItem)
633 return QQmlDelegateModel::ReleaseFlags{};
634
635 if (!cacheItem->releaseObject())
636 return QQmlDelegateModel::Referenced;
637
638 if (reusableFlag == QQmlInstanceModel::Reusable && m_reusableItemsPool.insertItem(cacheItem)) {
639 removeCacheItem(cacheItem);
640 emit q_func()->itemPooled(cacheItem->modelIndex(), cacheItem->object);
641 return QQmlInstanceModel::Pooled;
642 }
643
644 destroyCacheItem(cacheItem);
645 return QQmlInstanceModel::Destroyed;
646}
647
649{
650 if (QObject *object = cacheItem->object) {
651 emitDestroyingItem(object);
652 cacheItem->destroyObject();
653 }
654
655 if (cacheItem->incubationTask) {
657 cacheItem->incubationTask = nullptr;
658 }
659 cacheItem->dispose();
660}
661
662/*
663 Returns ReleaseStatus flags.
664*/
665QQmlDelegateModel::ReleaseFlags QQmlDelegateModel::release(QObject *item, QQmlInstanceModel::ReusableFlag reusableFlag)
666{
667 Q_D(QQmlDelegateModel);
668 QQmlInstanceModel::ReleaseFlags stat = d->release(item, reusableFlag);
669 return stat;
670}
671
672// Cancel a requested async item
673void QQmlDelegateModel::cancel(int index)
674{
675 Q_D(QQmlDelegateModel);
676 if (index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) {
677 qWarning() << "DelegateModel::cancel: index out range" << index << d->m_compositor.count(d->m_compositorGroup);
678 return;
679 }
680
681 Compositor::iterator it = d->m_compositor.find(d->m_compositorGroup, index);
682 QQmlDelegateModelItem *cacheItem = it->inCache() ? d->m_cache.at(it.cacheIndex()) : 0;
683 if (cacheItem) {
684 if (cacheItem->incubationTask && !cacheItem->isObjectReferenced()) {
685 d->releaseIncubator(cacheItem->incubationTask);
686 cacheItem->incubationTask = nullptr;
687
688 if (cacheItem->object) {
689 QObject *object = cacheItem->object;
690 cacheItem->destroyObject();
691 if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object))
692 d->emitDestroyingPackage(package);
693 else
694 d->emitDestroyingItem(object);
695 }
696
697 cacheItem->scriptRef -= 1;
698 }
699 if (!cacheItem->isReferenced()) {
700 d->m_compositor.clearFlags(
701 Compositor::Cache, it.cacheIndex(), 1, Compositor::CacheFlag);
702 d->m_cache.removeAt(it.cacheIndex());
703 delete cacheItem;
704 Q_ASSERT(d->m_cache.size() == d->m_compositor.count(Compositor::Cache));
705 }
706 }
707}
708
709void QQmlDelegateModelPrivate::group_append(
710 QQmlListProperty<QQmlDelegateModelGroup> *property, QQmlDelegateModelGroup *group)
711{
712 QQmlDelegateModelPrivate *d = static_cast<QQmlDelegateModelPrivate *>(property->data);
713 if (d->m_complete)
714 return;
715 if (d->m_groupCount == Compositor::MaximumGroupCount) {
716 qmlWarning(d->q_func()) << QQmlDelegateModel::tr("The maximum number of supported DelegateModelGroups is 8");
717 return;
718 }
719 d->m_groups[d->m_groupCount] = group;
720 d->m_groupCount += 1;
721}
722
723qsizetype QQmlDelegateModelPrivate::group_count(
724 QQmlListProperty<QQmlDelegateModelGroup> *property)
725{
726 QQmlDelegateModelPrivate *d = static_cast<QQmlDelegateModelPrivate *>(property->data);
727 return d->m_groupCount - 1;
728}
729
730QQmlDelegateModelGroup *QQmlDelegateModelPrivate::group_at(
731 QQmlListProperty<QQmlDelegateModelGroup> *property, qsizetype index)
732{
733 QQmlDelegateModelPrivate *d = static_cast<QQmlDelegateModelPrivate *>(property->data);
734 return index >= 0 && index < d->m_groupCount - 1
735 ? d->m_groups[index + 1]
736 : nullptr;
737}
738
739/*!
740 \qmlproperty list<DelegateModelGroup> QtQml.Models::DelegateModel::groups
741
742 This property holds a delegate model's group definitions.
743
744 Groups define a sub-set of the items in a delegate model and can be used to filter
745 a model.
746
747 For every group defined in a DelegateModel two attached pseudo-properties are added to each
748 delegate item. The first of the form DelegateModel.in\e{GroupName} holds whether the
749 item belongs to the group and the second DelegateModel.\e{groupName}Index holds the
750 index of the item in that group.
751
752 The following example illustrates using groups to select items in a model.
753
754 \snippet delegatemodel/delegatemodelgroup.qml 0
755 \keyword dm-groups-property
756
757
758 \warning In contrast to normal attached properties, those cannot be set in a declarative way.
759 The following would result in an error:
760 \badcode
761 delegate: Rectangle {
762 DelegateModel.inSelected: true
763 }
764 \endcode
765*/
766
767QQmlListProperty<QQmlDelegateModelGroup> QQmlDelegateModel::groups()
768{
769 Q_D(QQmlDelegateModel);
770 return QQmlListProperty<QQmlDelegateModelGroup>(
771 this,
772 d,
773 QQmlDelegateModelPrivate::group_append,
774 QQmlDelegateModelPrivate::group_count,
775 QQmlDelegateModelPrivate::group_at,
776 nullptr, nullptr, nullptr);
777}
778
779/*!
780 \qmlproperty DelegateModelGroup QtQml.Models::DelegateModel::items
781
782 This property holds default group to which all new items are added.
783*/
784
785QQmlDelegateModelGroup *QQmlDelegateModel::items()
786{
787 Q_D(QQmlDelegateModel);
788 return d->m_items;
789}
790
791/*!
792 \qmlproperty DelegateModelGroup QtQml.Models::DelegateModel::persistedItems
793
794 This property holds delegate model's persisted items group.
795
796 Items in this group are not destroyed when released by a view, instead they are persisted
797 until removed from the group.
798
799 An item can be removed from the persistedItems group by setting the
800 DelegateModel.inPersistedItems property to false. If the item is not referenced by a view
801 at that time it will be destroyed. Adding an item to this group will not create a new
802 instance.
803
804 Items returned by the \l QtQml.Models::DelegateModelGroup::create() function are automatically added
805 to this group.
806*/
807
808QQmlDelegateModelGroup *QQmlDelegateModel::persistedItems()
809{
810 Q_D(QQmlDelegateModel);
811 return d->m_persistedItems;
812}
813
814/*!
815 \qmlproperty string QtQml.Models::DelegateModel::filterOnGroup
816
817 This property holds name of the group that is used to filter the delegate model.
818
819 Only items that belong to this group are visible to a view.
820
821 By default this is the \l items group.
822*/
823
824QString QQmlDelegateModel::filterGroup() const
825{
826 Q_D(const QQmlDelegateModel);
827 return d->m_filterGroup;
828}
829
830void QQmlDelegateModel::setFilterGroup(const QString &group)
831{
832 Q_D(QQmlDelegateModel);
833
834 if (d->m_transaction) {
835 qmlWarning(this) << tr("The group of a DelegateModel cannot be changed within onChanged");
836 return;
837 }
838
839 if (d->m_filterGroup != group) {
840 d->m_filterGroup = group;
841 d->updateFilterGroup();
842 emit filterGroupChanged();
843 }
844}
845
846void QQmlDelegateModel::resetFilterGroup()
847{
848 setFilterGroup(QStringLiteral("items"));
849}
850
852{
853 Q_Q(QQmlDelegateModel);
854 if (!m_cacheMetaType)
855 return;
856
857 QQmlListCompositor::Group previousGroup = m_compositorGroup;
858 m_compositorGroup = Compositor::Default;
859 for (int i = 1; i < m_groupCount; ++i) {
860 if (m_filterGroup == m_cacheMetaType->groupNames.at(i - 1)) {
861 m_compositorGroup = Compositor::Group(i);
862 break;
863 }
864 }
865
866 QQmlDelegateModelGroupPrivate::get(m_groups[m_compositorGroup])->emitters.insert(this);
867 if (m_compositorGroup != previousGroup) {
868 QVector<QQmlChangeSet::Change> removes;
869 QVector<QQmlChangeSet::Change> inserts;
870 m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts);
871
872 QQmlChangeSet changeSet;
873 changeSet.move(removes, inserts);
874 emit q->modelUpdated(changeSet, false);
875
876 if (changeSet.difference() != 0)
877 emit q->countChanged();
878
879 if (m_parts) {
880 auto partsCopy = m_parts->models; // deliberate; this may alter m_parts
881 for (QQmlPartsModel *model : std::as_const(partsCopy))
882 model->updateFilterGroup(m_compositorGroup, changeSet);
883 }
884 }
885}
886
887/*!
888 \qmlproperty object QtQml.Models::DelegateModel::parts
889
890 The \a parts property selects a DelegateModel which creates
891 delegates from the part named. This is used in conjunction with
892 the \l Package type.
893
894 For example, the code below selects a model which creates
895 delegates named \e list from a \l Package:
896
897 \code
898 DelegateModel {
899 id: visualModel
900 delegate: Package {
901 Item { Package.name: "list" }
902 }
903 model: myModel
904 }
905
906 ListView {
907 width: 200; height:200
908 model: visualModel.parts.list
909 }
910 \endcode
911
912 \sa Package
913*/
914
915QObject *QQmlDelegateModel::parts()
916{
917 Q_D(QQmlDelegateModel);
918 if (!d->m_parts)
919 d->m_parts = new QQmlDelegateModelParts(this);
920 return d->m_parts;
921}
922
923const QAbstractItemModel *QQmlDelegateModel::abstractItemModel() const
924{
925 Q_D(const QQmlDelegateModel);
926 return d->m_adaptorModel.adaptsAim() ? d->m_adaptorModel.aim() : nullptr;
927}
928
929void QQmlDelegateModelPrivate::emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package)
930{
931 for (int i = 1; i < m_groupCount; ++i)
932 QQmlDelegateModelGroupPrivate::get(m_groups[i])->createdPackage(incubationTask->index[i], package);
933}
934
935void QQmlDelegateModelPrivate::emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package)
936{
937 for (int i = 1; i < m_groupCount; ++i)
938 QQmlDelegateModelGroupPrivate::get(m_groups[i])->initPackage(incubationTask->index[i], package);
939}
940
942{
943 for (int i = 1; i < m_groupCount; ++i)
944 QQmlDelegateModelGroupPrivate::get(m_groups[i])->destroyingPackage(package);
945}
946
947static bool isDoneIncubating(QQmlIncubator::Status status)
948{
949 return status == QQmlIncubator::Ready || status == QQmlIncubator::Error;
950}
951
953{
955public:
964
965 template<typename ModelObjectType>
967 {
969 for (int i = modelMetaObject->propertyOffset(); i < end; ++i) {
971 if (!prop.name())
972 continue;
974
975 bool wasInRequired = false;
979 if (!wasInRequired)
980 continue;
981
988
990 continue;
991
996 // Temporarily take away the metaobject so that the property can't actually be
997 // written. It shouldn't be written since we've just synchronized it the other way
998 // when installing the "forward" binding.
1001 } else {
1002 // It's only not a QQmlDelegateModelItem if the model is actually an ObjectModel.
1003 // In that case, we hope that the generic equality check when setting a property
1004 // is enough to de-bounce it.
1006 }
1007 }
1008 }
1009
1010private:
1011 QQmlEngine *engine = nullptr;
1012 QObject *targetObject = nullptr;
1013 RequiredProperties *requiredProperties = nullptr;
1016};
1017
1019 QQmlDelegateModelItem *modelItemToIncubate, QObject *object,
1020 QQmlDelegateModel::DelegateModelAccess access)
1021{
1022 // QQmlObjectCreator produces a private internal context.
1023 // We can always attach the extra object there.
1024 QQmlData *d = QQmlData::get(object);
1025 if (auto contextData = d ? d->context : nullptr)
1026 contextData->setExtraObject(modelItemToIncubate);
1027
1028 Q_ASSERT(modelItemToIncubate->delegate);
1029 const bool isBound = QQmlComponentPrivate::get(modelItemToIncubate->delegate)->isBound();
1030
1031 auto incubatorPriv = QQmlIncubatorPrivate::get(this);
1032 if (incubatorPriv->hadTopLevelRequiredProperties()) {
1033 // If we have required properties, we clear the context object
1034 // so that the model role names are not polluting the context.
1035 // Unless the context is bound, in which case we have never set the context object.
1036 if (incubating && !isBound) {
1037 Q_ASSERT(incubating->contextData);
1038 incubating->contextData->setContextObject(nullptr);
1039 }
1040 if (proxyContext) {
1041 proxyContext->setContextObject(nullptr);
1042 }
1043
1044 // Retrieve the metaObject before the potential return so that the accessors have a chance
1045 // to perform some finalization in case they produce a dynamic metaobject. Here we know for
1046 // sure that we are using required properties.
1047 const QMetaObject *qmlMetaObject = modelItemToIncubate->metaObject();
1048
1049 RequiredProperties *requiredProperties = incubatorPriv->requiredProperties();
1050 if (requiredProperties->empty())
1051 return;
1052
1053 const RequiredPropertiesInitializer initializer(
1054 QQmlEnginePrivate::get(incubatorPriv->enginePriv),
1055 object, requiredProperties, access);
1056
1057 // if a required property was not in the model, it might still be a static property of the
1058 // QQmlDelegateModelItem or one of its derived classes this is the case for index, row,
1059 // column, model and more
1060 // the most derived subclasses of QQmlDelegateModelItem are QQmlDMAbstractItemModelData and
1061 // QQmlDMObjectData at depth 2, so 4 should be plenty
1062
1063 // we first check the dynamic meta object for properties originating from the model
1064 // contains abstractitemmodelproperties
1065 initializer(qmlMetaObject, modelItemToIncubate);
1066
1067 auto delegateModelItemSubclassMO = qmlMetaObject->superClass();
1068 initializer(delegateModelItemSubclassMO, modelItemToIncubate);
1069
1070 while (strcmp(delegateModelItemSubclassMO->className(),
1071 modelItemToIncubate->staticMetaObject.className())) {
1072 delegateModelItemSubclassMO = delegateModelItemSubclassMO->superClass();
1073 initializer(delegateModelItemSubclassMO, modelItemToIncubate);
1074 }
1075
1076 if (proxiedObject)
1077 initializer(proxiedObject->metaObject(), proxiedObject.data());
1078
1079 } else {
1080 // To retain compatibility, we cannot enable structured model data if the data is passed
1081 // via context properties.
1082 modelItemToIncubate->disableStructuredModelData();
1083
1084 if (!isBound)
1085 modelItemToIncubate->contextData->setContextObject(modelItemToIncubate);
1086 if (proxiedObject)
1087 proxyContext->setContextObject(proxiedObject);
1088
1089 // Retrieve the metaObject() once so that the accessors have a chance to perform some
1090 // finalization in case they produce a dynamic metaobject. For example, they might be
1091 // inclined to create a propertyCache now because there are no required properties and any
1092 // revisioned properties should be hidden after all. Here is the first time we know for
1093 // sure whether we are using context properties.
1094 modelItemToIncubate->metaObject();
1095 }
1096}
1097
1099{
1100 if (vdm) {
1101 vdm->incubatorStatusChanged(this, status);
1102 } else if (isDoneIncubating(status)) {
1103 Q_ASSERT(incubating);
1104 // The model was deleted from under our feet, cleanup ourselves
1105 delete incubating->object;
1106 incubating->object = nullptr;
1107 incubating->contextData.reset();
1109 incubating->deleteLater();
1110 }
1111}
1112
1114{
1115 Q_Q(QQmlDelegateModel);
1116 if (!incubationTask->isError())
1117 incubationTask->clear();
1118 m_finishedIncubating.append(incubationTask);
1121 QCoreApplication::postEvent(q, new QEvent(QEvent::User));
1122 }
1123}
1124
1125void QQmlDelegateModelPrivate::reuseItem(QQmlDelegateModelItem *item, int newModelIndex, int newGroups)
1126{
1127 Q_ASSERT(item->object);
1128
1129 // Update/reset which groups the item belongs to
1130 item->groups = newGroups;
1131
1132 // Update context property index (including row and column) on the delegate
1133 // item, and inform the application about it. For a list, the row is the same
1134 // as the index, and the column is always 0. We set alwaysEmit to true, to
1135 // force all bindings to be reevaluated, even if the index didn't change.
1136 const bool alwaysEmit = true;
1137 item->setModelIndex(newModelIndex, newModelIndex, 0, alwaysEmit);
1138
1139 // Notify the application that all 'dynamic'/role-based context data has
1140 // changed as well (their getter function will use the updated index).
1141 auto const itemAsList = QList<QQmlDelegateModelItem *>() << item;
1142 auto const updateAllRoles = QVector<int>();
1143 m_adaptorModel.notify(itemAsList, newModelIndex, 1, updateAllRoles);
1144
1145 if (QQmlDelegateModelAttached *att = static_cast<QQmlDelegateModelAttached *>(
1146 qmlAttachedPropertiesObject<QQmlDelegateModel>(item->object, false))) {
1147 // Update currentIndex of the attached DelegateModel object
1148 // to the index the item has in the cache.
1150 // emitChanges will emit both group-, and index changes to the application
1151 att->emitChanges();
1152 }
1153
1154 // Inform the view that the item is recycled. This will typically result
1155 // in the view updating its own attached delegate item properties.
1156 emit q_func()->itemReused(newModelIndex, item->object);
1157}
1158
1160{
1161 m_reusableItemsPool.drain(maxPoolTime, [this](QQmlDelegateModelItem *cacheItem){ destroyCacheItem(cacheItem); });
1162}
1163
1164void QQmlDelegateModel::drainReusableItemsPool(int maxPoolTime)
1165{
1166 d_func()->drainReusableItemsPool(maxPoolTime);
1167}
1168
1169int QQmlDelegateModel::poolSize()
1170{
1171 return d_func()->m_reusableItemsPool.size();
1172}
1173
1175{
1176 if (!m_delegateChooser)
1177 return m_delegate;
1178
1179 QQmlComponent *delegate = nullptr;
1180 QQmlAbstractDelegateComponent *chooser = m_delegateChooser;
1181
1182 do {
1183 delegate = chooser->delegate(&m_adaptorModel, index);
1184 chooser = qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
1185 } while (chooser);
1186
1187 return delegate;
1188}
1189
1191{
1192 m_cache.insert(it.cacheIndex(), item);
1193 m_compositor.setFlags(it, 1, Compositor::CacheFlag);
1194 Q_ASSERT(m_cache.size() == m_compositor.count(Compositor::Cache));
1195}
1196
1198{
1199 int cidx = m_cache.lastIndexOf(cacheItem);
1200 if (cidx >= 0) {
1201 m_compositor.clearFlags(Compositor::Cache, cidx, 1, Compositor::CacheFlag);
1202 m_cache.removeAt(cidx);
1203 }
1204 Q_ASSERT(m_cache.size() == m_compositor.count(Compositor::Cache));
1205}
1206
1207void QQmlDelegateModelPrivate::incubatorStatusChanged(QQDMIncubationTask *incubationTask, QQmlIncubator::Status status)
1208{
1209 if (!isDoneIncubating(status))
1210 return;
1211
1212 const QList<QQmlError> incubationTaskErrors = incubationTask->errors();
1213
1214 QQmlDelegateModelItem *cacheItem = incubationTask->incubating;
1215 cacheItem->incubationTask = nullptr;
1216 incubationTask->incubating = nullptr;
1217 releaseIncubator(incubationTask);
1218
1219 if (status == QQmlIncubator::Ready) {
1220 cacheItem->referenceObject();
1221 if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(cacheItem->object))
1222 emitCreatedPackage(incubationTask, package);
1223 else
1224 emitCreatedItem(incubationTask, cacheItem->object);
1225 cacheItem->releaseObject();
1226 } else if (status == QQmlIncubator::Error) {
1227 qmlInfo(m_delegate, incubationTaskErrors + m_delegate->errors()) << "Cannot create delegate";
1228 }
1229
1230 if (!cacheItem->isObjectReferenced()) {
1231 if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(cacheItem->object))
1233 else
1234 emitDestroyingItem(cacheItem->object);
1235 delete cacheItem->object;
1236 cacheItem->object = nullptr;
1237 cacheItem->scriptRef -= 1;
1238 cacheItem->contextData.reset();
1239
1240 if (!cacheItem->isReferenced()) {
1241 removeCacheItem(cacheItem);
1242 delete cacheItem;
1243 }
1244 }
1245}
1246
1248{
1249 vdm->setInitialState(this, o);
1250}
1251
1253void QQmlDelegateModelGroupEmitter::createdPackage(int, QQuickPackage *) {}
1254void QQmlDelegateModelGroupEmitter::initPackage(int, QQuickPackage *) {}
1256
1258{
1259 QQmlDelegateModelItem *cacheItem = incubationTask->incubating;
1260 incubationTask->initializeRequiredProperties(
1261 incubationTask->incubating, o, m_adaptorModel.delegateModelAccess);
1262 cacheItem->object = o;
1263
1264 if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(cacheItem->object))
1265 emitInitPackage(incubationTask, package);
1266 else
1267 emitInitItem(incubationTask, cacheItem->object);
1268}
1269
1270QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQmlIncubator::IncubationMode incubationMode)
1271{
1272 if (!m_delegate || index < 0 || index >= m_compositor.count(group)) {
1273 qWarning() << "DelegateModel::item: index out range" << index << m_compositor.count(group);
1274 return nullptr;
1275 } else if (!m_context || !m_context->isValid()) {
1276 return nullptr;
1277 }
1278
1279 Compositor::iterator it = m_compositor.find(group, index);
1280 const auto flags = it->flags;
1281 const auto modelIndex = it.modelIndex();
1282
1283 QQmlDelegateModelItem *cacheItem = it->inCache() ? m_cache.at(it.cacheIndex()) : 0;
1284
1285 if (!cacheItem || !cacheItem->delegate) {
1286 QQmlComponent *delegate = resolveDelegate(modelIndex);
1287 if (!delegate)
1288 return nullptr;
1289
1290 if (!cacheItem) {
1291 cacheItem = m_reusableItemsPool.takeItem(delegate, index);
1292 if (cacheItem) {
1293 // Move the pooled item back into the cache, update
1294 // all related properties, and return the object (which
1295 // has already been incubated, otherwise it wouldn't be in the pool).
1296 addCacheItem(cacheItem, it);
1297 reuseItem(cacheItem, index, flags);
1298 cacheItem->referenceObject();
1299
1300 if (index == m_compositor.count(group) - 1)
1302 return cacheItem->object;
1303 }
1304
1305 // Since we could't find an available item in the pool, we create a new one
1306 cacheItem = m_adaptorModel.createItem(m_cacheMetaType, modelIndex);
1307 if (!cacheItem)
1308 return nullptr;
1309
1310 cacheItem->groups = flags;
1311 addCacheItem(cacheItem, it);
1312 }
1313
1314 cacheItem->delegate = delegate;
1315 }
1316
1317 // Bump the reference counts temporarily so neither the content data or the delegate object
1318 // are deleted if incubatorStatusChanged() is called synchronously.
1319 cacheItem->scriptRef += 1;
1320 cacheItem->referenceObject();
1321
1322 if (cacheItem->incubationTask) {
1323 bool sync = (incubationMode == QQmlIncubator::Synchronous || incubationMode == QQmlIncubator::AsynchronousIfNested);
1324 if (sync && cacheItem->incubationTask->incubationMode() == QQmlIncubator::Asynchronous) {
1325 // previously requested async - now needed immediately
1326 cacheItem->incubationTask->forceCompletion();
1327 }
1328 } else if (!cacheItem->object) {
1329 QQmlContext *creationContext = cacheItem->delegate->creationContext();
1330
1331 cacheItem->scriptRef += 1;
1332
1333 cacheItem->incubationTask = new QQDMIncubationTask(this, incubationMode);
1334 cacheItem->incubationTask->incubating = cacheItem;
1335 cacheItem->incubationTask->clear();
1336
1337 for (int i = 1; i < m_groupCount; ++i)
1338 cacheItem->incubationTask->index[i] = it.index[i];
1339
1340 const QQmlRefPointer<QQmlContextData> componentContext
1341 = QQmlContextData::get(creationContext ? creationContext : m_context.data());
1342 QQmlComponentPrivate *cp = QQmlComponentPrivate::get(cacheItem->delegate);
1343
1344 if (cp->isBound()) {
1345 cacheItem->contextData = componentContext;
1346
1347 // Ignore return value of initProxy. We want to know the proxy when assigning required
1348 // properties, but we don't want it to pollute our context. The context is bound.
1349 if (m_adaptorModel.hasProxyObject())
1350 cacheItem->initProxy();
1351
1352 cp->incubateObject(
1353 cacheItem->incubationTask,
1354 cacheItem->delegate,
1355 m_context->engine(),
1356 componentContext,
1357 QQmlContextData::get(m_context));
1358 } else {
1359 QQmlRefPointer<QQmlContextData> ctxt
1360 = QQmlContextData::createRefCounted(componentContext);
1361 ctxt->setContextObject(cacheItem);
1362 cacheItem->contextData = ctxt;
1363
1364 // If the model is read-only we cannot just expose the object as context
1365 // We actually need a separate model object to moderate access.
1366 if (m_adaptorModel.hasProxyObject()) {
1367 if (m_adaptorModel.delegateModelAccess == QQmlDelegateModel::ReadOnly)
1368 cacheItem->initProxy();
1369 else
1370 ctxt = cacheItem->initProxy();
1371 }
1372
1373 cp->incubateObject(
1374 cacheItem->incubationTask,
1375 cacheItem->delegate,
1376 m_context->engine(),
1377 ctxt,
1378 QQmlContextData::get(m_context));
1379 }
1380 }
1381
1382 if (index == m_compositor.count(group) - 1)
1384
1385 // Remove the temporary reference count.
1386 cacheItem->scriptRef -= 1;
1387 if (cacheItem->object && (!cacheItem->incubationTask || isDoneIncubating(cacheItem->incubationTask->status())))
1388 return cacheItem->object;
1389
1390 if (cacheItem->objectRef > 0)
1391 cacheItem->releaseObject();
1392
1393 if (!cacheItem->isReferenced()) {
1394 removeCacheItem(cacheItem);
1395 delete cacheItem;
1396 }
1397
1398 return nullptr;
1399}
1400
1401/*
1402 If asynchronous is true or the component is being loaded asynchronously due
1403 to an ancestor being loaded asynchronously, object() may return 0. In this
1404 case createdItem() will be emitted when the object is available. The object
1405 at this stage does not have any references, so object() must be called again
1406 to ensure a reference is held. Any call to object() which returns a valid object
1407 must be matched by a call to release() in order to destroy the object.
1408*/
1409QObject *QQmlDelegateModel::object(int index, QQmlIncubator::IncubationMode incubationMode)
1410{
1411 Q_D(QQmlDelegateModel);
1412 if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) {
1413 qWarning() << "DelegateModel::item: index out range" << index << d->m_compositor.count(d->m_compositorGroup);
1414 return nullptr;
1415 }
1416
1417 return d->object(d->m_compositorGroup, index, incubationMode);
1418}
1419
1420QQmlIncubator::Status QQmlDelegateModel::incubationStatus(int index)
1421{
1422 Q_D(QQmlDelegateModel);
1423 if (d->m_compositor.count(d->m_compositorGroup) <= index)
1424 return QQmlIncubator::Null;
1425 Compositor::iterator it = d->m_compositor.find(d->m_compositorGroup, index);
1426 if (!it->inCache())
1427 return QQmlIncubator::Null;
1428
1429 if (auto incubationTask = d->m_cache.at(it.cacheIndex())->incubationTask)
1430 return incubationTask->status();
1431
1432 return QQmlIncubator::Ready;
1433}
1434
1435QVariant QQmlDelegateModelPrivate::variantValue(QQmlListCompositor::Group group, int index, const QString &name)
1436{
1437 Compositor::iterator it = m_compositor.find(group, index);
1438 if (QQmlAdaptorModel *model = it.list<QQmlAdaptorModel>()) {
1439 QString role = name;
1440 int dot = name.indexOf(QLatin1Char('.'));
1441 if (dot > 0)
1442 role = name.left(dot);
1443 QVariant value = model->value(it.modelIndex(), role);
1444 while (dot > 0) {
1445 const int from = dot + 1;
1446 dot = name.indexOf(QLatin1Char('.'), from);
1447 QStringView propertyName = QStringView{name}.mid(from, dot - from);
1448 if (QObject *obj = qvariant_cast<QObject*>(value)) {
1449 value = obj->property(propertyName.toUtf8());
1450 } else if (const QMetaObject *metaObject = QQmlMetaType::metaObjectForValueType(value.metaType())) {
1451 // Q_GADGET
1452 const int propertyIndex = metaObject->indexOfProperty(propertyName.toUtf8());
1453 if (propertyIndex >= 0)
1454 value = metaObject->property(propertyIndex).readOnGadget(value.constData());
1455 } else {
1456 return QVariant();
1457 }
1458 }
1459 return value;
1460 }
1461 return QVariant();
1462}
1463
1464QVariant QQmlDelegateModel::variantValue(int index, const QString &role)
1465{
1466 Q_D(QQmlDelegateModel);
1467 return d->variantValue(d->m_compositorGroup, index, role);
1468}
1469
1470int QQmlDelegateModel::indexOf(QObject *item, QObject *) const
1471{
1472 Q_D(const QQmlDelegateModel);
1473 if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(item))
1474 return cacheItem->groupIndex(d->m_compositorGroup);
1475 return -1;
1476}
1477
1478void QQmlDelegateModel::setWatchedRoles(const QList<QByteArray> &roles)
1479{
1480 Q_D(QQmlDelegateModel);
1481 d->m_adaptorModel.replaceWatchedRoles(d->m_watchedRoles, roles);
1482 d->m_watchedRoles = roles;
1483}
1484
1486 Compositor::iterator from, int count, Compositor::Group group, int groupFlags)
1487{
1488 QVector<Compositor::Insert> inserts;
1489 m_compositor.setFlags(from, count, group, groupFlags, &inserts);
1490 itemsInserted(inserts);
1492}
1493
1495 Compositor::iterator from, int count, Compositor::Group group, int groupFlags)
1496{
1497 QVector<Compositor::Remove> removes;
1498 m_compositor.clearFlags(from, count, group, groupFlags, &removes);
1499 itemsRemoved(removes);
1501}
1502
1504 Compositor::iterator from, int count, Compositor::Group group, int groupFlags)
1505{
1506 QVector<Compositor::Remove> removes;
1507 QVector<Compositor::Insert> inserts;
1508
1509 m_compositor.setFlags(from, count, group, groupFlags, &inserts);
1510 itemsInserted(inserts);
1511 const int removeFlags = ~groupFlags & Compositor::GroupMask;
1512
1513 from = m_compositor.find(from.group, from.index[from.group]);
1514 m_compositor.clearFlags(from, count, group, removeFlags, &removes);
1515 itemsRemoved(removes);
1517}
1518
1519bool QQmlDelegateModel::event(QEvent *e)
1520{
1521 Q_D(QQmlDelegateModel);
1522 if (e->type() == QEvent::UpdateRequest) {
1523 d->m_waitingToFetchMore = false;
1524 d->m_adaptorModel.fetchMore();
1525 } else if (e->type() == QEvent::User) {
1526 d->m_incubatorCleanupScheduled = false;
1527 qDeleteAll(d->m_finishedIncubating);
1528 d->m_finishedIncubating.clear();
1529 }
1530 return QQmlInstanceModel::event(e);
1531}
1532
1533void QQmlDelegateModelPrivate::itemsChanged(const QVector<Compositor::Change> &changes)
1534{
1535 if (!m_delegate)
1536 return;
1537
1538 QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> translatedChanges(m_groupCount);
1539
1540 for (const Compositor::Change &change : changes) {
1541 for (int i = 1; i < m_groupCount; ++i) {
1542 if (change.inGroup(i)) {
1543 translatedChanges[i].append(QQmlChangeSet::Change(change.index[i], change.count));
1544 }
1545 }
1546 }
1547
1548 for (int i = 1; i < m_groupCount; ++i)
1549 QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.change(translatedChanges.at(i));
1550}
1551
1552void QQmlDelegateModel::_q_itemsChanged(int index, int count, const QVector<int> &roles)
1553{
1554 Q_D(QQmlDelegateModel);
1555 if (count <= 0 || !d->m_complete)
1556 return;
1557
1558 if (d->m_adaptorModel.notify(d->m_cache, index, count, roles)) {
1559 QVector<Compositor::Change> changes;
1560 d->m_compositor.listItemsChanged(&d->m_adaptorModel, index, count, &changes);
1561 d->itemsChanged(changes);
1562 d->emitChanges();
1563 }
1564 const bool needToCheckDelegateChoiceInvalidation = d->m_delegateChooser && !roles.isEmpty();
1565 if (!needToCheckDelegateChoiceInvalidation)
1566 return;
1567
1568 // here, we only really can handle AIM based models, because only there
1569 // we can do something sensible with roles
1570 if (!d->m_adaptorModel.adaptsAim())
1571 return;
1572
1573 const auto aim = d->m_adaptorModel.aim();
1574 const auto choiceRole = d->m_delegateChooser->role().toUtf8();
1575 const auto &roleNames = aim->roleNames();
1576 auto it = std::find_if(roles.begin(), roles.end(), [&](int role) {
1577 return roleNames[role] == choiceRole;
1578 });
1579 if (it == roles.end())
1580 return;
1581
1582 // Compare handleModelReset - we're doing a more localized version
1583
1584 /* A role change affecting the DelegateChoice is equivalent to removing all
1585 affected items (including invalidating their cache entries) and afterwards
1586 reinserting them.
1587 */
1588 QVector<Compositor::Remove> removes;
1589 QVector<Compositor::Insert> inserts;
1590 d->m_compositor.listItemsRemoved(&d->m_adaptorModel, index, count, &removes);
1591 const QList<QQmlDelegateModelItem *> cache = d->m_cache;
1592 for (QQmlDelegateModelItem *item : cache)
1593 item->referenceObject();
1594 for (const auto& removed: removes) {
1595 if (!d->m_cache.isSharedWith(cache))
1596 break;
1597 QQmlDelegateModelItem *item = cache.value(removed.cacheIndex(), nullptr);
1598 if (!d->m_cache.contains(item))
1599 continue;
1600 if (item->modelIndex() != -1)
1601 item->setModelIndex(-1, -1, -1);
1602 }
1603 for (QQmlDelegateModelItem *item : cache)
1604 item->releaseObject();
1605 d->m_compositor.listItemsInserted(&d->m_adaptorModel, index, count, &inserts);
1606 d->itemsMoved(removes, inserts);
1607 d->emitChanges();
1608}
1609
1610static void incrementIndexes(QQmlDelegateModelItem *cacheItem, int count, const int *deltas)
1611{
1612 if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) {
1613 for (int i = 1; i < count; ++i)
1614 incubationTask->index[i] += deltas[i];
1615 }
1616 if (QQmlDelegateModelAttached *attached = cacheItem->attached()) {
1617 for (int i = 1; i < qMin<int>(count, Compositor::MaximumGroupCount); ++i)
1618 attached->m_currentIndex[i] += deltas[i];
1619 }
1620}
1621
1623 const QVector<Compositor::Insert> &inserts,
1624 QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> *translatedInserts,
1625 QHash<int, QList<QQmlDelegateModelItem *> > *movedItems)
1626{
1627 int cacheIndex = 0;
1628
1629 int inserted[Compositor::MaximumGroupCount];
1630 for (int i = 1; i < m_groupCount; ++i)
1631 inserted[i] = 0;
1632
1633 for (const Compositor::Insert &insert : inserts) {
1634 for (; cacheIndex < insert.cacheIndex(); ++cacheIndex)
1635 incrementIndexes(m_cache.at(cacheIndex), m_groupCount, inserted);
1636
1637 for (int i = 1; i < m_groupCount; ++i) {
1638 if (insert.inGroup(i)) {
1639 (*translatedInserts)[i].append(
1640 QQmlChangeSet::Change(insert.index[i], insert.count, insert.moveId));
1641 inserted[i] += insert.count;
1642 }
1643 }
1644
1645 if (!insert.inCache())
1646 continue;
1647
1648 if (movedItems && insert.isMove()) {
1649 QList<QQmlDelegateModelItem *> items = movedItems->take(insert.moveId);
1650 Q_ASSERT(items.size() == insert.count);
1651 m_cache = m_cache.mid(0, insert.cacheIndex())
1652 + items + m_cache.mid(insert.cacheIndex());
1653 }
1654 if (insert.inGroup()) {
1655 for (int offset = 0; cacheIndex < insert.cacheIndex() + insert.count;
1656 ++cacheIndex, ++offset) {
1657 QQmlDelegateModelItem *cacheItem = m_cache.at(cacheIndex);
1658 cacheItem->groups |= insert.flags & Compositor::GroupMask;
1659
1660 if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) {
1661 for (int i = 1; i < m_groupCount; ++i)
1662 incubationTask->index[i] = cacheItem->groups & (1 << i)
1663 ? insert.index[i] + offset
1664 : insert.index[i];
1665 }
1666 if (QQmlDelegateModelAttached *attached = cacheItem->attached()) {
1667 for (int i = 1; i < m_groupCount; ++i)
1668 attached->m_currentIndex[i] = cacheItem->groups & (1 << i)
1669 ? insert.index[i] + offset
1670 : insert.index[i];
1671 }
1672 }
1673 } else {
1674 cacheIndex = insert.cacheIndex() + insert.count;
1675 }
1676 }
1677 for (const QList<QQmlDelegateModelItem *> cache = m_cache; cacheIndex < cache.size(); ++cacheIndex)
1678 incrementIndexes(cache.at(cacheIndex), m_groupCount, inserted);
1679}
1680
1681void QQmlDelegateModelPrivate::itemsInserted(const QVector<Compositor::Insert> &inserts)
1682{
1683 QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> translatedInserts(m_groupCount);
1684 itemsInserted(inserts, &translatedInserts);
1685 Q_ASSERT(m_cache.size() == m_compositor.count(Compositor::Cache));
1686 if (!m_delegate)
1687 return;
1688
1689 for (int i = 1; i < m_groupCount; ++i)
1690 QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.insert(translatedInserts.at(i));
1691}
1692
1693void QQmlDelegateModel::_q_itemsInserted(int index, int count)
1694{
1695
1696 Q_D(QQmlDelegateModel);
1697 if (count <= 0 || !d->m_complete)
1698 return;
1699
1700 d->m_count += count;
1701
1702 const QList<QQmlDelegateModelItem *> cache = d->m_cache;
1703 for (int i = 0, c = cache.size(); i < c; ++i) {
1704 QQmlDelegateModelItem *item = cache.at(i);
1705 // layout change triggered by changing the modelIndex might have
1706 // already invalidated this item in d->m_cache and deleted it.
1707 if (!d->m_cache.isSharedWith(cache) && !d->m_cache.contains(item))
1708 continue;
1709
1710 if (item->modelIndex() >= index) {
1711 const int newIndex = item->modelIndex() + count;
1712 const int row = newIndex;
1713 const int column = 0;
1714 item->setModelIndex(newIndex, row, column);
1715 }
1716 }
1717
1718 QVector<Compositor::Insert> inserts;
1719 d->m_compositor.listItemsInserted(&d->m_adaptorModel, index, count, &inserts);
1720 d->itemsInserted(inserts);
1721 d->emitChanges();
1722}
1723
1724//### This method should be split in two. It will remove delegates, and it will re-render the list.
1725// When e.g. QQmlListModel::remove is called, the removal of the delegates should be done on
1726// QAbstractItemModel::rowsAboutToBeRemoved, and the re-rendering on
1727// QAbstractItemModel::rowsRemoved. Currently both are done on the latter signal. The problem is
1728// that the destruction of an item will emit a changed signal that ends up at the delegate, which
1729// in turn will try to load the data from the model (which should have already freed it), resulting
1730// in a use-after-free. See QTBUG-59256.
1732 const QVector<Compositor::Remove> &removes,
1733 QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> *translatedRemoves,
1734 QHash<int, QList<QQmlDelegateModelItem *> > *movedItems)
1735{
1736 int cacheIndex = 0;
1737 int removedCache = 0;
1738
1739 int removed[Compositor::MaximumGroupCount];
1740 for (int i = 1; i < m_groupCount; ++i)
1741 removed[i] = 0;
1742
1743 for (const Compositor::Remove &remove : removes) {
1744 for (; cacheIndex < remove.cacheIndex() && cacheIndex < m_cache.size(); ++cacheIndex)
1745 incrementIndexes(m_cache.at(cacheIndex), m_groupCount, removed);
1746
1747 for (int i = 1; i < m_groupCount; ++i) {
1748 if (remove.inGroup(i)) {
1749 (*translatedRemoves)[i].append(
1750 QQmlChangeSet::Change(remove.index[i], remove.count, remove.moveId));
1751 removed[i] -= remove.count;
1752 }
1753 }
1754
1755 if (!remove.inCache())
1756 continue;
1757
1758 if (movedItems && remove.isMove()) {
1759 movedItems->insert(remove.moveId, m_cache.mid(remove.cacheIndex(), remove.count));
1760 QList<QQmlDelegateModelItem *>::const_iterator begin = m_cache.constBegin() + remove.cacheIndex();
1761 QList<QQmlDelegateModelItem *>::const_iterator end = begin + remove.count;
1762 m_cache.erase(begin, end);
1763 } else {
1764 for (; cacheIndex < remove.cacheIndex() + remove.count - removedCache; ++cacheIndex) {
1765 QQmlDelegateModelItem *cacheItem = m_cache.at(cacheIndex);
1766 if (remove.inGroup(Compositor::Persisted) && cacheItem->objectRef == 0 && cacheItem->object) {
1767 QObject *object = cacheItem->object;
1768 cacheItem->destroyObject();
1769 if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object))
1770 emitDestroyingPackage(package);
1771 else
1772 emitDestroyingItem(object);
1773 cacheItem->scriptRef -= 1;
1774 }
1775 if (!cacheItem->isReferenced() && !remove.inGroup(Compositor::Persisted)) {
1776 m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag);
1777 m_cache.removeAt(cacheIndex);
1778 delete cacheItem;
1779 --cacheIndex;
1780 ++removedCache;
1781 Q_ASSERT(m_cache.size() == m_compositor.count(Compositor::Cache));
1782 } else if (remove.groups() == cacheItem->groups) {
1783 cacheItem->groups = 0;
1784 if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) {
1785 for (int i = 1; i < m_groupCount; ++i)
1786 incubationTask->index[i] = -1;
1787 }
1788 if (QQmlDelegateModelAttached *attached = cacheItem->attached()) {
1789 for (int i = 1; i < m_groupCount; ++i)
1790 attached->m_currentIndex[i] = -1;
1791 }
1792 } else {
1793 if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) {
1794 if (!cacheItem->isObjectReferenced()) {
1795 releaseIncubator(cacheItem->incubationTask);
1796 cacheItem->incubationTask = nullptr;
1797 if (cacheItem->object) {
1798 QObject *object = cacheItem->object;
1799 cacheItem->destroyObject();
1800 if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object))
1801 emitDestroyingPackage(package);
1802 else
1803 emitDestroyingItem(object);
1804 }
1805 cacheItem->scriptRef -= 1;
1806 } else {
1807 for (int i = 1; i < m_groupCount; ++i) {
1808 if (remove.inGroup(i))
1809 incubationTask->index[i] = remove.index[i];
1810 }
1811 }
1812 }
1813 if (QQmlDelegateModelAttached *attached = cacheItem->attached()) {
1814 for (int i = 1; i < m_groupCount; ++i) {
1815 if (remove.inGroup(i))
1816 attached->m_currentIndex[i] = remove.index[i];
1817 }
1818 }
1819 cacheItem->groups &= ~remove.flags;
1820 }
1821 }
1822 }
1823 }
1824
1825 for (const QList<QQmlDelegateModelItem *> cache = m_cache; cacheIndex < cache.size(); ++cacheIndex)
1826 incrementIndexes(cache.at(cacheIndex), m_groupCount, removed);
1827}
1828
1829void QQmlDelegateModelPrivate::itemsRemoved(const QVector<Compositor::Remove> &removes)
1830{
1831 QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount);
1832 itemsRemoved(removes, &translatedRemoves);
1833 Q_ASSERT(m_cache.size() == m_compositor.count(Compositor::Cache));
1834 if (!m_delegate)
1835 return;
1836
1837 for (int i = 1; i < m_groupCount; ++i)
1838 QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.remove(translatedRemoves.at(i));
1839}
1840
1841void QQmlDelegateModel::_q_itemsRemoved(int index, int count)
1842{
1843 Q_D(QQmlDelegateModel);
1844 if (count <= 0|| !d->m_complete)
1845 return;
1846
1847 d->m_count -= count;
1848 Q_ASSERT(d->m_count >= 0);
1849 const QList<QQmlDelegateModelItem *> cache = d->m_cache;
1850 //Prevents items being deleted in remove loop
1851 for (QQmlDelegateModelItem *item : cache)
1852 item->referenceObject();
1853
1854 for (int i = 0, c = cache.size(); i < c; ++i) {
1855 QQmlDelegateModelItem *item = cache.at(i);
1856 // layout change triggered by removal of a previous item might have
1857 // already invalidated this item in d->m_cache and deleted it
1858 if (!d->m_cache.isSharedWith(cache) && !d->m_cache.contains(item))
1859 continue;
1860
1861 if (item->modelIndex() >= index + count) {
1862 const int newIndex = item->modelIndex() - count;
1863 const int row = newIndex;
1864 const int column = 0;
1865 item->setModelIndex(newIndex, row, column);
1866 } else if (item->modelIndex() >= index) {
1867 item->setModelIndex(-1, -1, -1);
1868 }
1869 }
1870 //Release items which are referenced before the loop
1871 for (QQmlDelegateModelItem *item : cache)
1872 item->releaseObject();
1873
1874 QVector<Compositor::Remove> removes;
1875 d->m_compositor.listItemsRemoved(&d->m_adaptorModel, index, count, &removes);
1876 d->itemsRemoved(removes);
1877
1878 d->emitChanges();
1879}
1880
1882 const QVector<Compositor::Remove> &removes, const QVector<Compositor::Insert> &inserts)
1883{
1884 QHash<int, QList<QQmlDelegateModelItem *> > movedItems;
1885
1886 QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount);
1887 itemsRemoved(removes, &translatedRemoves, &movedItems);
1888
1889 QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> translatedInserts(m_groupCount);
1890 itemsInserted(inserts, &translatedInserts, &movedItems);
1891 Q_ASSERT(m_cache.size() == m_compositor.count(Compositor::Cache));
1892 Q_ASSERT(movedItems.isEmpty());
1893 if (!m_delegate)
1894 return;
1895
1896 for (int i = 1; i < m_groupCount; ++i) {
1897 QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.move(
1898 translatedRemoves.at(i),
1899 translatedInserts.at(i));
1900 }
1901}
1902
1903void QQmlDelegateModel::_q_itemsMoved(int from, int to, int count)
1904{
1905 Q_D(QQmlDelegateModel);
1906 if (count <= 0 || !d->m_complete)
1907 return;
1908
1909 const int minimum = qMin(from, to);
1910 const int maximum = qMax(from, to) + count;
1911 const int difference = from > to ? count : -count;
1912
1913 const QList<QQmlDelegateModelItem *> cache = d->m_cache;
1914 for (int i = 0, c = cache.size(); i < c; ++i) {
1915 QQmlDelegateModelItem *item = cache.at(i);
1916 // layout change triggered by changing the modelIndex might have
1917 // already invalidated this item in d->m_cache and deleted it.
1918 if (!d->m_cache.isSharedWith(cache) && !d->m_cache.contains(item))
1919 continue;
1920
1921 if (item->modelIndex() >= from && item->modelIndex() < from + count) {
1922 const int newIndex = item->modelIndex() - from + to;
1923 const int row = newIndex;
1924 const int column = 0;
1925 item->setModelIndex(newIndex, row, column);
1926 } else if (item->modelIndex() >= minimum && item->modelIndex() < maximum) {
1927 const int newIndex = item->modelIndex() + difference;
1928 const int row = newIndex;
1929 const int column = 0;
1930 item->setModelIndex(newIndex, row, column);
1931 }
1932 }
1933
1934 QVector<Compositor::Remove> removes;
1935 QVector<Compositor::Insert> inserts;
1936 d->m_compositor.listItemsMoved(&d->m_adaptorModel, from, to, count, &removes, &inserts);
1937 d->itemsMoved(removes, inserts);
1938 d->emitChanges();
1939}
1940
1941void QQmlDelegateModelPrivate::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset)
1942{
1943 Q_Q(QQmlDelegateModel);
1944 emit q->modelUpdated(changeSet, reset);
1945 if (changeSet.difference() != 0)
1946 emit q->countChanged();
1947}
1948
1949void QQmlDelegateModelPrivate::delegateChanged(bool add, bool remove)
1950{
1951 Q_Q(QQmlDelegateModel);
1952 if (!m_complete)
1953 return;
1954
1955 if (m_transaction) {
1956 qmlWarning(q) << QQmlDelegateModel::tr("The delegates of a DelegateModel cannot be changed within onUpdated.");
1957 return;
1958 }
1959
1960 if (remove) {
1961 for (int i = 1; i < m_groupCount; ++i) {
1962 QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.remove(
1963 0, m_compositor.count(Compositor::Group(i)));
1964 }
1965 }
1966 if (add) {
1967 for (int i = 1; i < m_groupCount; ++i) {
1968 QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.insert(
1969 0, m_compositor.count(Compositor::Group(i)));
1970 }
1971 }
1973}
1974
1976{
1977 if (m_transaction || !m_complete || !m_context || !m_context->isValid())
1978 return;
1979
1980 m_transaction = true;
1981 QV4::ExecutionEngine *engine = m_context->engine()->handle();
1982 for (int i = 1; i < m_groupCount; ++i)
1983 QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitChanges(engine);
1984 m_transaction = false;
1985
1986 const bool reset = m_reset;
1987 m_reset = false;
1988 for (int i = 1; i < m_groupCount; ++i)
1989 QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitModelUpdated(reset);
1990
1991 // emitChanges may alter m_cache and delete items
1992 QVarLengthArray<QPointer<QQmlDelegateModelAttached>> attachedObjects;
1993 attachedObjects.reserve(m_cache.length());
1994 for (const QQmlDelegateModelItem *cacheItem : std::as_const(m_cache))
1995 attachedObjects.append(cacheItem->attached());
1996
1997 for (const QPointer<QQmlDelegateModelAttached> &attached : std::as_const(attachedObjects)) {
1998 if (attached && attached->m_cacheItem)
1999 attached->emitChanges();
2000 }
2001}
2002
2003void QQmlDelegateModel::_q_modelAboutToBeReset()
2004{
2005 Q_D(QQmlDelegateModel);
2006 if (!d->m_adaptorModel.adaptsAim())
2007 return;
2008 auto aim = d->m_adaptorModel.aim();
2009 auto oldRoleNames = aim->roleNames();
2010 // this relies on the fact that modelAboutToBeReset must be followed
2011 // by a modelReset signal before any further modelAboutToBeReset can occur
2012 QObject::connect(aim, &QAbstractItemModel::modelReset, this, [this, d, oldRoleNames, aim](){
2013 if (!d->m_adaptorModel.adaptsAim() || d->m_adaptorModel.aim() != aim)
2014 return;
2015 if (oldRoleNames == aim->roleNames()) {
2016 // if the rolenames stayed the same (most common case), then we don't have
2017 // to throw away all the setup that we did
2018 handleModelReset();
2019 } else {
2020 // If they did change, we give up and just start from scratch via setModel
2021 QVariant m = model();
2022 setModel(QVariant());
2023 setModel(m);
2024 // but we still have to call handleModelReset, otherwise views will
2025 // not refresh
2026 handleModelReset();
2027 }
2028 }, Qt::SingleShotConnection);
2029}
2030
2031void QQmlDelegateModel::handleModelReset()
2032{
2033 Q_D(QQmlDelegateModel);
2034 if (!d->m_delegate)
2035 return;
2036
2037 int oldCount = d->m_count;
2038 d->m_adaptorModel.rootIndex = QModelIndex();
2039
2040 if (d->m_complete) {
2041 d->m_count = d->adaptorModelCount();
2042
2043 const QList<QQmlDelegateModelItem *> cache = d->m_cache;
2044 for (QQmlDelegateModelItem *item : cache)
2045 item->referenceObject();
2046
2047 for (int i = 0, c = cache.size(); i < c; ++i) {
2048 QQmlDelegateModelItem *item = cache.at(i);
2049 // layout change triggered by changing the modelIndex might have
2050 // already invalidated this item in d->m_cache and deleted it.
2051 if (!d->m_cache.isSharedWith(cache) && !d->m_cache.contains(item))
2052 continue;
2053
2054 if (item->modelIndex() != -1)
2055 item->setModelIndex(-1, -1, -1);
2056 }
2057
2058 for (QQmlDelegateModelItem *item : cache)
2059 item->releaseObject();
2060 QVector<Compositor::Remove> removes;
2061 QVector<Compositor::Insert> inserts;
2062 if (oldCount)
2063 d->m_compositor.listItemsRemoved(&d->m_adaptorModel, 0, oldCount, &removes);
2064 if (d->m_count)
2065 d->m_compositor.listItemsInserted(&d->m_adaptorModel, 0, d->m_count, &inserts);
2066 d->itemsMoved(removes, inserts);
2067 d->m_reset = true;
2068
2069 if (d->m_adaptorModel.canFetchMore())
2070 d->m_adaptorModel.fetchMore();
2071
2072 d->emitChanges();
2073 }
2074 emit rootIndexChanged();
2075}
2076
2077void QQmlDelegateModel::_q_rowsInserted(const QModelIndex &parent, int begin, int end)
2078{
2079 Q_D(QQmlDelegateModel);
2080 if (parent == d->m_adaptorModel.rootIndex)
2081 _q_itemsInserted(begin, end - begin + 1);
2082}
2083
2084void QQmlDelegateModel::_q_rowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end)
2085{
2086 Q_D(QQmlDelegateModel);
2087 if (!d->m_adaptorModel.rootIndex.isValid())
2088 return;
2089 const QModelIndex index = d->m_adaptorModel.rootIndex;
2090 if (index.parent() == parent && index.row() >= begin && index.row() <= end) {
2091 const int oldCount = d->m_count;
2092 d->m_count = 0;
2093 d->disconnectFromAbstractItemModel();
2094 d->m_adaptorModel.invalidateModel();
2095
2096 if (d->m_complete && oldCount > 0) {
2097 QVector<Compositor::Remove> removes;
2098 d->m_compositor.listItemsRemoved(&d->m_adaptorModel, 0, oldCount, &removes);
2099 d->itemsRemoved(removes);
2100 d->emitChanges();
2101 }
2102 }
2103}
2104
2105void QQmlDelegateModel::_q_rowsRemoved(const QModelIndex &parent, int begin, int end)
2106{
2107 Q_D(QQmlDelegateModel);
2108 if (parent == d->m_adaptorModel.rootIndex)
2109 _q_itemsRemoved(begin, end - begin + 1);
2110}
2111
2112void QQmlDelegateModel::_q_rowsMoved(
2113 const QModelIndex &sourceParent, int sourceStart, int sourceEnd,
2114 const QModelIndex &destinationParent, int destinationRow)
2115{
2116 Q_D(QQmlDelegateModel);
2117 const int count = sourceEnd - sourceStart + 1;
2118 if (destinationParent == d->m_adaptorModel.rootIndex && sourceParent == d->m_adaptorModel.rootIndex) {
2119 _q_itemsMoved(sourceStart, sourceStart > destinationRow ? destinationRow : destinationRow - count, count);
2120 } else if (sourceParent == d->m_adaptorModel.rootIndex) {
2121 _q_itemsRemoved(sourceStart, count);
2122 } else if (destinationParent == d->m_adaptorModel.rootIndex) {
2123 _q_itemsInserted(destinationRow, count);
2124 }
2125}
2126
2127void QQmlDelegateModel::_q_columnsInserted(const QModelIndex &parent, int begin, int end)
2128{
2129 Q_D(QQmlDelegateModel);
2130 Q_UNUSED(end);
2131 if (parent == d->m_adaptorModel.rootIndex && begin == 0) {
2132 // mark all items as changed
2133 _q_itemsChanged(0, d->m_count, QVector<int>());
2134 }
2135}
2136
2137void QQmlDelegateModel::_q_columnsRemoved(const QModelIndex &parent, int begin, int end)
2138{
2139 Q_D(QQmlDelegateModel);
2140 Q_UNUSED(end);
2141 if (parent == d->m_adaptorModel.rootIndex && begin == 0) {
2142 // mark all items as changed
2143 _q_itemsChanged(0, d->m_count, QVector<int>());
2144 }
2145}
2146
2147void QQmlDelegateModel::_q_columnsMoved(const QModelIndex &parent, int start, int end,
2148 const QModelIndex &destination, int column)
2149{
2150 Q_D(QQmlDelegateModel);
2151 Q_UNUSED(end);
2152 if ((parent == d->m_adaptorModel.rootIndex && start == 0)
2153 || (destination == d->m_adaptorModel.rootIndex && column == 0)) {
2154 // mark all items as changed
2155 _q_itemsChanged(0, d->m_count, QVector<int>());
2156 }
2157}
2158
2159void QQmlDelegateModel::_q_dataChanged(const QModelIndex &begin, const QModelIndex &end, const QVector<int> &roles)
2160{
2161 Q_D(QQmlDelegateModel);
2162 if (begin.parent() == d->m_adaptorModel.rootIndex)
2163 _q_itemsChanged(begin.row(), end.row() - begin.row() + 1, roles);
2164}
2165
2166bool QQmlDelegateModel::isDescendantOf(const QPersistentModelIndex& desc, const QList< QPersistentModelIndex >& parents) const
2167{
2168 for (int i = 0, c = parents.size(); i < c; ++i) {
2169 for (QPersistentModelIndex parent = desc; parent.isValid(); parent = parent.parent()) {
2170 if (parent == parents[i])
2171 return true;
2172 }
2173 }
2174
2175 return false;
2176}
2177
2178void QQmlDelegateModel::_q_layoutChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint)
2179{
2180 Q_D(QQmlDelegateModel);
2181 if (!d->m_complete)
2182 return;
2183
2184 if (hint == QAbstractItemModel::VerticalSortHint) {
2185 if (!parents.isEmpty() && d->m_adaptorModel.rootIndex.isValid() && !isDescendantOf(d->m_adaptorModel.rootIndex, parents)) {
2186 return;
2187 }
2188
2189 // mark all items as changed
2190 _q_itemsChanged(0, d->m_count, QVector<int>());
2191
2192 } else if (hint == QAbstractItemModel::HorizontalSortHint) {
2193 // Ignored
2194 } else {
2195 // We don't know what's going on, so reset the model
2196 handleModelReset();
2197 }
2198}
2199
2200QQmlDelegateModelAttached *QQmlDelegateModel::qmlAttachedProperties(QObject *obj)
2201{
2202 if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(obj))
2203 return new QQmlDelegateModelAttached(cacheItem, obj);
2204 return new QQmlDelegateModelAttached(obj);
2205}
2206
2208QQmlDelegateModelPrivate::insert(Compositor::insert_iterator &before, const QV4::Value &object, int groups)
2209{
2210 if (!m_context || !m_context->isValid())
2212
2213 QQmlDelegateModelItem *cacheItem = m_adaptorModel.createItem(m_cacheMetaType, -1);
2214 if (!cacheItem)
2216 if (!object.isObject())
2218
2219 QV4::ExecutionEngine *v4 = object.as<QV4::Object>()->engine();
2220 QV4::Scope scope(v4);
2221 QV4::ScopedObject o(scope, object);
2222 if (!o)
2224
2225 QV4::ObjectIterator it(scope, o, QV4::ObjectIterator::EnumerableOnly);
2226 QV4::ScopedValue propertyName(scope);
2227 QV4::ScopedValue v(scope);
2228 const auto oldCache = m_cache;
2229 while (1) {
2230 propertyName = it.nextPropertyNameAsString(v);
2231 if (propertyName->isNull())
2232 break;
2233 cacheItem->setValue(
2234 propertyName->toQStringNoThrow(),
2235 QV4::ExecutionEngine::toVariant(v, QMetaType {}));
2236 }
2237 const bool cacheModified = !m_cache.isSharedWith(oldCache);
2238 if (cacheModified)
2240
2241 cacheItem->groups = groups | Compositor::UnresolvedFlag | Compositor::CacheFlag;
2242
2243 // Must be before the new object is inserted into the cache or its indexes will be adjusted too.
2244 itemsInserted(QVector<Compositor::Insert>(1, Compositor::Insert(before, 1, cacheItem->groups & ~Compositor::CacheFlag)));
2245
2246 m_cache.insert(before.cacheIndex(), cacheItem);
2247 m_compositor.insert(before, nullptr, 0, 1, cacheItem->groups);
2248
2250}
2251
2252//============================================================================
2253
2254QQmlDelegateModelItemMetaType::QQmlDelegateModelItemMetaType(
2255 QV4::ExecutionEngine *engine, QQmlDelegateModel *model, const QStringList &groupNames)
2256 : model(model)
2257 , v4Engine(engine)
2258 , groupNames(groupNames)
2259 , modelKind(ModelKind::DelegateModel)
2260{
2261}
2262
2263QQmlDelegateModelItemMetaType::QQmlDelegateModelItemMetaType(
2264 QV4::ExecutionEngine *engine, QQmlTableInstanceModel *model)
2265 : model(model)
2266 , v4Engine(engine)
2267 , modelKind(ModelKind::TableInstanceModel)
2268{
2269}
2270
2271QQmlDelegateModelItemMetaType::~QQmlDelegateModelItemMetaType() = default;
2272
2273void QQmlDelegateModelItemMetaType::emitModelChanged() const
2274{
2275 switch (modelKind) {
2276 case ModelKind::InstanceModel:
2277 break;
2278 case ModelKind::DelegateModel:
2279 emit static_cast<QQmlDelegateModel *>(model.data())->modelChanged();
2280 break;
2281 case ModelKind::TableInstanceModel:
2282 emit static_cast<QQmlTableInstanceModel *>(model.data())->modelChanged();
2283 break;
2284 }
2285}
2286
2287void QQmlDelegateModelItemMetaType::initializeAttachedMetaObject()
2288{
2289 QMetaObjectBuilder builder;
2290 builder.setFlags(DynamicMetaObject);
2291 builder.setClassName(QQmlDelegateModelAttached::staticMetaObject.className());
2292 builder.setSuperClass(&QQmlDelegateModelAttached::staticMetaObject);
2293
2294 int notifierId = 0;
2295 for (qsizetype i = 0, end = groupCount(); i < end; ++i, ++notifierId) {
2296 QString propertyName = QLatin1String("in") + groupNames.at(i);
2297 propertyName.replace(2, 1, propertyName.at(2).toUpper());
2298 builder.addSignal("__" + propertyName.toUtf8() + "Changed()");
2299 QMetaPropertyBuilder propertyBuilder = builder.addProperty(
2300 propertyName.toUtf8(), "bool", notifierId);
2301 propertyBuilder.setWritable(true);
2302 }
2303 for (qsizetype i = 0, end = groupCount(); i < end; ++i, ++notifierId) {
2304 const QString propertyName = groupNames.at(i) + QLatin1String("Index");
2305 builder.addSignal("__" + propertyName.toUtf8() + "Changed()");
2306 QMetaPropertyBuilder propertyBuilder = builder.addProperty(
2307 propertyName.toUtf8(), "int", notifierId);
2308 propertyBuilder.setWritable(true);
2309 }
2310
2311 attachedMetaObject = QQml::makeRefPointer<QQmlDelegateModelAttachedMetaObject>(
2312 this, builder.toMetaObject());
2313}
2314
2315void QQmlDelegateModelItemMetaType::initializePrototype()
2316{
2317 QV4::Scope scope(v4Engine);
2318
2319 QV4::ScopedObject proto(scope, v4Engine->newObject());
2320 proto->defineAccessorProperty(QStringLiteral("model"), QQmlDelegateModelItem::get_model, nullptr);
2321 proto->defineAccessorProperty(QStringLiteral("groups"), QQmlDelegateModelItem::get_groups, QQmlDelegateModelItem::set_groups);
2322 QV4::ScopedString s(scope);
2323 QV4::ScopedProperty p(scope);
2324
2325 s = v4Engine->newString(QStringLiteral("isUnresolved"));
2326 QV4::ScopedFunctionObject f(scope);
2327 QV4::ExecutionEngine *engine = scope.engine;
2328 p->setGetter((f = QV4::DelegateModelGroupFunction::create(engine, 30, QQmlDelegateModelItem::get_member)));
2329 p->setSetter(nullptr);
2330 proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable);
2331
2332 s = v4Engine->newString(QStringLiteral("inItems"));
2333 p->setGetter((f = QV4::DelegateModelGroupFunction::create(engine, QQmlListCompositor::Default, QQmlDelegateModelItem::get_member)));
2334 p->setSetter((f = QV4::DelegateModelGroupFunction::create(engine, QQmlListCompositor::Default, QQmlDelegateModelItem::set_member)));
2335 proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable);
2336
2337 s = v4Engine->newString(QStringLiteral("inPersistedItems"));
2338 p->setGetter((f = QV4::DelegateModelGroupFunction::create(engine, QQmlListCompositor::Persisted, QQmlDelegateModelItem::get_member)));
2339 p->setSetter((f = QV4::DelegateModelGroupFunction::create(engine, QQmlListCompositor::Persisted, QQmlDelegateModelItem::set_member)));
2340 proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable);
2341
2342 s = v4Engine->newString(QStringLiteral("itemsIndex"));
2343 p->setGetter((f = QV4::DelegateModelGroupFunction::create(engine, QQmlListCompositor::Default, QQmlDelegateModelItem::get_index)));
2344 proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable);
2345
2346 s = v4Engine->newString(QStringLiteral("persistedItemsIndex"));
2347 p->setGetter((f = QV4::DelegateModelGroupFunction::create(engine, QQmlListCompositor::Persisted, QQmlDelegateModelItem::get_index)));
2348 p->setSetter(nullptr);
2349 proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable);
2350
2351 for (qsizetype i = 2, end = groupCount(); i < end; ++i) {
2352 QString propertyName = QLatin1String("in") + groupNames.at(i);
2353 propertyName.replace(2, 1, propertyName.at(2).toUpper());
2354 s = v4Engine->newString(propertyName);
2355 p->setGetter((f = QV4::DelegateModelGroupFunction::create(engine, i + 1, QQmlDelegateModelItem::get_member)));
2356 p->setSetter((f = QV4::DelegateModelGroupFunction::create(engine, i + 1, QQmlDelegateModelItem::set_member)));
2357 proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable);
2358 }
2359 for (qsizetype i = 2, end = groupCount(); i < end; ++i) {
2360 const QString propertyName = groupNames.at(i) + QLatin1String("Index");
2361 s = v4Engine->newString(propertyName);
2362 p->setGetter((f = QV4::DelegateModelGroupFunction::create(engine, i + 1, QQmlDelegateModelItem::get_index)));
2363 p->setSetter(nullptr);
2364 proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable);
2365 }
2366 modelItemProto.set(v4Engine, proto);
2367}
2368
2369int QQmlDelegateModelItemMetaType::parseGroups(const QStringList &groups) const
2370{
2371 int groupFlags = 0;
2372 for (const QString &groupName : groups) {
2373 int index = groupNames.indexOf(groupName);
2374 if (index != -1)
2375 groupFlags |= 2 << index;
2376 }
2377 return groupFlags;
2378}
2379
2380int QQmlDelegateModelItemMetaType::parseGroups(const QV4::Value &groups) const
2381{
2382 int groupFlags = 0;
2383 QV4::Scope scope(v4Engine);
2384
2385 QV4::ScopedString s(scope, groups);
2386 if (s) {
2387 const QString groupName = s->toQString();
2388 int index = groupNames.indexOf(groupName);
2389 if (index != -1)
2390 groupFlags |= 2 << index;
2391 return groupFlags;
2392 }
2393
2394 QV4::ScopedArrayObject array(scope, groups);
2395 if (array) {
2396 QV4::ScopedValue v(scope);
2397 uint arrayLength = array->getLength();
2398 for (uint i = 0; i < arrayLength; ++i) {
2399 v = array->get(i);
2400 const QString groupName = v->toQString();
2401 int index = groupNames.indexOf(groupName);
2402 if (index != -1)
2403 groupFlags |= 2 << index;
2404 }
2405 }
2406 return groupFlags;
2407}
2408
2409QV4::ReturnedValue QQmlDelegateModelItem::get_model(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2410{
2411 QV4::Scope scope(b);
2412 QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
2413 if (!o)
2414 return b->engine()->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
2415 if (!o->d()->item->metaType->model)
2416 RETURN_UNDEFINED();
2417
2418 return o->d()->item->get();
2419}
2420
2421QV4::ReturnedValue QQmlDelegateModelItem::get_groups(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2422{
2423 QV4::Scope scope(b);
2424 QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
2425 if (!o)
2426 return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
2427
2428 QStringList groups;
2429 QQmlDelegateModelItem *item = o->d()->item;
2430 const auto &metaType = item->metaType;
2431 for (qsizetype i = 0, end = metaType->groupCount(); i < end; ++i) {
2432 if (item->groups & (1 << (i + 1)))
2433 groups.append(metaType->groupNames.at(i));
2434 }
2435
2436 return scope.engine->fromVariant(groups);
2437}
2438
2439QV4::ReturnedValue QQmlDelegateModelItem::set_groups(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2440{
2441 QV4::Scope scope(b);
2442 QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
2443 if (!o)
2444 return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
2445
2446 if (!argc)
2447 THROW_TYPE_ERROR();
2448
2449 QQmlDelegateModel *delegateModel = o->d()->item->metaType->delegateModel();
2450 if (!delegateModel)
2451 RETURN_UNDEFINED();
2452
2453 QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(delegateModel);
2454 const int groupFlags = model->m_cacheMetaType->parseGroups(argv[0]);
2455 const int cacheIndex = model->m_cache.indexOf(o->d()->item);
2456 Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex);
2457 model->setGroups(it, 1, Compositor::Cache, groupFlags);
2458 return QV4::Encode::undefined();
2459}
2460
2461QV4::ReturnedValue QQmlDelegateModelItem::get_member(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &)
2462{
2463 return QV4::Encode(bool(thisItem->groups & (1 << flag)));
2464}
2465
2466QV4::ReturnedValue QQmlDelegateModelItem::set_member(QQmlDelegateModelItem *cacheItem, uint flag, const QV4::Value &arg)
2467{
2468 bool member = arg.toBoolean();
2469 uint groupFlag = (1 << flag);
2470 if (member == ((cacheItem->groups & groupFlag) != 0))
2471 return QV4::Encode::undefined();
2472
2473 QQmlDelegateModel *delegateModel = cacheItem->metaType->delegateModel();
2474 if (!delegateModel)
2475 return QV4::Encode::undefined();
2476
2477 QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(delegateModel);
2478 const int cacheIndex = model->m_cache.indexOf(cacheItem);
2479 Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex);
2480 if (member)
2481 model->addGroups(it, 1, Compositor::Cache, groupFlag);
2482 else
2483 model->removeGroups(it, 1, Compositor::Cache, groupFlag);
2484 return QV4::Encode::undefined();
2485}
2486
2487QV4::ReturnedValue QQmlDelegateModelItem::get_index(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &)
2488{
2489 return QV4::Encode((int)thisItem->groupIndex(Compositor::Group(flag)));
2490}
2491
2492void QQmlDelegateModelItem::childContextObjectDestroyed(QObject *childContextObject)
2493{
2494 if (!contextData)
2495 return;
2496
2497 for (QQmlRefPointer<QQmlContextData> ctxt = contextData->childContexts(); ctxt;
2498 ctxt = ctxt->nextChild()) {
2499 ctxt->deepClearContextObject(childContextObject);
2500 }
2501}
2502
2503
2504//---------------------------------------------------------------------------
2505
2507
2509{
2511 Object::destroy();
2512}
2513
2514
2515QQmlDelegateModelItem::QQmlDelegateModelItem(
2516 const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
2517 QQmlAdaptorModel::Accessors *accessor,
2518 int modelIndex, int row, int column)
2519 : metaType(metaType)
2520 , index(modelIndex)
2521 , row(row)
2522 , column(column)
2523{
2524 if (accessor->propertyCache) {
2525 // The property cache in the accessor is common for all the model
2526 // items in the model it wraps. It describes available model roles,
2527 // together with revisioned properties like row, column and index, all
2528 // which should be available in the delegate. We assign this cache to the
2529 // model item so that the QML engine can use the revision information
2530 // when resolving the properties (rather than falling back to just
2531 // inspecting the QObject in the model item directly).
2532 QQmlData::get(this, true)->propertyCache = accessor->propertyCache;
2533 }
2534}
2535
2537{
2538 Q_ASSERT(scriptRef == 0);
2539 Q_ASSERT(objectRef == 0);
2540 Q_ASSERT(!object);
2541
2542 if (incubationTask) {
2543 if (QQmlDelegateModel *delegateModel = metaType->delegateModel())
2544 QQmlDelegateModelPrivate::get(delegateModel)->releaseIncubator(incubationTask);
2545 else
2546 delete incubationTask;
2547 }
2548}
2549
2551{
2552 --scriptRef;
2553 if (isReferenced())
2554 return;
2555
2556 if (QQmlDelegateModel *delegateModel = metaType->delegateModel())
2557 QQmlDelegateModelPrivate::get(delegateModel)->removeCacheItem(this);
2558
2559 delete this;
2560}
2561
2562void QQmlDelegateModelItem::setModelIndex(int idx, int newRow, int newColumn, bool alwaysEmit)
2563{
2564 const int prevIndex = index;
2565 const int prevRow = row;
2566 const int prevColumn = column;
2567
2568 index = idx;
2569 row = newRow;
2570 column = newColumn;
2571
2572 if (idx != prevIndex || alwaysEmit)
2573 emit modelIndexChanged();
2574 if (row != prevRow || alwaysEmit)
2575 emit rowChanged();
2576 if (column != prevColumn || alwaysEmit)
2577 emit columnChanged();
2578}
2579
2581{
2582 Q_ASSERT(object);
2583 Q_ASSERT(contextData);
2584
2585 QQmlData *data = QQmlData::get(object);
2586 Q_ASSERT(data);
2587 if (data->ownContext) {
2588 data->ownContext->clearContext();
2589 data->ownContext->deepClearContextObject(object);
2590 data->ownContext.reset();
2591 data->context = nullptr;
2592 }
2593 /* QTBUG-87228: when destroying object at the application exit, the deferred
2594 * parent by setting it to QCoreApplication instance if it's nullptr, so
2595 * deletion won't work. Not to leak memory, make sure our object has a that
2596 * the parent claims the object at the end of the lifetime. When not at the
2597 * application exit, normal event loop will handle the deferred deletion
2598 * earlier.
2599 */
2600 if (Q_UNLIKELY(static_cast<QCoreApplicationPrivate *>(QCoreApplicationPrivate::get(QCoreApplication::instance()))->aboutToQuitEmitted)) {
2601 if (object->parent() == nullptr)
2602 object->setParent(QCoreApplication::instance());
2603 }
2604 object->deleteLater();
2605
2606 if (QQmlDelegateModelAttached *attachedObject = attached())
2607 attachedObject->m_cacheItem = nullptr;
2608
2609 contextData.reset();
2610 object = nullptr;
2611}
2612
2613QQmlDelegateModelItem *QQmlDelegateModelItem::dataForObject(QObject *object)
2614{
2615 QQmlData *d = QQmlData::get(object);
2616 if (!d)
2617 return nullptr;
2618
2619 QQmlRefPointer<QQmlContextData> context = d->context;
2620 if (!context || !context->isValid())
2621 return nullptr;
2622
2623 if (QObject *extraObject = context->extraObject())
2624 return qobject_cast<QQmlDelegateModelItem *>(extraObject);
2625
2626 for (context = context->parent(); context; context = context->parent()) {
2627 if (QObject *extraObject = context->extraObject())
2628 return qobject_cast<QQmlDelegateModelItem *>(extraObject);
2629 if (QQmlDelegateModelItem *cacheItem = qobject_cast<QQmlDelegateModelItem *>(
2630 context->contextObject())) {
2631 return cacheItem;
2632 }
2633 }
2634 return nullptr;
2635}
2636
2637int QQmlDelegateModelItem::groupIndex(Compositor::Group group)
2638{
2639 if (QQmlDelegateModel *delegateModel = metaType->delegateModel()) {
2640 QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(delegateModel);
2641 return model->m_compositor.find(Compositor::Cache, model->m_cache.indexOf(this)).index[group];
2642 }
2643 return -1;
2644}
2645
2646//---------------------------------------------------------------------------
2647
2648QQmlDelegateModelAttachedMetaObject::QQmlDelegateModelAttachedMetaObject(
2649 QQmlDelegateModelItemMetaType *metaType, QMetaObject *metaObject)
2654 + metaType->groupCount())
2655{
2656 // Don't reference count the meta-type here as that would create a circular reference.
2657 // Instead we rely the fact that the meta-type's reference count can't reach 0 without first
2658 // destroying all delegates with attached objects.
2659 *static_cast<QMetaObject *>(this) = *metaObject;
2660}
2661
2662QQmlDelegateModelAttachedMetaObject::~QQmlDelegateModelAttachedMetaObject()
2663{
2664 ::free(metaObject);
2665}
2666
2667void QQmlDelegateModelAttachedMetaObject::objectDestroyed(QObject *)
2668{
2669 release();
2670}
2671
2672int QQmlDelegateModelAttachedMetaObject::metaCall(QObject *object, QMetaObject::Call call, int _id, void **arguments)
2673{
2674 QQmlDelegateModelAttached *attached = static_cast<QQmlDelegateModelAttached *>(object);
2675 if (call == QMetaObject::ReadProperty) {
2676 if (_id >= indexPropertyOffset) {
2677 Compositor::Group group = Compositor::Group(_id - indexPropertyOffset + 1);
2678 *static_cast<int *>(arguments[0]) = attached->m_currentIndex[group];
2679 return -1;
2680 } else if (_id >= memberPropertyOffset) {
2681 Compositor::Group group = Compositor::Group(_id - memberPropertyOffset + 1);
2682 *static_cast<bool *>(arguments[0]) = attached->m_cacheItem->groups & (1 << group);
2683 return -1;
2684 }
2685 } else if (call == QMetaObject::WriteProperty) {
2686 if (_id >= memberPropertyOffset) {
2687 QQmlDelegateModel *delegateModel = metaType->delegateModel();
2688 if (!delegateModel)
2689 return -1;
2690 QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(delegateModel);
2691 Compositor::Group group = Compositor::Group(_id - memberPropertyOffset + 1);
2692 const int groupFlag = 1 << group;
2693 const bool member = attached->m_cacheItem->groups & groupFlag;
2694 if (member && !*static_cast<bool *>(arguments[0])) {
2695 Compositor::iterator it = model->m_compositor.find(
2696 group, attached->m_currentIndex[group]);
2697 model->removeGroups(it, 1, group, groupFlag);
2698 } else if (!member && *static_cast<bool *>(arguments[0])) {
2699 for (qsizetype i = 1, end = metaType->groupCount(); i <= end; ++i) {
2700 if (attached->m_cacheItem->groups & (1 << i)) {
2701 Compositor::iterator it = model->m_compositor.find(
2702 Compositor::Group(i), attached->m_currentIndex[i]);
2703 model->addGroups(it, 1, Compositor::Group(i), groupFlag);
2704 break;
2705 }
2706 }
2707 }
2708 return -1;
2709 }
2710 }
2711 return attached->qt_metacall(call, _id, arguments);
2712}
2713
2714QQmlDelegateModelAttached::QQmlDelegateModelAttached(QObject *parent)
2715 : m_cacheItem(nullptr)
2716 , m_previousGroups(0)
2717{
2718 QQml_setParent_noEvent(this, parent);
2719}
2720
2722 QQmlDelegateModelItem *cacheItem, QObject *parent)
2723 : m_cacheItem(cacheItem)
2724 , m_previousGroups(cacheItem->groups)
2725{
2726 QQml_setParent_noEvent(this, parent);
2728 // Let m_previousIndex be equal to m_currentIndex
2729 std::copy(std::begin(m_currentIndex), std::end(m_currentIndex), std::begin(m_previousIndex));
2730
2731 if (!cacheItem->metaType->attachedMetaObject)
2732 cacheItem->metaType->initializeAttachedMetaObject();
2733
2734 QObjectPrivate::get(this)->metaObject = cacheItem->metaType->attachedMetaObject.data();
2735 cacheItem->metaType->attachedMetaObject->addref();
2736}
2737
2739
2741{
2742 const auto &metaType = m_cacheItem->metaType;
2743 if (QQDMIncubationTask *incubationTask = m_cacheItem->incubationTask) {
2744 for (qsizetype i = 1, end = metaType->groupCount(); i <= end; ++i)
2745 m_currentIndex[i] = incubationTask->index[i];
2746 } else if (QQmlDelegateModel *delegateModel = metaType->delegateModel()) {
2747 QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(delegateModel);
2748 Compositor::iterator it = model->m_compositor.find(
2749 Compositor::Cache, model->m_cache.indexOf(m_cacheItem));
2750 for (qsizetype i = 1, end = metaType->groupCount(); i <= end; ++i)
2751 m_currentIndex[i] = it.index[i];
2752 }
2753}
2754
2756{
2757 setInGroup(QQmlListCompositor::Persisted, inPersisted);
2758}
2759
2761{
2762 if (!m_cacheItem)
2763 return false;
2764 const uint groupFlag = (1 << QQmlListCompositor::Persisted);
2765 return m_cacheItem->groups & groupFlag;
2766}
2767
2769{
2770 if (!m_cacheItem)
2771 return -1;
2772 return m_cacheItem->groupIndex(QQmlListCompositor::Persisted);
2773}
2774
2775void QQmlDelegateModelAttached::setInGroup(QQmlListCompositor::Group group, bool inGroup)
2776{
2777 if (!m_cacheItem)
2778 return;
2779
2780 QQmlDelegateModel *delegateModel = m_cacheItem->metaType->delegateModel();
2781 if (!delegateModel)
2782 return;
2783
2784 QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(delegateModel);
2785 const uint groupFlag = (1 << group);
2786 if (inGroup == bool(m_cacheItem->groups & groupFlag))
2787 return;
2788
2789 const int cacheIndex = model->m_cache.indexOf(m_cacheItem);
2790 Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex);
2791 if (inGroup)
2792 model->addGroups(it, 1, Compositor::Cache, groupFlag);
2793 else
2794 model->removeGroups(it, 1, Compositor::Cache, groupFlag);
2795 // signal emission happens in add-/removeGroups
2796}
2797
2799{
2800 setInGroup(QQmlListCompositor::Default, inItems);
2801}
2802
2804{
2805 if (!m_cacheItem)
2806 return false;
2807 const uint groupFlag = (1 << QQmlListCompositor::Default);
2808 return m_cacheItem->groups & groupFlag;
2809}
2810
2812{
2813 if (!m_cacheItem)
2814 return -1;
2815 return m_cacheItem->groupIndex(QQmlListCompositor::Default);
2816}
2817
2818/*!
2819 \qmlattachedproperty model QtQml.Models::DelegateModel::model
2820
2821 This attached property holds the data model this delegate instance belongs to.
2822
2823 It is attached to each instance of the delegate.
2824*/
2825
2827{
2828 return m_cacheItem ? m_cacheItem->metaType->delegateModel() : nullptr;
2829}
2830
2831/*!
2832 \qmlattachedproperty stringlist QtQml.Models::DelegateModel::groups
2833
2834 This attached property holds the name of DelegateModelGroups the item belongs to.
2835
2836 It is attached to each instance of the delegate.
2837*/
2838
2840{
2841 QStringList groups;
2842
2843 if (!m_cacheItem)
2844 return groups;
2845 const auto &metaType = m_cacheItem->metaType;
2846 for (qsizetype i = 0, end = metaType->groupCount(); i < end; ++i) {
2847 if (m_cacheItem->groups & (1 << (i + 1)))
2848 groups.append(metaType->groupNames.at(i));
2849 }
2850 return groups;
2851}
2852
2853void QQmlDelegateModelAttached::setGroups(const QStringList &groups)
2854{
2855 if (!m_cacheItem)
2856 return;
2857
2858 QQmlDelegateModel *delegateModel = m_cacheItem->metaType->delegateModel();
2859 if (!delegateModel)
2860 return;
2861
2862 QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(delegateModel);
2863 const int groupFlags = model->m_cacheMetaType->parseGroups(groups);
2864 const int cacheIndex = model->m_cache.indexOf(m_cacheItem);
2865 Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex);
2866 model->setGroups(it, 1, Compositor::Cache, groupFlags);
2867}
2868
2869/*!
2870 \qmlattachedproperty bool QtQml.Models::DelegateModel::isUnresolved
2871
2872 This attached property indicates whether the visual item is bound to a data model index.
2873 Returns true if the item is not bound to the model, and false if it is.
2874
2875 An unresolved item can be bound to the data model using the DelegateModelGroup::resolve()
2876 function.
2877
2878 It is attached to each instance of the delegate.
2879*/
2880
2882{
2883 if (!m_cacheItem)
2884 return false;
2885
2886 return m_cacheItem->groups & Compositor::UnresolvedFlag;
2887}
2888
2889/*!
2890 \qmlattachedproperty bool QtQml.Models::DelegateModel::inItems
2891
2892 This attached property holds whether the item belongs to the default \l items
2893 DelegateModelGroup.
2894
2895 Changing this property will add or remove the item from the items group.
2896
2897 It is attached to each instance of the delegate.
2898*/
2899
2900/*!
2901 \qmlattachedproperty int QtQml.Models::DelegateModel::itemsIndex
2902
2903 This attached property holds the index of the item in the default \l items DelegateModelGroup.
2904
2905 It is attached to each instance of the delegate.
2906*/
2907
2908/*!
2909 \qmlattachedproperty bool QtQml.Models::DelegateModel::inPersistedItems
2910
2911 This attached property holds whether the item belongs to the \l persistedItems
2912 DelegateModelGroup.
2913
2914 Changing this property will add or remove the item from the items group. Change with caution
2915 as removing an item from the persistedItems group will destroy the current instance if it is
2916 not referenced by a model.
2917
2918 It is attached to each instance of the delegate.
2919*/
2920
2921/*!
2922 \qmlattachedproperty int QtQml.Models::DelegateModel::persistedItemsIndex
2923
2924 This attached property holds the index of the item in the \l persistedItems DelegateModelGroup.
2925
2926 It is attached to each instance of the delegate.
2927*/
2928
2930{
2931 const int groupChanges = m_previousGroups ^ m_cacheItem->groups;
2933
2934 int indexChanges = 0;
2935 const qsizetype groupCount = m_cacheItem->metaType->groupCount();
2936 for (qsizetype i = 1; i <= groupCount; ++i) {
2937 if (m_previousIndex[i] != m_currentIndex[i]) {
2938 m_previousIndex[i] = m_currentIndex[i];
2939 indexChanges |= (1 << i);
2940 }
2941 }
2942
2943 // Don't access m_cacheItem anymore once we've started sending signals.
2944 // We don't own it and someone might delete it.
2945
2946 int notifierId = 0;
2947 const QMetaObject *meta = metaObject();
2948 for (qsizetype i = 1; i <= groupCount; ++i, ++notifierId) {
2949 if (groupChanges & (1 << i))
2950 QMetaObject::activate(this, meta, notifierId, nullptr);
2951 }
2952 for (qsizetype i = 1; i <= groupCount; ++i, ++notifierId) {
2953 if (indexChanges & (1 << i))
2954 QMetaObject::activate(this, meta, notifierId, nullptr);
2955 }
2956
2957 if (groupChanges)
2958 emit groupsChanged();
2959}
2960
2961//============================================================================
2962
2963void QQmlDelegateModelGroupPrivate::setModel(QQmlDelegateModel *m, Compositor::Group g)
2964{
2965 Q_ASSERT(!model);
2966 model = m;
2967 group = g;
2968}
2969
2971{
2972 Q_Q(QQmlDelegateModelGroup);
2973 IS_SIGNAL_CONNECTED(q, QQmlDelegateModelGroup, changed, (const QJSValue &,const QJSValue &));
2974}
2975
2976void QQmlDelegateModelGroupPrivate::emitChanges(QV4::ExecutionEngine *v4)
2977{
2978 Q_Q(QQmlDelegateModelGroup);
2979 if (isChangedConnected() && !changeSet.isEmpty()) {
2980 emit q->changed(QJSValuePrivate::fromReturnedValue(
2981 qdmEngineData(v4)->array(v4, changeSet.removes())),
2982 QJSValuePrivate::fromReturnedValue(
2983 qdmEngineData(v4)->array(v4, changeSet.inserts())));
2984 }
2985 if (changeSet.difference() != 0)
2986 emit q->countChanged();
2987}
2988
2990{
2991 for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it)
2992 it->emitModelUpdated(changeSet, reset);
2993 changeSet.clear();
2994}
2995
2997
2998void QQmlDelegateModelGroupPrivate::createdPackage(int index, QQuickPackage *package)
2999{
3000 for (GroupEmitterListIt it = emitters.begin(), end = emitters.end(); it != end; ++it)
3001 it->createdPackage(index, package);
3002}
3003
3004void QQmlDelegateModelGroupPrivate::initPackage(int index, QQuickPackage *package)
3005{
3006 for (GroupEmitterListIt it = emitters.begin(), end = emitters.end(); it != end; ++it)
3007 it->initPackage(index, package);
3008}
3009
3011{
3012 for (GroupEmitterListIt it = emitters.begin(), end = emitters.end(); it != end; ++it)
3013 it->destroyingPackage(package);
3014}
3015
3016/*!
3017 \qmltype DelegateModelGroup
3018 \nativetype QQmlDelegateModelGroup
3019 \inqmlmodule QtQml.Models
3020 \ingroup qtquick-models
3021 \brief Encapsulates a filtered set of visual data items.
3022
3023 The DelegateModelGroup type provides a means to address the model data of a
3024 DelegateModel's delegate items, as well as sort and filter these delegate
3025 items.
3026
3027 The initial set of instantiable delegate items in a DelegateModel is represented
3028 by its \l {QtQml.Models::DelegateModel::items}{items} group, which normally directly reflects
3029 the contents of the model assigned to DelegateModel::model. This set can be changed to
3030 the contents of any other member of DelegateModel::groups by assigning the \l name of that
3031 DelegateModelGroup to the DelegateModel::filterOnGroup property.
3032
3033 The data of an item in a DelegateModelGroup can be accessed using the get() function, which returns
3034 information about group membership and indexes as well as model data. In combination
3035 with the move() function this can be used to implement view sorting, with remove() to filter
3036 items out of a view, or with setGroups() and \l Package delegates to categorize items into
3037 different views. Different groups can only be sorted independently if they are disjunct. Moving
3038 an item in one group will also move it in all other groups it is a part of.
3039
3040 Data from models can be supplemented by inserting data directly into a DelegateModelGroup
3041 with the insert() function. This can be used to introduce mock items into a view, or
3042 placeholder items that are later \l {resolve()}{resolved} to real model data when it becomes
3043 available.
3044
3045 Delegate items can also be instantiated directly from a DelegateModelGroup using the
3046 create() function, making it possible to use DelegateModel without an accompanying view
3047 type or to cherry-pick specific items that should be instantiated irregardless of whether
3048 they're currently within a view's visible area.
3049
3050 \sa {QML Dynamic View Ordering Tutorial}
3051*/
3052QQmlDelegateModelGroup::QQmlDelegateModelGroup(QObject *parent)
3053 : QObject(*new QQmlDelegateModelGroupPrivate, parent)
3054{
3055}
3056
3057QQmlDelegateModelGroup::QQmlDelegateModelGroup(
3058 const QString &name, QQmlDelegateModel *model, int index, QObject *parent)
3059 : QQmlDelegateModelGroup(parent)
3060{
3061 Q_D(QQmlDelegateModelGroup);
3062 d->name = name;
3063 d->setModel(model, Compositor::Group(index));
3064}
3065
3066QQmlDelegateModelGroup::~QQmlDelegateModelGroup() = default;
3067
3068/*!
3069 \qmlproperty string QtQml.Models::DelegateModelGroup::name
3070
3071 This property holds the name of the group.
3072
3073 Each group in a model must have a unique name starting with a lower case letter.
3074*/
3075
3076QString QQmlDelegateModelGroup::name() const
3077{
3078 Q_D(const QQmlDelegateModelGroup);
3079 return d->name;
3080}
3081
3082void QQmlDelegateModelGroup::setName(const QString &name)
3083{
3084 Q_D(QQmlDelegateModelGroup);
3085 if (d->model)
3086 return;
3087 if (d->name != name) {
3088 d->name = name;
3089 emit nameChanged();
3090 }
3091}
3092
3093/*!
3094 \qmlproperty int QtQml.Models::DelegateModelGroup::count
3095
3096 This property holds the number of items in the group.
3097*/
3098
3099int QQmlDelegateModelGroup::count() const
3100{
3101 Q_D(const QQmlDelegateModelGroup);
3102 if (!d->model)
3103 return 0;
3104 return QQmlDelegateModelPrivate::get(d->model)->m_compositor.count(d->group);
3105}
3106
3107/*!
3108 \qmlproperty bool QtQml.Models::DelegateModelGroup::includeByDefault
3109
3110 This property holds whether new items are assigned to this group by default.
3111*/
3112
3113bool QQmlDelegateModelGroup::defaultInclude() const
3114{
3115 Q_D(const QQmlDelegateModelGroup);
3116 return d->defaultInclude;
3117}
3118
3119void QQmlDelegateModelGroup::setDefaultInclude(bool include)
3120{
3121 Q_D(QQmlDelegateModelGroup);
3122 if (d->defaultInclude != include) {
3123 d->defaultInclude = include;
3124
3125 if (d->model) {
3126 if (include)
3127 QQmlDelegateModelPrivate::get(d->model)->m_compositor.setDefaultGroup(d->group);
3128 else
3129 QQmlDelegateModelPrivate::get(d->model)->m_compositor.clearDefaultGroup(d->group);
3130 }
3131 emit defaultIncludeChanged();
3132 }
3133}
3134
3135/*!
3136 \qmlmethod object QtQml.Models::DelegateModelGroup::get(int index)
3137
3138 Returns a javascript object describing the item at \a index in the group.
3139
3140 The returned object contains the same information that is available to a delegate from the
3141 DelegateModel attached as well as the model for that item. It has the properties:
3142
3143 \list
3144 \li \b model The model data of the item. This is the same as the model context property in
3145 a delegate
3146 \li \b groups A list the of names of groups the item is a member of. This property can be
3147 written to change the item's membership.
3148 \li \b inItems Whether the item belongs to the \l {QtQml.Models::DelegateModel::items}{items} group.
3149 Writing to this property will add or remove the item from the group.
3150 \li \b itemsIndex The index of the item within the \l {QtQml.Models::DelegateModel::items}{items} group.
3151 \li \b {in<GroupName>} Whether the item belongs to the dynamic group \e groupName. Writing to
3152 this property will add or remove the item from the group.
3153 \li \b {<groupName>Index} The index of the item within the dynamic group \e groupName.
3154 \li \b isUnresolved Whether the item is bound to an index in the model assigned to
3155 DelegateModel::model. Returns true if the item is not bound to the model, and false if it is.
3156 \endlist
3157*/
3158
3159QJSValue QQmlDelegateModelGroup::get(int index)
3160{
3161 Q_D(QQmlDelegateModelGroup);
3162 if (!d->model)
3163 return QJSValue();
3164
3165 QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
3166 if (!model->m_context || !model->m_context->isValid()) {
3167 return QJSValue();
3168 } else if (index < 0 || index >= model->m_compositor.count(d->group)) {
3169 qmlWarning(this) << tr("get: index out of range");
3170 return QJSValue();
3171 }
3172
3173 Compositor::iterator it = model->m_compositor.find(d->group, index);
3174 QQmlDelegateModelItem *cacheItem = it->inCache()
3175 ? model->m_cache.at(it.cacheIndex())
3176 : 0;
3177
3178 if (!cacheItem) {
3179 cacheItem = model->m_adaptorModel.createItem(
3180 model->m_cacheMetaType, it.modelIndex());
3181 if (!cacheItem)
3182 return QJSValue();
3183 cacheItem->groups = it->flags;
3184
3185 model->m_cache.insert(it.cacheIndex(), cacheItem);
3186 model->m_compositor.setFlags(it, 1, Compositor::CacheFlag);
3187 }
3188
3189 if (model->m_cacheMetaType->modelItemProto.isUndefined())
3190 model->m_cacheMetaType->initializePrototype();
3191 QV4::ExecutionEngine *v4 = model->m_cacheMetaType->v4Engine;
3192 QV4::Scope scope(v4);
3193 ++cacheItem->scriptRef;
3194 QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(cacheItem));
3195 QV4::ScopedObject p(scope, model->m_cacheMetaType->modelItemProto.value());
3196 o->setPrototypeOf(p);
3197
3198 return QJSValuePrivate::fromReturnedValue(o->asReturnedValue());
3199}
3200
3201bool QQmlDelegateModelGroupPrivate::parseIndex(const QV4::Value &value, int *index, Compositor::Group *group) const
3202{
3203 if (value.isNumber()) {
3204 *index = value.toInt32();
3205 return true;
3206 }
3207
3208 if (!value.isObject())
3209 return false;
3210
3211 QV4::ExecutionEngine *v4 = value.as<QV4::Object>()->engine();
3212 QV4::Scope scope(v4);
3213 QV4::Scoped<QQmlDelegateModelItemObject> object(scope, value);
3214
3215 if (object) {
3216 QQmlDelegateModelItem * const cacheItem = object->d()->item;
3217 if (QQmlDelegateModel *delegateModel = cacheItem->metaType->delegateModel()) {
3218 *index = QQmlDelegateModelPrivate::get(delegateModel)->m_cache.indexOf(cacheItem);
3219 *group = Compositor::Cache;
3220 return true;
3221 }
3222 }
3223 return false;
3224}
3225
3226/*!
3227 \qmlmethod QtQml.Models::DelegateModelGroup::insert(int index, jsdict data, array groups = undefined)
3228 \qmlmethod QtQml.Models::DelegateModelGroup::insert(jsdict data, var groups = undefined)
3229
3230 Creates a new entry at \a index in a DelegateModel with the values from \a data that
3231 correspond to roles in the model assigned to DelegateModel::model.
3232
3233 If no index is supplied the data is appended to the model.
3234
3235 The optional \a groups parameter identifies the groups the new entry should belong to,
3236 if unspecified this is equal to the group insert was called on.
3237
3238 Data inserted into a DelegateModel can later be merged with an existing entry in
3239 DelegateModel::model using the \l resolve() function. This can be used to create placeholder
3240 items that are later replaced by actual data.
3241*/
3242
3243void QQmlDelegateModelGroup::insert(QQmlV4FunctionPtr args)
3244{
3245 Q_D(QQmlDelegateModelGroup);
3246 QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
3247
3248 int index = model->m_compositor.count(d->group);
3249 Compositor::Group group = d->group;
3250
3251 if (args->length() == 0)
3252 return;
3253
3254 int i = 0;
3255 QV4::Scope scope(args->v4engine());
3256 QV4::ScopedValue v(scope, (*args)[i]);
3257 if (d->parseIndex(v, &index, &group)) {
3258 if (index < 0 || index > model->m_compositor.count(group)) {
3259 qmlWarning(this) << tr("insert: index out of range");
3260 return;
3261 }
3262 if (++i == args->length())
3263 return;
3264 v = (*args)[i];
3265 }
3266
3267 if (v->as<QV4::ArrayObject>())
3268 return;
3269
3270 int groups = 1 << d->group;
3271 if (++i < args->length()) {
3272 QV4::ScopedValue val(scope, (*args)[i]);
3273 groups |= model->m_cacheMetaType->parseGroups(val);
3274 }
3275
3276 if (v->as<QV4::Object>()) {
3277 auto insertionResult = QQmlDelegateModelPrivate::InsertionResult::Retry;
3278 do {
3279 Compositor::insert_iterator before = index < model->m_compositor.count(group)
3280 ? model->m_compositor.findInsertPosition(group, index)
3281 : model->m_compositor.end();
3282 insertionResult = model->insert(before, v, groups);
3283 } while (insertionResult == QQmlDelegateModelPrivate::InsertionResult::Retry);
3284 if (insertionResult == QQmlDelegateModelPrivate::InsertionResult::Success)
3285 model->emitChanges();
3286 }
3287}
3288
3289/*!
3290 \qmlmethod QtQml.Models::DelegateModelGroup::create(int index)
3291 \qmlmethod QtQml.Models::DelegateModelGroup::create(int index, jsdict data, array groups = undefined)
3292 \qmlmethod QtQml.Models::DelegateModelGroup::create(jsdict data, array groups = undefined)
3293
3294 Returns a reference to the instantiated item at \a index in the group.
3295
3296 If a \a data object is provided it will be \l {insert}{inserted} at \a index and an item
3297 referencing this new entry will be returned. The optional \a groups parameter identifies
3298 the groups the new entry should belong to, if unspecified this is equal to the group create()
3299 was called on.
3300
3301 All items returned by create are added to the
3302 \l {QtQml.Models::DelegateModel::persistedItems}{persistedItems} group. Items in this
3303 group remain instantiated when not referenced by any view.
3304*/
3305
3306void QQmlDelegateModelGroup::create(QQmlV4FunctionPtr args)
3307{
3308 Q_D(QQmlDelegateModelGroup);
3309 if (!d->model)
3310 return;
3311
3312 if (args->length() == 0)
3313 return;
3314
3315 QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
3316
3317 int index = model->m_compositor.count(d->group);
3318 Compositor::Group group = d->group;
3319
3320 int i = 0;
3321 QV4::Scope scope(args->v4engine());
3322 QV4::ScopedValue v(scope, (*args)[i]);
3323 if (d->parseIndex(v, &index, &group))
3324 ++i;
3325
3326 if (i < args->length() && index >= 0 && index <= model->m_compositor.count(group)) {
3327 v = (*args)[i];
3328 if (v->as<QV4::Object>()) {
3329 int groups = 1 << d->group;
3330 if (++i < args->length()) {
3331 QV4::ScopedValue val(scope, (*args)[i]);
3332 groups |= model->m_cacheMetaType->parseGroups(val);
3333 }
3334
3335 auto insertionResult = QQmlDelegateModelPrivate::InsertionResult::Retry;
3336 do {
3337 Compositor::insert_iterator before = index < model->m_compositor.count(group)
3338 ? model->m_compositor.findInsertPosition(group, index)
3339 : model->m_compositor.end();
3340
3341 index = before.index[d->group];
3342 group = d->group;
3343
3344 insertionResult = model->insert(before, v, groups);
3345 } while (insertionResult == QQmlDelegateModelPrivate::InsertionResult::Retry);
3346 if (insertionResult == QQmlDelegateModelPrivate::InsertionResult::Error)
3347 return;
3348 }
3349 }
3350 if (index < 0 || index >= model->m_compositor.count(group)) {
3351 qmlWarning(this) << tr("create: index out of range");
3352 return;
3353 }
3354
3355 QObject *object = model->object(group, index, QQmlIncubator::AsynchronousIfNested);
3356 if (object) {
3357 QVector<Compositor::Insert> inserts;
3358 Compositor::iterator it = model->m_compositor.find(group, index);
3359 model->m_compositor.setFlags(it, 1, d->group, Compositor::PersistedFlag, &inserts);
3360 model->itemsInserted(inserts);
3361 model->m_cache.at(it.cacheIndex())->releaseObject();
3362 }
3363
3364 args->setReturnValue(QV4::QObjectWrapper::wrap(args->v4engine(), object));
3365 model->emitChanges();
3366}
3367
3368/*!
3369 \qmlmethod QtQml.Models::DelegateModelGroup::resolve(int from, int to)
3370
3371 Binds an unresolved item at \a from to an item in DelegateModel::model at index \a to.
3372
3373 Unresolved items are entries whose data has been \l {insert()}{inserted} into a DelegateModelGroup
3374 instead of being derived from a DelegateModel::model index. Resolving an item will replace
3375 the item at the target index with the unresolved item. A resolved an item will reflect the data
3376 of the source model at its bound index and will move when that index moves like any other item.
3377
3378 If a new item is replaced in the DelegateModelGroup onChanged() handler its insertion and
3379 replacement will be communicated to views as an atomic operation, creating the appearance
3380 that the model contents have not changed, or if the unresolved and model item are not adjacent
3381 that the previously unresolved item has simply moved.
3382
3383*/
3384void QQmlDelegateModelGroup::resolve(QQmlV4FunctionPtr args)
3385{
3386 Q_D(QQmlDelegateModelGroup);
3387 if (!d->model)
3388 return;
3389
3390 QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
3391
3392 if (args->length() < 2)
3393 return;
3394
3395 int from = -1;
3396 int to = -1;
3397 Compositor::Group fromGroup = d->group;
3398 Compositor::Group toGroup = d->group;
3399
3400 QV4::Scope scope(args->v4engine());
3401 QV4::ScopedValue v(scope, (*args)[0]);
3402 if (d->parseIndex(v, &from, &fromGroup)) {
3403 if (from < 0 || from >= model->m_compositor.count(fromGroup)) {
3404 qmlWarning(this) << tr("resolve: from index out of range");
3405 return;
3406 }
3407 } else {
3408 qmlWarning(this) << tr("resolve: from index invalid");
3409 return;
3410 }
3411
3412 v = (*args)[1];
3413 if (d->parseIndex(v, &to, &toGroup)) {
3414 if (to < 0 || to >= model->m_compositor.count(toGroup)) {
3415 qmlWarning(this) << tr("resolve: to index out of range");
3416 return;
3417 }
3418 } else {
3419 qmlWarning(this) << tr("resolve: to index invalid");
3420 return;
3421 }
3422
3423 Compositor::iterator fromIt = model->m_compositor.find(fromGroup, from);
3424 Compositor::iterator toIt = model->m_compositor.find(toGroup, to);
3425
3426 if (!fromIt->isUnresolved()) {
3427 qmlWarning(this) << tr("resolve: from is not an unresolved item");
3428 return;
3429 }
3430 if (!toIt->list) {
3431 qmlWarning(this) << tr("resolve: to is not a model item");
3432 return;
3433 }
3434
3435 const int unresolvedFlags = fromIt->flags;
3436 const int resolvedFlags = toIt->flags;
3437 const int resolvedIndex = toIt.modelIndex();
3438 void * const resolvedList = toIt->list;
3439
3440 QQmlDelegateModelItem *cacheItem = model->m_cache.at(fromIt.cacheIndex());
3441 cacheItem->groups &= ~Compositor::UnresolvedFlag;
3442
3443 if (toIt.cacheIndex() > fromIt.cacheIndex())
3444 toIt.decrementIndexes(1, unresolvedFlags);
3445 if (!toIt->inGroup(fromGroup) || toIt.index[fromGroup] > from)
3446 from += 1;
3447
3448 model->itemsMoved(
3449 QVector<Compositor::Remove>(1, Compositor::Remove(fromIt, 1, unresolvedFlags, 0)),
3450 QVector<Compositor::Insert>(1, Compositor::Insert(toIt, 1, unresolvedFlags, 0)));
3451 model->itemsInserted(
3452 QVector<Compositor::Insert>(1, Compositor::Insert(toIt, 1, (resolvedFlags & ~unresolvedFlags) | Compositor::CacheFlag)));
3453 toIt.incrementIndexes(1, resolvedFlags | unresolvedFlags);
3454 model->itemsRemoved(QVector<Compositor::Remove>(1, Compositor::Remove(toIt, 1, resolvedFlags)));
3455
3456 model->m_compositor.setFlags(toGroup, to, 1, unresolvedFlags & ~Compositor::UnresolvedFlag);
3457 model->m_compositor.clearFlags(fromGroup, from, 1, unresolvedFlags);
3458
3459 if (resolvedFlags & Compositor::CacheFlag)
3460 model->m_compositor.insert(
3461 Compositor::Cache, toIt.cacheIndex(), resolvedList,
3462 resolvedIndex, 1, Compositor::CacheFlag);
3463
3464 Q_ASSERT(model->m_cache.size() == model->m_compositor.count(Compositor::Cache));
3465
3466 if (!cacheItem->isReferenced()) {
3467 Q_ASSERT(toIt.cacheIndex() == model->m_cache.indexOf(cacheItem));
3468 model->m_cache.removeAt(toIt.cacheIndex());
3469 model->m_compositor.clearFlags(
3470 Compositor::Cache, toIt.cacheIndex(), 1, Compositor::CacheFlag);
3471 delete cacheItem;
3472 Q_ASSERT(model->m_cache.size() == model->m_compositor.count(Compositor::Cache));
3473 } else {
3474 cacheItem->resolveIndex(model->m_adaptorModel, resolvedIndex);
3475 if (QQmlDelegateModelAttached *attached = cacheItem->attached())
3476 attached->emitUnresolvedChanged();
3477 }
3478
3479 model->emitChanges();
3480}
3481
3482/*!
3483 \qmlmethod QtQml.Models::DelegateModelGroup::remove(int index, int count)
3484
3485 Removes \a count items starting at \a index from the group.
3486*/
3487
3488void QQmlDelegateModelGroup::remove(QQmlV4FunctionPtr args)
3489{
3490 Q_D(QQmlDelegateModelGroup);
3491 if (!d->model)
3492 return;
3493 Compositor::Group group = d->group;
3494 int index = -1;
3495 int count = 1;
3496
3497 if (args->length() == 0)
3498 return;
3499
3500 int i = 0;
3501 QV4::Scope scope(args->v4engine());
3502 QV4::ScopedValue v(scope, (*args)[0]);
3503 if (!d->parseIndex(v, &index, &group)) {
3504 qmlWarning(this) << tr("remove: invalid index");
3505 return;
3506 }
3507
3508 if (++i < args->length()) {
3509 v = (*args)[i];
3510 if (v->isNumber())
3511 count = v->toInt32();
3512 }
3513
3514 QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
3515 if (index < 0 || index >= model->m_compositor.count(group)) {
3516 qmlWarning(this) << tr("remove: index out of range");
3517 } else if (count != 0) {
3518 Compositor::iterator it = model->m_compositor.find(group, index);
3519 if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) {
3520 qmlWarning(this) << tr("remove: invalid count");
3521 } else {
3522 model->removeGroups(it, count, d->group, 1 << d->group);
3523 }
3524 }
3525}
3526
3528 QQmlV4FunctionPtr args, Compositor::Group *group, int *index, int *count, int *groups) const
3529{
3530 if (!model || !QQmlDelegateModelPrivate::get(model)->m_cacheMetaType)
3531 return false;
3532
3533 if (args->length() < 2)
3534 return false;
3535
3536 int i = 0;
3537 QV4::Scope scope(args->v4engine());
3538 QV4::ScopedValue v(scope, (*args)[i]);
3539 if (!parseIndex(v, index, group))
3540 return false;
3541
3542 v = (*args)[++i];
3543 if (v->isNumber()) {
3544 *count = v->toInt32();
3545
3546 if (++i == args->length())
3547 return false;
3548 v = (*args)[i];
3549 }
3550
3551 *groups = QQmlDelegateModelPrivate::get(model)->m_cacheMetaType->parseGroups(v);
3552
3553 return true;
3554}
3555
3556/*!
3557 \qmlmethod QtQml.Models::DelegateModelGroup::addGroups(int index, int count, stringlist groups)
3558
3559 Adds \a count items starting at \a index to \a groups.
3560*/
3561
3562void QQmlDelegateModelGroup::addGroups(QQmlV4FunctionPtr args)
3563{
3564 Q_D(QQmlDelegateModelGroup);
3565 Compositor::Group group = d->group;
3566 int index = -1;
3567 int count = 1;
3568 int groups = 0;
3569
3570 if (!d->parseGroupArgs(args, &group, &index, &count, &groups))
3571 return;
3572
3573 QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
3574 if (index < 0 || index >= model->m_compositor.count(group)) {
3575 qmlWarning(this) << tr("addGroups: index out of range");
3576 } else if (count != 0) {
3577 Compositor::iterator it = model->m_compositor.find(group, index);
3578 if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) {
3579 qmlWarning(this) << tr("addGroups: invalid count");
3580 } else {
3581 model->addGroups(it, count, d->group, groups);
3582 }
3583 }
3584}
3585
3586/*!
3587 \qmlmethod QtQml.Models::DelegateModelGroup::removeGroups(int index, int count, stringlist groups)
3588
3589 Removes \a count items starting at \a index from \a groups.
3590*/
3591
3592void QQmlDelegateModelGroup::removeGroups(QQmlV4FunctionPtr args)
3593{
3594 Q_D(QQmlDelegateModelGroup);
3595 Compositor::Group group = d->group;
3596 int index = -1;
3597 int count = 1;
3598 int groups = 0;
3599
3600 if (!d->parseGroupArgs(args, &group, &index, &count, &groups))
3601 return;
3602
3603 QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
3604 if (index < 0 || index >= model->m_compositor.count(group)) {
3605 qmlWarning(this) << tr("removeGroups: index out of range");
3606 } else if (count != 0) {
3607 Compositor::iterator it = model->m_compositor.find(group, index);
3608 if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) {
3609 qmlWarning(this) << tr("removeGroups: invalid count");
3610 } else {
3611 model->removeGroups(it, count, d->group, groups);
3612 }
3613 }
3614}
3615
3616/*!
3617 \qmlmethod QtQml.Models::DelegateModelGroup::setGroups(int index, int count, stringlist groups)
3618
3619 Changes the group membership of \a count items starting at \a index. The items are removed from
3620 their existing groups and added to \a groups.
3621*/
3622
3623void QQmlDelegateModelGroup::setGroups(QQmlV4FunctionPtr args)
3624{
3625 Q_D(QQmlDelegateModelGroup);
3626 Compositor::Group group = d->group;
3627 int index = -1;
3628 int count = 1;
3629 int groups = 0;
3630
3631 if (!d->parseGroupArgs(args, &group, &index, &count, &groups))
3632 return;
3633
3634 QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
3635 if (index < 0 || index >= model->m_compositor.count(group)) {
3636 qmlWarning(this) << tr("setGroups: index out of range");
3637 } else if (count != 0) {
3638 Compositor::iterator it = model->m_compositor.find(group, index);
3639 if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) {
3640 qmlWarning(this) << tr("setGroups: invalid count");
3641 } else {
3642 model->setGroups(it, count, d->group, groups);
3643 }
3644 }
3645}
3646
3647/*!
3648 \qmlmethod QtQml.Models::DelegateModelGroup::move(var from, var to, int count)
3649
3650 Moves \a count at \a from in a group \a to a new position.
3651
3652 \note The DelegateModel acts as a proxy model: it holds the delegates in a
3653 different order than the \l{dm-model-property}{underlying model} has them.
3654 Any subsequent changes to the underlying model will not undo whatever
3655 reordering you have done via this function.
3656*/
3657
3658void QQmlDelegateModelGroup::move(QQmlV4FunctionPtr args)
3659{
3660 Q_D(QQmlDelegateModelGroup);
3661
3662 if (args->length() < 2)
3663 return;
3664
3665 Compositor::Group fromGroup = d->group;
3666 Compositor::Group toGroup = d->group;
3667 int from = -1;
3668 int to = -1;
3669 int count = 1;
3670
3671 QV4::Scope scope(args->v4engine());
3672 QV4::ScopedValue v(scope, (*args)[0]);
3673 if (!d->parseIndex(v, &from, &fromGroup)) {
3674 qmlWarning(this) << tr("move: invalid from index");
3675 return;
3676 }
3677
3678 v = (*args)[1];
3679 if (!d->parseIndex(v, &to, &toGroup)) {
3680 qmlWarning(this) << tr("move: invalid to index");
3681 return;
3682 }
3683
3684 if (args->length() > 2) {
3685 v = (*args)[2];
3686 if (v->isNumber())
3687 count = v->toInt32();
3688 }
3689
3690 QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
3691
3692 if (count < 0) {
3693 qmlWarning(this) << tr("move: invalid count");
3694 } else if (from < 0 || from + count > model->m_compositor.count(fromGroup)) {
3695 qmlWarning(this) << tr("move: from index out of range");
3696 } else if (!model->m_compositor.verifyMoveTo(fromGroup, from, toGroup, to, count, d->group)) {
3697 qmlWarning(this) << tr("move: to index out of range");
3698 } else if (count > 0) {
3699 QVector<Compositor::Remove> removes;
3700 QVector<Compositor::Insert> inserts;
3701
3702 model->m_compositor.move(fromGroup, from, toGroup, to, count, d->group, &removes, &inserts);
3703 model->itemsMoved(removes, inserts);
3704 model->emitChanges();
3705 }
3706
3707}
3708
3709/*!
3710 \qmlsignal QtQml.Models::DelegateModelGroup::changed(array removed, array inserted)
3711
3712 This signal is emitted when items have been removed from or inserted into the group.
3713
3714 Each object in the \a removed and \a inserted arrays has two values; the \e index of the first
3715 item inserted or removed and a \e count of the number of consecutive items inserted or removed.
3716
3717 Each index is adjusted for previous changes with all removed items preceding any inserted
3718 items.
3719*/
3720
3721//============================================================================
3722
3723QQmlPartsModel::QQmlPartsModel(QQmlDelegateModel *model, const QString &part, QObject *parent)
3724 : QQmlInstanceModel(*new QObjectPrivate, parent)
3725 , m_model(model)
3726 , m_part(part)
3727 , m_compositorGroup(Compositor::Cache)
3728 , m_inheritGroup(true)
3729{
3730 QQmlDelegateModelPrivate *d = QQmlDelegateModelPrivate::get(m_model);
3731 if (d->m_cacheMetaType) {
3732 QQmlDelegateModelGroupPrivate::get(d->m_groups[1])->emitters.insert(this);
3733 m_compositorGroup = Compositor::Default;
3734 } else {
3735 d->m_pendingParts.insert(this);
3736 }
3737}
3738
3742
3744{
3745 if (m_inheritGroup)
3746 return m_model->filterGroup();
3747 return m_filterGroup;
3748}
3749
3750void QQmlPartsModel::setFilterGroup(const QString &group)
3751{
3752 if (QQmlDelegateModelPrivate::get(m_model)->m_transaction) {
3753 qmlWarning(this) << tr("The group of a DelegateModel cannot be changed within onChanged");
3754 return;
3755 }
3756
3757 if (m_filterGroup != group || m_inheritGroup) {
3758 m_filterGroup = group;
3759 m_inheritGroup = false;
3761
3762 emit filterGroupChanged();
3763 }
3764}
3765
3767{
3768 if (!m_inheritGroup) {
3769 m_inheritGroup = true;
3771 emit filterGroupChanged();
3772 }
3773}
3774
3776{
3777 QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model);
3778 if (!model->m_cacheMetaType)
3779 return;
3780
3781 if (m_inheritGroup) {
3782 if (m_filterGroup == model->m_filterGroup)
3783 return;
3784 m_filterGroup = model->m_filterGroup;
3785 }
3786
3787 QQmlListCompositor::Group previousGroup = m_compositorGroup;
3788 m_compositorGroup = Compositor::Default;
3789 QQmlDelegateModelGroupPrivate::get(model->m_groups[Compositor::Default])->emitters.insert(this);
3790 for (int i = 1; i < model->m_groupCount; ++i) {
3791 if (m_filterGroup == model->m_cacheMetaType->groupNames.at(i - 1)) {
3792 m_compositorGroup = Compositor::Group(i);
3793 break;
3794 }
3795 }
3796
3797 QQmlDelegateModelGroupPrivate::get(model->m_groups[m_compositorGroup])->emitters.insert(this);
3798 if (m_compositorGroup != previousGroup) {
3799 QVector<QQmlChangeSet::Change> removes;
3800 QVector<QQmlChangeSet::Change> inserts;
3801 model->m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts);
3802
3803 QQmlChangeSet changeSet;
3804 changeSet.move(removes, inserts);
3805 if (!changeSet.isEmpty())
3806 emit modelUpdated(changeSet, false);
3807
3808 if (changeSet.difference() != 0)
3809 emit countChanged();
3810 }
3811}
3812
3814 Compositor::Group group, const QQmlChangeSet &changeSet)
3815{
3816 if (!m_inheritGroup)
3817 return;
3818
3819 m_compositorGroup = group;
3820 QQmlDelegateModelGroupPrivate::get(QQmlDelegateModelPrivate::get(m_model)->m_groups[m_compositorGroup])->emitters.insert(this);
3821
3822 if (!changeSet.isEmpty())
3823 emit modelUpdated(changeSet, false);
3824
3825 if (changeSet.difference() != 0)
3826 emit countChanged();
3827
3828 emit filterGroupChanged();
3829}
3830
3832{
3833 QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model);
3834 return model->m_delegate
3835 ? model->m_compositor.count(m_compositorGroup)
3836 : 0;
3837}
3838
3840{
3841 return m_model->isValid();
3842}
3843
3844QObject *QQmlPartsModel::object(int index, QQmlIncubator::IncubationMode incubationMode)
3845{
3846 QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model);
3847
3848 if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup)) {
3849 qWarning() << "DelegateModel::item: index out range" << index << model->m_compositor.count(m_compositorGroup);
3850 return nullptr;
3851 }
3852
3853 QObject *object = model->object(m_compositorGroup, index, incubationMode);
3854
3855 if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object)) {
3856 QObject *part = package->part(m_part);
3857 if (!part)
3858 return nullptr;
3859 m_packaged.insert(part, package);
3860 return part;
3861 }
3862
3863 model->release(object);
3864 if (!model->m_delegateValidated) {
3865 if (object)
3866 qmlWarning(model->m_delegate) << tr("Delegate component must be Package type.");
3867 model->m_delegateValidated = true;
3868 }
3869
3870 return nullptr;
3871}
3872
3874{
3875 QQmlInstanceModel::ReleaseFlags flags;
3876
3877 auto it = m_packaged.find(item);
3878 if (it != m_packaged.end()) {
3879 QQuickPackage *package = *it;
3880 QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model);
3881 flags = model->release(package);
3882 m_packaged.erase(it);
3883 if (!m_packaged.contains(item))
3884 flags &= ~Referenced;
3885 if (flags & Destroyed)
3886 QQmlDelegateModelPrivate::get(m_model)->emitDestroyingPackage(package);
3887 }
3888 return flags;
3889}
3890
3891QVariant QQmlPartsModel::variantValue(int index, const QString &role)
3892{
3893 return QQmlDelegateModelPrivate::get(m_model)->variantValue(m_compositorGroup, index, role);
3894}
3895
3896void QQmlPartsModel::setWatchedRoles(const QList<QByteArray> &roles)
3897{
3898 QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model);
3899 model->m_adaptorModel.replaceWatchedRoles(m_watchedRoles, roles);
3900 m_watchedRoles = roles;
3901}
3902
3904{
3905 QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model);
3906 Compositor::iterator it = model->m_compositor.find(model->m_compositorGroup, index);
3907 if (!it->inCache())
3908 return QQmlIncubator::Null;
3909
3910 if (auto incubationTask = model->m_cache.at(it.cacheIndex())->incubationTask)
3911 return incubationTask->status();
3912
3913 return QQmlIncubator::Ready;
3914}
3915
3916int QQmlPartsModel::indexOf(QObject *item, QObject *) const
3917{
3918 auto it = m_packaged.find(item);
3919 if (it != m_packaged.end()) {
3920 if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(*it))
3921 return cacheItem->groupIndex(m_compositorGroup);
3922 }
3923 return -1;
3924}
3925
3926void QQmlPartsModel::createdPackage(int index, QQuickPackage *package)
3927{
3928 emit createdItem(index, package->part(m_part));
3929}
3930
3931void QQmlPartsModel::initPackage(int index, QQuickPackage *package)
3932{
3933 if (m_modelUpdatePending)
3934 m_pendingPackageInitializations << index;
3935 else
3936 emit initItem(index, package->part(m_part));
3937}
3938
3939void QQmlPartsModel::destroyingPackage(QQuickPackage *package)
3940{
3941 QObject *item = package->part(m_part);
3942 Q_ASSERT(!m_packaged.contains(item));
3943 emit destroyingItem(item);
3944}
3945
3946void QQmlPartsModel::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset)
3947{
3948 m_modelUpdatePending = false;
3949 emit modelUpdated(changeSet, reset);
3950 if (changeSet.difference() != 0)
3951 emit countChanged();
3952
3953 QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model);
3954 QVector<int> pendingPackageInitializations;
3955 qSwap(pendingPackageInitializations, m_pendingPackageInitializations);
3956 for (int index : pendingPackageInitializations) {
3957 if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup))
3958 continue;
3959 QObject *object = model->object(m_compositorGroup, index, QQmlIncubator::Asynchronous);
3960 if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object))
3961 emit initItem(index, package->part(m_part));
3962 model->release(object);
3963 }
3964}
3965
3967{
3968 // We can only hold INT_MAX items, due to the return type of size().
3969 if (Q_UNLIKELY(m_reusableItemsPool.size() == QQmlReusableDelegateModelItemsPool::MaxSize))
3970 return false;
3971
3972 Q_ASSERT(m_reusableItemsPool.size() < QQmlReusableDelegateModelItemsPool::MaxSize);
3973
3974 // Currently, the only way for a view to reuse items is to call release()
3975 // in the model class with the second argument explicitly set to
3976 // QQmlReuseableDelegateModelItemsPool::Reusable. If the released item is
3977 // no longer referenced, it will be added to the pool. Reusing of items can
3978 // be specified per item, in case certain items cannot be recycled. A
3979 // QQmlDelegateModelItem knows which delegate its object was created from.
3980 // So when we are about to create a new item, we first check if the pool
3981 // contains an item based on the same delegate from before. If so, we take
3982 // it out of the pool (instead of creating a new item), and update all its
3983 // context properties and attached properties.
3984
3985 // When a view is recycling items, it should call drain() regularly. As
3986 // there is currently no logic to 'hibernate' items in the pool, they are
3987 // only meant to rest there for a short while, ideally only from the time
3988 // e.g a row is unloaded on one side of the view, and until a new row is
3989 // loaded on the opposite side. Between these times, the application will
3990 // see the item as fully functional and 'alive' (just not visible on
3991 // screen). Since this time is supposed to be short, we don't take any
3992 // action to notify the application about it, since we don't want to
3993 // trigger any bindings that can disturb performance.
3994
3995 // A recommended time for calling drain() is each time a view has finished
3996 // loading e.g a new row or column. If there are more items in the pool
3997 // after that, it means that the view most likely doesn't need them anytime
3998 // soon. Those items should be destroyed to reduce resource consumption.
3999
4000 // Depending on if a view is a list or a table, it can sometimes be
4001 // performant to keep items in the pool for a bit longer than one "row
4002 // out/row in" cycle. E.g for a table, if the number of visible rows in a
4003 // view is much larger than the number of visible columns. In that case, if
4004 // you flick out a row, and then flick in a column, you would throw away a
4005 // lot of items in the pool if completely draining it. The reason is that
4006 // unloading a row places more items in the pool than what ends up being
4007 // recycled when loading a new column. And then, when you next flick in a
4008 // new row, you would need to load all those drained items again from
4009 // scratch. For that reason, you can specify a maxPoolTime to the
4010 // drainReusableItemsPool() that allows you to keep items in the pool for a
4011 // bit longer, effectively keeping more items in circulation. A recommended
4012 // maxPoolTime would be equal to the number of dimensions in the view,
4013 // which means 1 for a list view and 2 for a table view. If you specify 0,
4014 // all items will be drained.
4015
4016 Q_ASSERT(!modelItem->incubationTask);
4017 Q_ASSERT(!modelItem->isObjectReferenced());
4018 Q_ASSERT(modelItem->object);
4019 Q_ASSERT(modelItem->delegate);
4020
4021 m_reusableItemsPool.push_back({modelItem, 0});
4022
4023 qCDebug(lcItemViewDelegateRecycling)
4024 << "item:" << modelItem
4025 << "delegate:" << modelItem->delegate
4026 << "index:" << modelItem->modelIndex()
4027 << "row:" << modelItem->modelRow()
4028 << "column:" << modelItem->modelColumn()
4029 << "pool size:" << m_reusableItemsPool.size();
4030
4031 return true;
4032}
4033
4034QQmlDelegateModelItem *QQmlReusableDelegateModelItemsPool::takeItem(const QQmlComponent *delegate, int newIndexHint)
4035{
4036 // Find the oldest item in the pool that was made from the same delegate as
4037 // the given argument, remove it from the pool, and return it.
4038 for (auto it = m_reusableItemsPool.cbegin(); it != m_reusableItemsPool.cend(); ++it) {
4039 QQmlDelegateModelItem *modelItem = it->item;
4040 if (modelItem->delegate != delegate)
4041 continue;
4042 m_reusableItemsPool.erase(it);
4043
4044 qCDebug(lcItemViewDelegateRecycling)
4045 << "item:" << modelItem
4046 << "delegate:" << delegate
4047 << "old index:" << modelItem->modelIndex()
4048 << "old row:" << modelItem->modelRow()
4049 << "old column:" << modelItem->modelColumn()
4050 << "new index:" << newIndexHint
4051 << "pool size:" << m_reusableItemsPool.size();
4052
4053 return modelItem;
4054 }
4055
4056 qCDebug(lcItemViewDelegateRecycling)
4057 << "no available item for delegate:" << delegate
4058 << "new index:" << newIndexHint
4059 << "pool size:" << m_reusableItemsPool.size();
4060
4061 return nullptr;
4062}
4063
4064void QQmlReusableDelegateModelItemsPool::drain(int maxPoolTime, std::function<void(QQmlDelegateModelItem *cacheItem)> releaseItem)
4065{
4066 // Rather than releasing all pooled items upon a call to this function, each
4067 // item has a poolTime. The poolTime specifies for how many loading cycles an item
4068 // has been resting in the pool. And for each invocation of this function, poolTime
4069 // will increase. If poolTime is equal to, or exceeds, maxPoolTime, it will be removed
4070 // from the pool and released. This way, the view can tweak a bit for how long
4071 // items should stay in "circulation", even if they are not recycled right away.
4072 qCDebug(lcItemViewDelegateRecycling) << "pool size before drain:" << m_reusableItemsPool.size();
4073
4074 if (maxPoolTime == 0) {
4075 // Special-case 0 because it's so common and so easy to handle.
4076 const std::vector<PoolItem> removed = std::exchange(m_reusableItemsPool, {});
4077 for (const PoolItem &item : removed)
4078 releaseItem(item.item);
4079 qCDebug(lcItemViewDelegateRecycling) << "pool size after drain: 0";
4080 return;
4081 }
4082
4083 const auto begin = m_reusableItemsPool.begin();
4084 const auto oldEnd = m_reusableItemsPool.end();
4085 const auto newEnd = std::stable_partition(
4086 begin, oldEnd,
4087 [maxPoolTime](const PoolItem &item) { return item.poolTime < maxPoolTime; });
4088 std::for_each(begin, newEnd, [](PoolItem &item) { ++item.poolTime; });
4089
4090 QVarLengthArray<PoolItem> removed;
4091 removed.reserve(oldEnd - newEnd);
4092 std::move(newEnd, oldEnd, std::back_inserter(removed));
4093 m_reusableItemsPool.erase(newEnd, oldEnd);
4094
4095 for (const PoolItem &item : removed)
4096 releaseItem(item.item);
4097
4098 qCDebug(lcItemViewDelegateRecycling) << "pool size after drain:" << m_reusableItemsPool.size();
4099}
4100
4101//============================================================================
4102
4135
4137
4139{
4140 V4_OBJECT2(QQmlDelegateModelGroupChangeArray, QV4::Object)
4142public:
4147
4148 quint32 count() const { return d()->changes->size(); }
4149 const QQmlChangeSet::Change &at(int index) const { return d()->changes->at(index); }
4150
4152 {
4153 if (id.isArrayIndex()) {
4156 QV4::ExecutionEngine *v4 = static_cast<const QQmlDelegateModelGroupChangeArray *>(m)->engine();
4157 QV4::Scope scope(v4);
4159
4160 if (index >= array->count()) {
4161 if (hasProperty)
4162 *hasProperty = false;
4164 }
4165
4167
4171 object->d()->change = change;
4172
4173 if (hasProperty)
4174 *hasProperty = true;
4175 return object.asReturnedValue();
4176 }
4177
4180
4181 if (id == array->engine()->id_length()->propertyKey()) {
4182 if (hasProperty)
4183 *hasProperty = true;
4184 return QV4::Encode(array->count());
4185 }
4186
4188 }
4189};
4190
4191void QV4::Heap::QQmlDelegateModelGroupChangeArray::init(const QVector<QQmlChangeSet::Change> &changes)
4192{
4193 Object::init();
4194 this->changes = new QVector<QQmlChangeSet::Change>(changes);
4195 QV4::Scope scope(internalClass->engine);
4196 QV4::ScopedObject o(scope, this);
4197 o->setArrayType(QV4::Heap::ArrayData::Custom);
4198}
4199
4201
4203{
4204 QV4::Scope scope(v4);
4205
4206 QV4::ScopedObject proto(scope, v4->newObject());
4207 proto->defineAccessorProperty(QStringLiteral("index"), QQmlDelegateModelGroupChange::method_get_index, nullptr);
4208 proto->defineAccessorProperty(QStringLiteral("count"), QQmlDelegateModelGroupChange::method_get_count, nullptr);
4209 proto->defineAccessorProperty(QStringLiteral("moveId"), QQmlDelegateModelGroupChange::method_get_moveId, nullptr);
4210 changeProto.set(v4, proto);
4211}
4212
4216
4218 const QVector<QQmlChangeSet::Change> &changes)
4219{
4220 QV4::Scope scope(v4);
4221 QV4::ScopedObject o(scope, QQmlDelegateModelGroupChangeArray::create(v4, changes));
4222 return o.asReturnedValue();
4223}
4224
4225QT_END_NAMESPACE
4226
4227#include "moc_qqmldelegatemodel_p_p.cpp"
4228
4229#include "moc_qqmldelegatemodel_p.cpp"
void initializeRequiredProperties(QQmlDelegateModelItem *modelItemToIncubate, QObject *object, QQmlDelegateModel::DelegateModelAccess access)
void statusChanged(Status) override
Called when the status of the incubator changes.
void setInitialState(QObject *) override
Called after the object is first created, but before complex property bindings are evaluated and,...
QQmlDelegateModelItem * incubating
QQmlDelegateModelPrivate * vdm
QQmlDelegateModelAttachedMetaObject(QQmlDelegateModelItemMetaType *metaType, QMetaObject *metaObject)
int metaCall(QObject *, QMetaObject::Call, int _id, void **) override
QQmlDelegateModelAttached(QQmlDelegateModelItem *cacheItem, QObject *parent)
void setGroups(const QStringList &groups)
bool isUnresolved() const
\qmlattachedproperty bool QtQml.Models::DelegateModel::isUnresolved
void emitChanges()
\qmlattachedproperty bool QtQml.Models::DelegateModel::inItems
void setInPersistedItems(bool inPersisted)
QQmlDelegateModel * model() const
\qmlattachedproperty model QtQml.Models::DelegateModel::model
QStringList groups() const
\qmlattachedproperty stringlist QtQml.Models::DelegateModel::groups
QQmlDelegateModelItem * m_cacheItem
QV4::ReturnedValue array(QV4::ExecutionEngine *engine, const QVector< QQmlChangeSet::Change > &changes)
QQmlDelegateModelEngineData(QV4::ExecutionEngine *v4)
virtual void initPackage(int, QQuickPackage *)
virtual void destroyingPackage(QQuickPackage *)
virtual void createdPackage(int, QQuickPackage *)
bool parseIndex(const QV4::Value &value, int *index, Compositor::Group *group) const
void createdPackage(int index, QQuickPackage *package)
bool parseGroupArgs(QQmlV4FunctionPtr args, Compositor::Group *group, int *index, int *count, int *groups) const
void initPackage(int index, QQuickPackage *package)
QQmlDelegateModelGroupEmitterList emitters
void destroyingPackage(QQuickPackage *package)
void emitChanges(QV4::ExecutionEngine *engine)
QQDMIncubationTask * incubationTask
void childContextObjectDestroyed(QObject *childContextObject)
int groupIndex(Compositor::Group group)
virtual void setModelIndex(int idx, int newRow, int newColumn, bool alwaysEmit=false)
QQmlDelegateModelAttached * attached() const
void propertyCreated(int, QMetaPropertyBuilder &) override
void setInitialState(QQDMIncubationTask *incubationTask, QObject *o)
void removeCacheItem(QQmlDelegateModelItem *cacheItem)
QVariant variantValue(Compositor::Group group, int index, const QString &name)
void emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package)
QQmlDelegateModelParts * m_parts
QQmlAbstractDelegateComponent * m_delegateChooser
void itemsRemoved(const QVector< Compositor::Remove > &removes)
void addCacheItem(QQmlDelegateModelItem *item, Compositor::iterator it)
void reuseItem(QQmlDelegateModelItem *item, int newModelIndex, int newGroups)
void drainReusableItemsPool(int maxPoolTime)
QQmlDelegateModelGroupEmitterList m_pendingParts
void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) override
QObject * object(Compositor::Group group, int index, QQmlIncubator::IncubationMode incubationMode)
void destroyCacheItem(QQmlDelegateModelItem *cacheItem)
InsertionResult insert(Compositor::insert_iterator &before, const QV4::Value &object, int groups)
void itemsRemoved(const QVector< Compositor::Remove > &removes, QVarLengthArray< QVector< QQmlChangeSet::Change >, Compositor::MaximumGroupCount > *translatedRemoves, QHash< int, QList< QQmlDelegateModelItem * > > *movedItems=nullptr)
void setGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags)
void itemsInserted(const QVector< Compositor::Insert > &inserts, QVarLengthArray< QVector< QQmlChangeSet::Change >, Compositor::MaximumGroupCount > *translatedInserts, QHash< int, QList< QQmlDelegateModelItem * > > *movedItems=nullptr)
void delegateChanged(bool add=true, bool remove=true)
void releaseIncubator(QQDMIncubationTask *incubationTask)
void incubatorStatusChanged(QQDMIncubationTask *incubationTask, QQmlIncubator::Status status)
void emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package)
void addGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags)
void emitDestroyingPackage(QQuickPackage *package)
QQmlComponent * resolveDelegate(int index)
void itemsChanged(const QVector< Compositor::Change > &changes)
void removeGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags)
void itemsInserted(const QVector< Compositor::Insert > &inserts)
void itemsMoved(const QVector< Compositor::Remove > &removes, const QVector< Compositor::Insert > &inserts)
void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) override
QVariant variantValue(int index, const QString &role) override
int count() const override
QObject * object(int index, QQmlIncubator::IncubationMode incubationMode=QQmlIncubator::AsynchronousIfNested) override
void updateFilterGroup(Compositor::Group group, const QQmlChangeSet &changeSet)
void setFilterGroup(const QString &group)
QQmlIncubator::Status incubationStatus(int index) override
int indexOf(QObject *item, QObject *objectContext) const override
void destroyingPackage(QQuickPackage *package) override
void setWatchedRoles(const QList< QByteArray > &roles) override
ReleaseFlags release(QObject *item, ReusableFlag reusable=NotReusable) override
void createdPackage(int index, QQuickPackage *package) override
void initPackage(int index, QQuickPackage *package) override
QString filterGroup() const
bool isValid() const override
bool insertItem(QQmlDelegateModelItem *modelItem)
void drain(int maxPoolTime, std::function< void(QQmlDelegateModelItem *cacheItem)> releaseItem)
QQmlDelegateModelItem * takeItem(const QQmlComponent *delegate, int newIndexHint)
Combined button and popup list for selecting options.
Definition qjsvalue.h:23
static bool isDoneIncubating(QQmlIncubator::Status status)
DEFINE_OBJECT_VTABLE(QQmlDelegateModelGroupChange)
DEFINE_OBJECT_VTABLE(QV4::DelegateModelGroupFunction)
DEFINE_OBJECT_VTABLE(QQmlDelegateModelItemObject)
static void incrementIndexes(QQmlDelegateModelItem *cacheItem, int count, const int *deltas)
DEFINE_OBJECT_VTABLE(QQmlDelegateModelGroupChangeArray)
QQmlDelegateModelGroupEmitterList::iterator GroupEmitterListIt
void init(ExecutionEngine *engine, uint flag, QV4::ReturnedValue(*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg))
QV4::ReturnedValue(* code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg)
void init(const QVector< QQmlChangeSet::Change > &changes)
QVector< QQmlChangeSet::Change > * changes