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