1// Copyright (C) 2016 Research In Motion.
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 "qqmlinstantiator_p.h"
5#include "qqmlinstantiator_p_p.h"
6#include <QtQml/QQmlContext>
7#include <QtQml/QQmlComponent>
8#include <QtQml/QQmlInfo>
9#include <QtQml/QQmlError>
10#include <QtQmlModels/private/qqmlobjectmodel_p.h>
11#if QT_CONFIG(qml_delegate_model)
12#include <QtQmlModels/private/qqmldelegatemodel_p.h>
13#endif
14
15QT_BEGIN_NAMESPACE
16
17QQmlInstantiatorPrivate::QQmlInstantiatorPrivate()
18 : componentComplete(true)
19 , effectiveReset(false)
20 , active(true)
21 , async(false)
22#if QT_CONFIG(qml_delegate_model)
23 , ownModel(false)
24#endif
25 , model(QVariant(1))
26{
27}
28
29void QQmlInstantiatorPrivate::clear()
30{
31 Q_Q(QQmlInstantiator);
32 if (!instanceModel)
33 return;
34
35 if (objects.isEmpty())
36 return;
37
38 for (int i=0; i < objects.size(); i++) {
39 QObject *object = objects[i];
40 emit q->objectRemoved(index: i, object);
41 instanceModel->release(object);
42 if (object && object->parent() == q)
43 object->setParent(nullptr);
44 }
45
46 objects.clear();
47 emit q->objectChanged();
48}
49
50QObject *QQmlInstantiatorPrivate::modelObject(int index, bool async)
51{
52 requestedIndex = index;
53 QObject *o = instanceModel->object(index, incubationMode: async ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested);
54 requestedIndex = -1;
55 return o;
56}
57
58
59void QQmlInstantiatorPrivate::regenerate()
60{
61 Q_Q(QQmlInstantiator);
62 if (!componentComplete)
63 return;
64
65 int prevCount = q->count();
66
67 clear();
68
69 if (!active || !instanceModel || !instanceModel->count() || !instanceModel->isValid()) {
70 if (prevCount)
71 q->countChanged();
72 return;
73 }
74
75 for (int i = 0; i < instanceModel->count(); i++) {
76 QObject *object = modelObject(index: i, async);
77 // If the item was already created we won't get a createdItem
78 if (object)
79 _q_createdItem(i, object);
80 }
81 if (q->count() != prevCount)
82 q->countChanged();
83}
84
85void QQmlInstantiatorPrivate::_q_createdItem(int idx, QObject* item)
86{
87 Q_Q(QQmlInstantiator);
88 if (objects.contains(t: item)) //Case when it was created synchronously in regenerate
89 return;
90 if (requestedIndex != idx) // Asynchronous creation, reference the object
91 (void)instanceModel->object(index: idx);
92 if (!item->parent())
93 item->setParent(q);
94 if (objects.size() < idx + 1) {
95 int modelCount = instanceModel->count();
96 if (objects.capacity() < modelCount)
97 objects.reserve(size: modelCount);
98 objects.resize(size: idx + 1);
99 }
100 if (QObject *o = objects.at(i: idx))
101 instanceModel->release(object: o);
102 objects.replace(i: idx, t: item);
103 if (objects.size() == 1)
104 q->objectChanged();
105 q->objectAdded(index: idx, object: item);
106}
107
108void QQmlInstantiatorPrivate::_q_modelUpdated(const QQmlChangeSet &changeSet, bool reset)
109{
110 Q_Q(QQmlInstantiator);
111
112 if (!componentComplete || effectiveReset || !active)
113 return;
114
115 if (reset) {
116 regenerate();
117 if (changeSet.difference() != 0)
118 q->countChanged();
119 return;
120 }
121
122 int difference = 0;
123 QHash<int, QVector<QPointer<QObject> > > moved;
124 const QVector<QQmlChangeSet::Change> &removes = changeSet.removes();
125 for (const QQmlChangeSet::Change &remove : removes) {
126 int index = qMin(a: remove.index, b: objects.size());
127 int count = qMin(a: remove.index + remove.count, b: objects.size()) - index;
128 if (remove.isMove()) {
129 moved.insert(key: remove.moveId, value: objects.mid(pos: index, len: count));
130 objects.erase(
131 begin: objects.begin() + index,
132 end: objects.begin() + index + count);
133 } else while (count--) {
134 QObject *obj = objects.at(i: index);
135 objects.remove(i: index);
136 q->objectRemoved(index, object: obj);
137 if (obj)
138 instanceModel->release(object: obj);
139 }
140
141 difference -= remove.count;
142 }
143
144 const QVector<QQmlChangeSet::Change> &inserts = changeSet.inserts();
145 for (const QQmlChangeSet::Change &insert : inserts) {
146 int index = qMin(a: insert.index, b: objects.size());
147 if (insert.isMove()) {
148 QVector<QPointer<QObject> > movedObjects = moved.value(key: insert.moveId);
149 objects = objects.mid(pos: 0, len: index) + movedObjects + objects.mid(pos: index);
150 } else {
151 if (insert.index <= objects.size())
152 objects.insert(i: insert.index, n: insert.count, t: nullptr);
153 for (int i = 0; i < insert.count; ++i) {
154 int modelIndex = index + i;
155 QObject* obj = modelObject(index: modelIndex, async);
156 if (obj)
157 _q_createdItem(idx: modelIndex, item: obj);
158 }
159 }
160 difference += insert.count;
161 }
162
163 if (difference != 0)
164 q->countChanged();
165}
166
167#if QT_CONFIG(qml_delegate_model)
168void QQmlInstantiatorPrivate::makeModel()
169{
170 Q_Q(QQmlInstantiator);
171 QQmlDelegateModel* delegateModel = new QQmlDelegateModel(qmlContext(q), q);
172 instanceModel = delegateModel;
173 ownModel = true;
174 delegateModel->setDelegate(delegate);
175 delegateModel->setDelegateModelAccess(delegateModelAccess);
176 delegateModel->classBegin(); //Pretend it was made in QML
177 if (componentComplete)
178 delegateModel->componentComplete();
179}
180#endif
181
182
183/*!
184 \qmltype Instantiator
185 \nativetype QQmlInstantiator
186 \inqmlmodule QtQml.Models
187 \ingroup qtquick-models
188 \brief Dynamically creates objects.
189
190 A Instantiator can be used to control the dynamic creation of objects, or to dynamically
191 create multiple objects from a template.
192
193 The Instantiator element will manage the objects it creates. Those objects are parented to the
194 Instantiator and can also be deleted by the Instantiator if the Instantiator's properties change. Objects
195 can also be destroyed dynamically through other means, and the Instantiator will not recreate
196 them unless the properties of the Instantiator change.
197
198 \note Instantiator is part of QtQml.Models since version 2.14 and part of QtQml since
199 version 2.1. Importing Instantiator via QtQml is deprecated since Qt 5.14.
200*/
201QQmlInstantiator::QQmlInstantiator(QObject *parent)
202 : QObject(*(new QQmlInstantiatorPrivate), parent)
203{
204}
205
206QQmlInstantiator::~QQmlInstantiator()
207{
208 Q_D(QQmlInstantiator);
209 d->clear();
210}
211
212/*!
213 \qmlsignal QtQml.Models::Instantiator::objectAdded(int index, QtObject object)
214
215 This signal is emitted when an object is added to the Instantiator. The \a index
216 parameter holds the index which the object has been given, and the \a object
217 parameter holds the \l QtObject that has been added.
218*/
219
220/*!
221 \qmlsignal QtQml.Models::Instantiator::objectRemoved(int index, QtObject object)
222
223 This signal is emitted when an object is removed from the Instantiator. The \a index
224 parameter holds the index which the object had been given, and the \a object
225 parameter holds the \l QtObject that has been removed.
226
227 Do not keep a reference to \a object if it was created by this Instantiator, as
228 in these cases it will be deleted shortly after the signal is handled.
229*/
230/*!
231 \qmlproperty bool QtQml.Models::Instantiator::active
232
233 When active is true, and the delegate component is ready, the Instantiator will
234 create objects according to the model. When active is false, no objects
235 will be created and any previously created objects will be destroyed.
236
237 Default is true.
238*/
239bool QQmlInstantiator::isActive() const
240{
241 Q_D(const QQmlInstantiator);
242 return d->active;
243}
244
245void QQmlInstantiator::setActive(bool newVal)
246{
247 Q_D(QQmlInstantiator);
248 if (newVal == d->active)
249 return;
250 d->active = newVal;
251 emit activeChanged();
252 d->regenerate();
253}
254
255/*!
256 \qmlproperty bool QtQml.Models::Instantiator::asynchronous
257
258 When asynchronous is true the Instantiator will attempt to create objects
259 asynchronously. This means that objects may not be available immediately,
260 even if active is set to true.
261
262 You can use the objectAdded signal to respond to items being created.
263
264 Default is false.
265*/
266bool QQmlInstantiator::isAsync() const
267{
268 Q_D(const QQmlInstantiator);
269 return d->async;
270}
271
272void QQmlInstantiator::setAsync(bool newVal)
273{
274 Q_D(QQmlInstantiator);
275 if (newVal == d->async)
276 return;
277 d->async = newVal;
278 emit asynchronousChanged();
279}
280
281
282/*!
283 \qmlproperty int QtQml.Models::Instantiator::count
284
285 The number of objects the Instantiator is currently managing.
286*/
287
288int QQmlInstantiator::count() const
289{
290 Q_D(const QQmlInstantiator);
291 return d->objects.size();
292}
293
294/*!
295 \qmlproperty QtQml::Component QtQml.Models::Instantiator::delegate
296 \qmldefault
297
298 The component used to create all objects.
299
300 Note that an extra variable, index, will be available inside instances of the
301 delegate. This variable refers to the index of the instance inside the Instantiator,
302 and can be used to obtain the object through the objectAt method of the Instantiator.
303
304 If this property is changed, all instances using the old delegate will be destroyed
305 and new instances will be created using the new delegate.
306*/
307QQmlComponent* QQmlInstantiator::delegate()
308{
309 Q_D(QQmlInstantiator);
310 return d->delegate;
311}
312
313void QQmlInstantiator::setDelegate(QQmlComponent* c)
314{
315 Q_D(QQmlInstantiator);
316 if (c == d->delegate)
317 return;
318
319 d->delegate = c;
320 emit delegateChanged();
321
322#if QT_CONFIG(qml_delegate_model)
323 if (!d->ownModel)
324 return;
325
326 if (QQmlDelegateModel *dModel = qobject_cast<QQmlDelegateModel*>(object: d->instanceModel))
327 dModel->setDelegate(c);
328 if (d->componentComplete)
329 d->regenerate();
330#endif
331}
332
333/*!
334 \qmlproperty variant QtQml.Models::Instantiator::model
335
336 This property can be set to any of the supported \l {qml-data-models}{data models}:
337
338 \list
339 \li A number that indicates the number of delegates to be created by the repeater
340 \li A model (e.g. a ListModel item, or a QAbstractItemModel subclass)
341 \li A string list
342 \li An object list
343 \endlist
344
345 The type of model affects the properties that are exposed to the \l delegate.
346
347 Default value is 1, which creates a single delegate instance.
348
349 \sa {qml-data-models}{Data Models}
350*/
351
352QVariant QQmlInstantiator::model() const
353{
354 Q_D(const QQmlInstantiator);
355 return d->model;
356}
357
358void QQmlInstantiator::setModel(const QVariant &v)
359{
360 Q_D(QQmlInstantiator);
361 if (d->model == v)
362 return;
363
364 d->model = v;
365 //Don't actually set model until componentComplete in case it wants to create its delegates immediately
366 if (!d->componentComplete)
367 return;
368
369 QQmlInstanceModel *prevModel = d->instanceModel;
370 QObject *object = qvariant_cast<QObject*>(v);
371 QQmlInstanceModel *vim = nullptr;
372 if (object && (vim = qobject_cast<QQmlInstanceModel *>(object))) {
373#if QT_CONFIG(qml_delegate_model)
374 if (d->ownModel) {
375 delete d->instanceModel;
376 prevModel = nullptr;
377 d->ownModel = false;
378 }
379#endif
380 d->instanceModel = vim;
381#if QT_CONFIG(qml_delegate_model)
382 } else if (v != QVariant(0)){
383 if (!d->ownModel)
384 d->makeModel();
385
386 if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel *>(object: d->instanceModel)) {
387 d->effectiveReset = true;
388 dataModel->setModel(v);
389 d->effectiveReset = false;
390 }
391#endif
392 }
393
394 if (d->instanceModel != prevModel) {
395 if (prevModel) {
396 disconnect(sender: prevModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)),
397 receiver: this, SLOT(_q_modelUpdated(QQmlChangeSet,bool)));
398 disconnect(sender: prevModel, SIGNAL(createdItem(int,QObject*)), receiver: this, SLOT(_q_createdItem(int,QObject*)));
399 //disconnect(prevModel, SIGNAL(initItem(int,QObject*)), this, SLOT(initItem(int,QObject*)));
400 }
401
402 if (d->instanceModel) {
403 connect(sender: d->instanceModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)),
404 receiver: this, SLOT(_q_modelUpdated(QQmlChangeSet,bool)));
405 connect(sender: d->instanceModel, SIGNAL(createdItem(int,QObject*)), receiver: this, SLOT(_q_createdItem(int,QObject*)));
406 //connect(d->instanceModel, SIGNAL(initItem(int,QObject*)), this, SLOT(initItem(int,QObject*)));
407 }
408 }
409
410 d->regenerate();
411 emit modelChanged();
412}
413
414#if QT_CONFIG(qml_delegate_model)
415/*!
416 \qmlproperty enumeration QtQml.Models::Instantiator::delegateModelAccess
417
418 \include delegatemodelaccess.qdocinc
419*/
420
421QQmlDelegateModel::DelegateModelAccess QQmlInstantiator::delegateModelAccess() const
422{
423 Q_D(const QQmlInstantiator);
424 return d->delegateModelAccess;
425}
426
427void QQmlInstantiator::setDelegateModelAccess(
428 QQmlDelegateModel::DelegateModelAccess delegateModelAccess)
429{
430 Q_D(QQmlInstantiator);
431 if (delegateModelAccess == d->delegateModelAccess)
432 return;
433
434 d->delegateModelAccess = delegateModelAccess;
435 emit delegateModelAccessChanged();
436
437 if (!d->ownModel)
438 return;
439
440 if (QQmlDelegateModel *dModel = qobject_cast<QQmlDelegateModel*>(object: d->instanceModel))
441 dModel->setDelegateModelAccess(delegateModelAccess);
442 if (d->componentComplete)
443 d->regenerate();
444}
445#endif
446
447/*!
448 \qmlproperty QtObject QtQml.Models::Instantiator::object
449
450 This is a reference to the first created object, intended as a convenience
451 for the case where only one object has been created.
452*/
453QObject *QQmlInstantiator::object() const
454{
455 Q_D(const QQmlInstantiator);
456 if (d->objects.size())
457 return d->objects[0];
458 return nullptr;
459}
460
461/*!
462 \qmlmethod QtObject QtQml.Models::Instantiator::objectAt(int index)
463
464 Returns a reference to the object with the given \a index.
465*/
466QObject *QQmlInstantiator::objectAt(int index) const
467{
468 Q_D(const QQmlInstantiator);
469 if (index >= 0 && index < d->objects.size())
470 return d->objects[index];
471 return nullptr;
472}
473
474/*!
475 \internal
476*/
477void QQmlInstantiator::classBegin()
478{
479 Q_D(QQmlInstantiator);
480 d->componentComplete = false;
481}
482
483/*!
484 \internal
485*/
486void QQmlInstantiator::componentComplete()
487{
488 Q_D(QQmlInstantiator);
489 d->componentComplete = true;
490#if QT_CONFIG(qml_delegate_model)
491 if (d->ownModel) {
492 static_cast<QQmlDelegateModel*>(d->instanceModel)->componentComplete();
493 d->regenerate();
494 } else
495#endif
496 {
497 QVariant realModel = d->model;
498 d->model = QVariant(0);
499 setModel(realModel); //If realModel == d->model this won't do anything, but that's fine since the model's 0
500 //setModel calls regenerate
501 }
502}
503
504QT_END_NAMESPACE
505
506#include "moc_qqmlinstantiator_p.cpp"
507

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