1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** 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/qdeclarativeglobal_p.h>
47 #include <private/qdeclarativelistaccessor_p.h>
48 #include <private/qlistmodelinterface_p.h>
49 #include <private/qdeclarativechangeset_p.h>
53 QQuickRepeaterPrivate::QQuickRepeaterPrivate()
54 : model(0), ownModel(false), inRequest(false), itemCount(0), createFrom(-1)
58 QQuickRepeaterPrivate::~QQuickRepeaterPrivate()
65 \qmlclass Repeater QQuickRepeater
66 \inqmlmodule QtQuick 2
67 \ingroup qml-utility-elements
70 \brief The Repeater element allows you to repeat an Item-based component using a model.
72 The Repeater element is used to create a large number of
73 similar items. Like other view elements, 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 element 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 doc/src/snippets/declarative/repeaters/repeater.qml import
84 \snippet doc/src/snippets/declarative/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 doc/src/snippets/declarative/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 element 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 elements 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 \o A number that indicates the number of delegates to be created by the repeater
166 \o 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);
178 return d->dataSource;
181 void QQuickRepeater::setModel(const QVariant &model)
184 if (d->dataSource == model)
189 disconnect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)),
190 this, SLOT(modelUpdated(QDeclarativeChangeSet,bool)));
191 disconnect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*)));
192 disconnect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*)));
193 // disconnect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*)));
195 d->dataSource = model;
196 QObject *object = qvariant_cast<QObject*>(model);
197 QQuickVisualModel *vim = 0;
198 if (object && (vim = qobject_cast<QQuickVisualModel *>(object))) {
206 d->model = new QQuickVisualDataModel(qmlContext(this));
208 if (isComponentComplete())
209 static_cast<QQuickVisualDataModel *>(d->model)->componentComplete();
211 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
212 dataModel->setModel(model);
215 connect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)),
216 this, SLOT(modelUpdated(QDeclarativeChangeSet,bool)));
217 connect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*)));
218 connect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*)));
219 // connect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*)));
227 \qmlproperty Component QtQuick2::Repeater::delegate
230 The delegate provides a template defining each item instantiated by the repeater.
232 Delegates are exposed to a read-only \c index property that indicates the index
233 of the delegate within the repeater. For example, the following \l Text delegate
234 displays the index of each repeated item:
238 \o \snippet doc/src/snippets/declarative/repeaters/repeater.qml index
239 \o \image repeater-index.png
242 If the \l model is a \l{QStringList-based model}{string list} or
243 \l{QObjectList-based model}{object list}, the delegate is also exposed to
244 a read-only \c modelData property that holds the string or object data. For
249 \o \snippet doc/src/snippets/declarative/repeaters/repeater.qml modeldata
250 \o \image repeater-modeldata.png
253 If the \l model is a model object (such as a \l ListModel) the delegate
254 can access all model roles as named properties, in the same way that delegates
255 do for view classes like ListView.
257 \sa {QML Data Models}
259 QDeclarativeComponent *QQuickRepeater::delegate() const
261 Q_D(const QQuickRepeater);
263 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
264 return dataModel->delegate();
270 void QQuickRepeater::setDelegate(QDeclarativeComponent *delegate)
273 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
274 if (delegate == dataModel->delegate())
278 d->model = new QQuickVisualDataModel(qmlContext(this));
281 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model)) {
282 dataModel->setDelegate(delegate);
284 emit delegateChanged();
289 \qmlproperty int QtQuick2::Repeater::count
291 This property holds the number of items in the repeater.
293 int QQuickRepeater::count() const
295 Q_D(const QQuickRepeater);
297 return d->model->count();
302 \qmlmethod Item QtQuick2::Repeater::itemAt(index)
304 Returns the \l Item that has been created at the given \a index, or \c null
305 if no item exists at \a index.
307 QQuickItem *QQuickRepeater::itemAt(int index) const
309 Q_D(const QQuickRepeater);
310 if (index >= 0 && index < d->deletables.count())
311 return d->deletables[index];
315 void QQuickRepeater::componentComplete()
318 if (d->model && d->ownModel)
319 static_cast<QQuickVisualDataModel *>(d->model)->componentComplete();
320 QQuickItem::componentComplete();
322 if (d->model && d->model->count())
326 void QQuickRepeater::itemChange(ItemChange change, const ItemChangeData &value)
328 QQuickItem::itemChange(change, value);
329 if (change == ItemParentHasChanged) {
334 void QQuickRepeater::clear()
337 bool complete = isComponentComplete();
340 for (int i = 0; i < d->deletables.count(); ++i) {
341 QQuickItem *item = d->deletables.at(i);
343 emit itemRemoved(i, item);
344 d->model->release(item);
347 d->deletables.clear();
351 void QQuickRepeater::regenerate()
354 if (!isComponentComplete())
359 if (!d->model || !d->model->count() || !d->model->isValid() || !parentItem() || !isComponentComplete())
362 d->itemCount = count();
363 d->deletables.resize(d->itemCount);
368 void QQuickRepeaterPrivate::createItems()
371 if (createFrom == -1)
374 for (int ii = createFrom; ii < itemCount; ++ii) {
375 if (!deletables.at(ii)) {
376 QQuickItem *item = model->item(ii, false);
381 deletables[ii] = item;
382 QDeclarative_setParent_noEvent(item, q->parentItem());
383 item->setParentItem(q->parentItem());
384 if (ii > 0 && deletables.at(ii-1)) {
385 item->stackAfter(deletables.at(ii-1));
387 QQuickItem *after = q;
388 for (int si = ii+1; si < itemCount; ++si) {
389 if (deletables.at(si)) {
390 after = deletables.at(si);
394 item->stackBefore(after);
396 emit q->itemAdded(ii, item);
402 void QQuickRepeater::createdItem(int, QQuickItem *)
409 void QQuickRepeater::initItem(int, QQuickItem *item)
411 QDeclarative_setParent_noEvent(item, parentItem());
412 item->setParentItem(parentItem());
415 void QQuickRepeater::modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset)
419 if (!isComponentComplete())
428 QHash<int, QVector<QPointer<QQuickItem> > > moved;
429 foreach (const QDeclarativeChangeSet::Remove &remove, changeSet.removes()) {
430 int index = qMin(remove.index, d->deletables.count());
431 int count = qMin(remove.index + remove.count, d->deletables.count()) - index;
432 if (remove.isMove()) {
433 moved.insert(remove.moveId, d->deletables.mid(index, count));
435 d->deletables.begin() + index,
436 d->deletables.begin() + index + count);
437 } else while (count--) {
438 QQuickItem *item = d->deletables.at(index);
439 d->deletables.remove(index);
440 emit itemRemoved(index, item);
442 d->model->release(item);
446 difference -= remove.count;
450 foreach (const QDeclarativeChangeSet::Insert &insert, changeSet.inserts()) {
451 int index = qMin(insert.index, d->deletables.count());
452 if (insert.isMove()) {
453 QVector<QPointer<QQuickItem> > items = moved.value(insert.moveId);
454 d->deletables = d->deletables.mid(0, index) + items + d->deletables.mid(index);
455 QQuickItem *stackBefore = index + items.count() < d->deletables.count()
456 ? d->deletables.at(index + items.count())
458 for (int i = index; i < index + items.count(); ++i)
459 d->deletables.at(i)->stackBefore(stackBefore);
460 } else for (int i = 0; i < insert.count; ++i) {
461 int modelIndex = index + i;
463 d->deletables.insert(modelIndex, 0);
464 if (d->createFrom == -1)
465 d->createFrom = modelIndex;
467 difference += insert.count;