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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtdeclarative/src/qmlmodels/qqmldelegatemodel.cpp