1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickrepeater_p.h"
43 #include "qquickrepeater_p_p.h"
44 #include "qquickvisualdatamodel_p.h"
46 #include <private/qqmlglobal_p.h>
47 #include <private/qquicklistaccessor_p.h>
48 #include <private/qquickchangeset_p.h>
52 QQuickRepeaterPrivate::QQuickRepeaterPrivate()
53 : model(0), ownModel(false), inRequest(false), dataSourceIsObject(false), itemCount(0), createFrom(-1)
57 QQuickRepeaterPrivate::~QQuickRepeaterPrivate()
65 \instantiates QQuickRepeater
66 \inqmlmodule QtQuick 2
67 \ingroup qtquick-models
68 \ingroup qtquick-positioning
70 \brief Instantiates a number of Item-based components using a provided model
72 The Repeater type is used to create a large number of
73 similar items. Like other view types, a Repeater has a \l model and a \l delegate:
74 for each entry in the model, the delegate is instantiated
75 in a context seeded with data from the model. A Repeater item is usually
76 enclosed in a positioner type such as \l Row or \l Column to visually
77 position the multiple delegate items created by the Repeater.
79 The following Repeater creates three instances of a \l Rectangle item within
82 \snippet qml/repeaters/repeater.qml import
84 \snippet qml/repeaters/repeater.qml simple
86 \image repeater-simple.png
88 A Repeater's \l model can be any of the supported \l {qmlmodels}{data models}.
89 Additionally, like delegates for other views, a Repeater delegate can access
90 its index within the repeater, as well as the model data relevant to the
91 delegate. See the \l delegate property documentation for details.
93 Items instantiated by the Repeater are inserted, in order, as
94 children of the Repeater's parent. The insertion starts immediately after
95 the repeater's position in its parent stacking list. This allows
96 a Repeater to be used inside a layout. For example, the following Repeater's
97 items are stacked between a red rectangle and a blue rectangle:
99 \snippet qml/repeaters/repeater.qml layout
104 \note A Repeater item owns all items it instantiates. Removing or dynamically destroying
105 an item created by a Repeater results in unpredictable behavior.
108 \section2 Considerations when using Repeater
110 The Repeater type creates all of its delegate items when the repeater is first
111 created. This can be inefficient if there are a large number of delegate items and
112 not all of the items are required to be visible at the same time. If this is the case,
113 consider using other view types like ListView (which only creates delegate items
114 when they are scrolled into view) or use the \l {Dynamic Object Creation} methods to
115 create items as they are required.
117 Also, note that Repeater is \l {Item}-based, and can only repeat \l {Item}-derived objects.
118 For example, it cannot be used to repeat QtObjects:
121 //XXX does not work! Can't repeat QtObject as it doesn't derive from Item.
131 \qmlsignal QtQuick2::Repeater::onItemAdded(int index, Item item)
133 This handler is called when an item is added to the repeater. The \a index
134 parameter holds the index at which the item has been inserted within the
135 repeater, and the \a item parameter holds the \l Item that has been added.
139 \qmlsignal QtQuick2::Repeater::onItemRemoved(int index, Item item)
141 This handler is called when an item is removed from the repeater. The \a index
142 parameter holds the index at which the item was removed from the repeater,
143 and the \a item parameter holds the \l Item that was removed.
145 Do not keep a reference to \a item if it was created by this repeater, as
146 in these cases it will be deleted shortly after the handler is called.
148 QQuickRepeater::QQuickRepeater(QQuickItem *parent)
149 : QQuickItem(*(new QQuickRepeaterPrivate), parent)
153 QQuickRepeater::~QQuickRepeater()
158 \qmlproperty any QtQuick2::Repeater::model
160 The model providing data for the repeater.
162 This property can be set to any of the supported \l {qmlmodels}{data models}:
165 \li A number that indicates the number of delegates to be created by the repeater
166 \li A model (e.g. a ListModel item, or a QAbstractItemModel subclass)
171 The type of model affects the properties that are exposed to the \l delegate.
173 \sa {qmlmodels}{Data Models}
175 QVariant QQuickRepeater::model() const
177 Q_D(const QQuickRepeater);
179 if (d->dataSourceIsObject) {
180 QObject *o = d->dataSourceAsObject;
181 return QVariant::fromValue(o);
184 return d->dataSource;
187 void QQuickRepeater::setModel(const QVariant &model)
190 if (d->dataSource == model)
195 disconnect(d->model, SIGNAL(modelUpdated(QQuickChangeSet,bool)),
196 this, SLOT(modelUpdated(QQuickChangeSet,bool)));
197 disconnect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*)));
198 disconnect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*)));
199 // disconnect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*)));
201 d->dataSource = model;
202 QObject *object = qvariant_cast<QObject*>(model);
203 d->dataSourceAsObject = object;
204 d->dataSourceIsObject = object != 0;
205 QQuickVisualModel *vim = 0;
206 if (object && (vim = qobject_cast<QQuickVisualModel *>(object))) {
214 d->model = new QQuickVisualDataModel(qmlContext(this));
216 if (isComponentComplete())
217 static_cast<QQuickVisualDataModel *>(d->model)->componentComplete();
219 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
220 dataModel->setModel(model);
223 connect(d->model, SIGNAL(modelUpdated(QQuickChangeSet,bool)),
224 this, SLOT(modelUpdated(QQuickChangeSet,bool)));
225 connect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*)));
226 connect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*)));
227 // connect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*)));
235 \qmlproperty Component QtQuick2::Repeater::delegate
238 The delegate provides a template defining each item instantiated by the repeater.
240 Delegates are exposed to a read-only \c index property that indicates the index
241 of the delegate within the repeater. For example, the following \l Text delegate
242 displays the index of each repeated item:
246 \li \snippet qml/repeaters/repeater.qml index
247 \li \image repeater-index.png
250 If the \l model is a \l{QStringList-based model}{string list} or
251 \l{QObjectList-based model}{object list}, the delegate is also exposed to
252 a read-only \c modelData property that holds the string or object data. For
257 \li \snippet qml/repeaters/repeater.qml modeldata
258 \li \image repeater-modeldata.png
261 If the \l model is a model object (such as a \l ListModel) the delegate
262 can access all model roles as named properties, in the same way that delegates
263 do for view classes like ListView.
265 \sa {QML Data Models}
267 QQmlComponent *QQuickRepeater::delegate() const
269 Q_D(const QQuickRepeater);
271 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
272 return dataModel->delegate();
278 void QQuickRepeater::setDelegate(QQmlComponent *delegate)
281 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
282 if (delegate == dataModel->delegate())
286 d->model = new QQuickVisualDataModel(qmlContext(this));
289 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model)) {
290 dataModel->setDelegate(delegate);
292 emit delegateChanged();
297 \qmlproperty int QtQuick2::Repeater::count
299 This property holds the number of items in the repeater.
301 int QQuickRepeater::count() const
303 Q_D(const QQuickRepeater);
305 return d->model->count();
310 \qmlmethod Item QtQuick2::Repeater::itemAt(index)
312 Returns the \l Item that has been created at the given \a index, or \c null
313 if no item exists at \a index.
315 QQuickItem *QQuickRepeater::itemAt(int index) const
317 Q_D(const QQuickRepeater);
318 if (index >= 0 && index < d->deletables.count())
319 return d->deletables[index];
323 void QQuickRepeater::componentComplete()
326 if (d->model && d->ownModel)
327 static_cast<QQuickVisualDataModel *>(d->model)->componentComplete();
328 QQuickItem::componentComplete();
330 if (d->model && d->model->count())
334 void QQuickRepeater::itemChange(ItemChange change, const ItemChangeData &value)
336 QQuickItem::itemChange(change, value);
337 if (change == ItemParentHasChanged) {
342 void QQuickRepeater::clear()
345 bool complete = isComponentComplete();
348 for (int i = 0; i < d->deletables.count(); ++i) {
349 QQuickItem *item = d->deletables.at(i);
351 emit itemRemoved(i, item);
352 d->model->release(item);
355 d->deletables.clear();
359 void QQuickRepeater::regenerate()
362 if (!isComponentComplete())
367 if (!d->model || !d->model->count() || !d->model->isValid() || !parentItem() || !isComponentComplete())
370 d->itemCount = count();
371 d->deletables.resize(d->itemCount);
376 void QQuickRepeaterPrivate::createItems()
379 if (createFrom == -1)
382 for (int ii = createFrom; ii < itemCount; ++ii) {
383 if (!deletables.at(ii)) {
384 QQuickItem *item = model->item(ii, false);
389 deletables[ii] = item;
390 item->setParentItem(q->parentItem());
391 if (ii > 0 && deletables.at(ii-1)) {
392 item->stackAfter(deletables.at(ii-1));
394 QQuickItem *after = q;
395 for (int si = ii+1; si < itemCount; ++si) {
396 if (deletables.at(si)) {
397 after = deletables.at(si);
401 item->stackBefore(after);
403 emit q->itemAdded(ii, item);
409 void QQuickRepeater::createdItem(int, QQuickItem *)
416 void QQuickRepeater::initItem(int, QQuickItem *item)
418 item->setParentItem(parentItem());
421 void QQuickRepeater::modelUpdated(const QQuickChangeSet &changeSet, bool reset)
425 if (!isComponentComplete())
430 if (changeSet.difference() != 0)
436 QHash<int, QVector<QPointer<QQuickItem> > > moved;
437 foreach (const QQuickChangeSet::Remove &remove, changeSet.removes()) {
438 int index = qMin(remove.index, d->deletables.count());
439 int count = qMin(remove.index + remove.count, d->deletables.count()) - index;
440 if (remove.isMove()) {
441 moved.insert(remove.moveId, d->deletables.mid(index, count));
443 d->deletables.begin() + index,
444 d->deletables.begin() + index + count);
445 } else while (count--) {
446 QQuickItem *item = d->deletables.at(index);
447 d->deletables.remove(index);
448 emit itemRemoved(index, item);
450 d->model->release(item);
454 difference -= remove.count;
458 foreach (const QQuickChangeSet::Insert &insert, changeSet.inserts()) {
459 int index = qMin(insert.index, d->deletables.count());
460 if (insert.isMove()) {
461 QVector<QPointer<QQuickItem> > items = moved.value(insert.moveId);
462 d->deletables = d->deletables.mid(0, index) + items + d->deletables.mid(index);
463 QQuickItem *stackBefore = index + items.count() < d->deletables.count()
464 ? d->deletables.at(index + items.count())
466 for (int i = index; i < index + items.count(); ++i)
467 d->deletables.at(i)->stackBefore(stackBefore);
468 } else for (int i = 0; i < insert.count; ++i) {
469 int modelIndex = index + i;
471 d->deletables.insert(modelIndex, 0);
472 if (d->createFrom == -1)
473 d->createFrom = modelIndex;
475 difference += insert.count;