2015-12-10 05:06:13 +02:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
|
|
|
** Copyright (C) 2015 The Qt Company Ltd.
|
|
|
|
** Contact: http://www.qt.io/licensing/
|
|
|
|
**
|
|
|
|
** This file is part of the QtDeclarative module of the Qt Toolkit.
|
|
|
|
**
|
|
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
|
|
** Commercial License Usage
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
** and conditions see http://www.qt.io/terms-conditions. For further
|
|
|
|
** information use the contact form at http://www.qt.io/contact-us.
|
|
|
|
**
|
|
|
|
** GNU Lesser General Public License Usage
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
|
|
** General Public License version 2.1 or version 3 as published by the Free
|
|
|
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
|
|
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
|
|
|
** following information to ensure the GNU Lesser General Public License
|
|
|
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
|
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
|
|
**
|
|
|
|
** As a special exception, The Qt Company gives you certain additional
|
|
|
|
** rights. These rights are described in The Qt Company LGPL Exception
|
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
**
|
|
|
|
** GNU General Public License Usage
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
** General Public License version 3.0 as published by the Free Software
|
|
|
|
** Foundation and appearing in the file LICENSE.GPL included in the
|
|
|
|
** packaging of this file. Please review the following information to
|
|
|
|
** ensure the GNU General Public License version 3.0 requirements will be
|
|
|
|
** met: http://www.gnu.org/copyleft/gpl.html.
|
|
|
|
**
|
|
|
|
** $QT_END_LICENSE$
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include "qdeclarativerepeater_p.h"
|
|
|
|
#include "qdeclarativerepeater_p_p.h"
|
|
|
|
|
|
|
|
#include "qdeclarativevisualitemmodel_p.h"
|
|
|
|
#include <qdeclarativeglobal_p.h>
|
|
|
|
#include <qdeclarativelistaccessor_p.h>
|
|
|
|
|
|
|
|
#include <qlistmodelinterface_p.h>
|
|
|
|
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
QDeclarativeRepeaterPrivate::QDeclarativeRepeaterPrivate()
|
|
|
|
: model(0), ownModel(false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QDeclarativeRepeaterPrivate::~QDeclarativeRepeaterPrivate()
|
|
|
|
{
|
|
|
|
if (ownModel)
|
|
|
|
delete model;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\qmlclass Repeater QDeclarativeRepeater
|
|
|
|
\ingroup qml-utility-elements
|
|
|
|
\since 4.7
|
|
|
|
\inherits Item
|
|
|
|
|
|
|
|
\brief The Repeater element allows you to repeat an Item-based component using a model.
|
|
|
|
|
|
|
|
The Repeater element is used to create a large number of
|
|
|
|
similar items. Like other view elements, a Repeater has a \l model and a \l delegate:
|
|
|
|
for each entry in the model, the delegate is instantiated
|
|
|
|
in a context seeded with data from the model. A Repeater item is usually
|
|
|
|
enclosed in a positioner element such as \l Row or \l Column to visually
|
|
|
|
position the multiple delegate items created by the Repeater.
|
|
|
|
|
|
|
|
The following Repeater creates three instances of a \l Rectangle item within
|
|
|
|
a \l Row:
|
|
|
|
|
|
|
|
\snippet doc/src/snippets/declarative/repeaters/repeater.qml import
|
|
|
|
\codeline
|
|
|
|
\snippet doc/src/snippets/declarative/repeaters/repeater.qml simple
|
|
|
|
|
|
|
|
\image repeater-simple.png
|
|
|
|
|
|
|
|
A Repeater's \l model can be any of the supported \l {qmlmodels}{data models}.
|
|
|
|
Additionally, like delegates for other views, a Repeater delegate can access
|
|
|
|
its index within the repeater, as well as the model data relevant to the
|
|
|
|
delegate. See the \l delegate property documentation for details.
|
|
|
|
|
|
|
|
Items instantiated by the Repeater are inserted, in order, as
|
|
|
|
children of the Repeater's parent. The insertion starts immediately after
|
|
|
|
the repeater's position in its parent stacking list. This allows
|
|
|
|
a Repeater to be used inside a layout. For example, the following Repeater's
|
|
|
|
items are stacked between a red rectangle and a blue rectangle:
|
|
|
|
|
|
|
|
\snippet doc/src/snippets/declarative/repeaters/repeater.qml layout
|
|
|
|
|
|
|
|
\image repeater.png
|
|
|
|
|
|
|
|
|
|
|
|
\note A Repeater item owns all items it instantiates. Removing or dynamically destroying
|
|
|
|
an item created by a Repeater results in unpredictable behavior.
|
|
|
|
|
|
|
|
|
|
|
|
\section2 Considerations when using Repeater
|
|
|
|
|
|
|
|
The Repeater element creates all of its delegate items when the repeater is first
|
|
|
|
created. This can be inefficient if there are a large number of delegate items and
|
|
|
|
not all of the items are required to be visible at the same time. If this is the case,
|
|
|
|
consider using other view elements like ListView (which only creates delegate items
|
|
|
|
when they are scrolled into view) or use the \l {Dynamic Object Creation} methods to
|
|
|
|
create items as they are required.
|
|
|
|
|
|
|
|
Also, note that Repeater is \l {Item}-based, and can only repeat \l {Item}-derived objects.
|
|
|
|
For example, it cannot be used to repeat QtObjects:
|
|
|
|
\badcode
|
|
|
|
Item {
|
|
|
|
//XXX does not work! Can't repeat QtObject as it doesn't derive from Item.
|
|
|
|
Repeater {
|
|
|
|
model: 10
|
|
|
|
QtObject {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
\endcode
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\qmlsignal Repeater::onItemAdded(int index, Item item)
|
|
|
|
\since QtQuick 1.1
|
|
|
|
|
|
|
|
This handler is called when an item is added to the repeater. The \a index
|
|
|
|
parameter holds the index at which the item has been inserted within the
|
|
|
|
repeater, and the \a item parameter holds the \l Item that has been added.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\qmlsignal Repeater::onItemRemoved(int index, Item item)
|
|
|
|
\since QtQuick 1.1
|
|
|
|
|
|
|
|
This handler is called when an item is removed from the repeater. The \a index
|
|
|
|
parameter holds the index at which the item was removed from the repeater,
|
|
|
|
and the \a item parameter holds the \l Item that was removed.
|
|
|
|
|
|
|
|
Do not keep a reference to \a item if it was created by this repeater, as
|
|
|
|
in these cases it will be deleted shortly after the handler is called.
|
|
|
|
*/
|
|
|
|
|
|
|
|
QDeclarativeRepeater::QDeclarativeRepeater(QDeclarativeItem *parent)
|
|
|
|
: QDeclarativeItem(*(new QDeclarativeRepeaterPrivate), parent)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QDeclarativeRepeater::~QDeclarativeRepeater()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\qmlproperty any Repeater::model
|
|
|
|
|
|
|
|
The model providing data for the repeater.
|
|
|
|
|
|
|
|
This property can be set to any of the supported \l {qmlmodels}{data models}:
|
|
|
|
|
|
|
|
\list
|
|
|
|
\o A number that indicates the number of delegates to be created by the repeater
|
|
|
|
\o A model (e.g. a ListModel item, or a QAbstractItemModel subclass)
|
|
|
|
\o A string list
|
|
|
|
\o An object list
|
|
|
|
\endlist
|
|
|
|
|
|
|
|
The type of model affects the properties that are exposed to the \l delegate.
|
|
|
|
|
|
|
|
\sa {qmlmodels}{Data Models}
|
|
|
|
*/
|
|
|
|
QVariant QDeclarativeRepeater::model() const
|
|
|
|
{
|
|
|
|
Q_D(const QDeclarativeRepeater);
|
|
|
|
return d->dataSource;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QDeclarativeRepeater::setModel(const QVariant &model)
|
|
|
|
{
|
|
|
|
Q_D(QDeclarativeRepeater);
|
|
|
|
if (d->dataSource == model)
|
|
|
|
return;
|
|
|
|
|
|
|
|
clear();
|
|
|
|
if (d->model) {
|
|
|
|
disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
|
|
|
|
disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
|
|
|
|
disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
|
|
|
|
disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
|
|
|
|
/*
|
|
|
|
disconnect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*)));
|
|
|
|
disconnect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*)));
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
d->dataSource = model;
|
|
|
|
QObject *object = qvariant_cast<QObject*>(model);
|
|
|
|
QDeclarativeVisualModel *vim = 0;
|
|
|
|
if (object && (vim = qobject_cast<QDeclarativeVisualModel *>(object))) {
|
|
|
|
if (d->ownModel) {
|
|
|
|
delete d->model;
|
|
|
|
d->ownModel = false;
|
|
|
|
}
|
|
|
|
d->model = vim;
|
|
|
|
} else {
|
|
|
|
if (!d->ownModel) {
|
|
|
|
d->model = new QDeclarativeVisualDataModel(qmlContext(this), this);
|
|
|
|
d->ownModel = true;
|
|
|
|
}
|
|
|
|
if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model))
|
|
|
|
dataModel->setModel(model);
|
|
|
|
}
|
|
|
|
if (d->model) {
|
|
|
|
connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
|
|
|
|
connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
|
|
|
|
connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
|
|
|
|
connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
|
|
|
|
/*
|
|
|
|
connect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*)));
|
|
|
|
connect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*)));
|
|
|
|
*/
|
|
|
|
regenerate();
|
|
|
|
}
|
|
|
|
emit modelChanged();
|
|
|
|
emit countChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\qmlproperty Component Repeater::delegate
|
|
|
|
\default
|
|
|
|
|
|
|
|
The delegate provides a template defining each item instantiated by the repeater.
|
|
|
|
|
|
|
|
Delegates are exposed to a read-only \c index property that indicates the index
|
|
|
|
of the delegate within the repeater. For example, the following \l Text delegate
|
|
|
|
displays the index of each repeated item:
|
|
|
|
|
|
|
|
\table
|
|
|
|
\row
|
|
|
|
\o \snippet doc/src/snippets/declarative/repeaters/repeater.qml index
|
|
|
|
\o \image repeater-index.png
|
|
|
|
\endtable
|
|
|
|
|
|
|
|
If the \l model is a \l{QStringList-based model}{string list} or
|
|
|
|
\l{QObjectList-based model}{object list}, the delegate is also exposed to
|
|
|
|
a read-only \c modelData property that holds the string or object data. For
|
|
|
|
example:
|
|
|
|
|
|
|
|
\table
|
|
|
|
\row
|
|
|
|
\o \snippet doc/src/snippets/declarative/repeaters/repeater.qml modeldata
|
|
|
|
\o \image repeater-modeldata.png
|
|
|
|
\endtable
|
|
|
|
|
|
|
|
If the \l model is a model object (such as a \l ListModel) the delegate
|
|
|
|
can access all model roles as named properties, in the same way that delegates
|
|
|
|
do for view classes like ListView.
|
|
|
|
|
|
|
|
\sa {QML Data Models}
|
|
|
|
*/
|
|
|
|
QDeclarativeComponent *QDeclarativeRepeater::delegate() const
|
|
|
|
{
|
|
|
|
Q_D(const QDeclarativeRepeater);
|
|
|
|
if (d->model) {
|
|
|
|
if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model))
|
|
|
|
return dataModel->delegate();
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QDeclarativeRepeater::setDelegate(QDeclarativeComponent *delegate)
|
|
|
|
{
|
|
|
|
Q_D(QDeclarativeRepeater);
|
|
|
|
if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model))
|
|
|
|
if (delegate == dataModel->delegate())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!d->ownModel) {
|
|
|
|
d->model = new QDeclarativeVisualDataModel(qmlContext(this));
|
|
|
|
d->ownModel = true;
|
|
|
|
}
|
|
|
|
if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model)) {
|
|
|
|
dataModel->setDelegate(delegate);
|
|
|
|
regenerate();
|
|
|
|
emit delegateChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\qmlproperty int Repeater::count
|
|
|
|
|
|
|
|
This property holds the number of items in the repeater.
|
|
|
|
*/
|
|
|
|
int QDeclarativeRepeater::count() const
|
|
|
|
{
|
|
|
|
Q_D(const QDeclarativeRepeater);
|
|
|
|
if (d->model)
|
|
|
|
return d->model->count();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\qmlmethod Item Repeater::itemAt(index)
|
|
|
|
\since QtQuick 1.1
|
|
|
|
|
|
|
|
Returns the \l Item that has been created at the given \a index, or \c null
|
|
|
|
if no item exists at \a index.
|
|
|
|
*/
|
|
|
|
QDeclarativeItem *QDeclarativeRepeater::itemAt(int index) const
|
|
|
|
{
|
|
|
|
Q_D(const QDeclarativeRepeater);
|
|
|
|
if (index >= 0 && index < d->deletables.count())
|
|
|
|
return d->deletables[index];
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void QDeclarativeRepeater::componentComplete()
|
|
|
|
{
|
|
|
|
QDeclarativeItem::componentComplete();
|
|
|
|
regenerate();
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant QDeclarativeRepeater::itemChange(GraphicsItemChange change,
|
|
|
|
const QVariant &value)
|
|
|
|
{
|
|
|
|
QVariant rv = QDeclarativeItem::itemChange(change, value);
|
|
|
|
if (change == ItemParentHasChanged) {
|
|
|
|
regenerate();
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QDeclarativeRepeater::clear()
|
|
|
|
{
|
|
|
|
Q_D(QDeclarativeRepeater);
|
|
|
|
bool complete = isComponentComplete();
|
|
|
|
|
|
|
|
if (d->model) {
|
|
|
|
while (d->deletables.count() > 0) {
|
|
|
|
QDeclarativeItem *item = d->deletables.takeLast();
|
|
|
|
if (complete)
|
|
|
|
emit itemRemoved(d->deletables.count()-1, item);
|
|
|
|
d->model->release(item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
d->deletables.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QDeclarativeRepeater::regenerate()
|
|
|
|
{
|
|
|
|
Q_D(QDeclarativeRepeater);
|
|
|
|
if (!isComponentComplete())
|
|
|
|
return;
|
|
|
|
|
|
|
|
clear();
|
|
|
|
|
|
|
|
if (!d->model || !d->model->count() || !d->model->isValid() || !parentItem() || !isComponentComplete())
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (int ii = 0; ii < count(); ++ii) {
|
|
|
|
QDeclarativeItem *item = d->model->item(ii);
|
|
|
|
if (item) {
|
|
|
|
QDeclarative_setParent_noEvent(item, parentItem());
|
|
|
|
item->setParentItem(parentItem());
|
|
|
|
item->stackBefore(this);
|
|
|
|
d->deletables << item;
|
|
|
|
emit itemAdded(ii, item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QDeclarativeRepeater::itemsInserted(int index, int count)
|
|
|
|
{
|
|
|
|
Q_D(QDeclarativeRepeater);
|
2016-01-06 20:24:56 +02:00
|
|
|
if (!isComponentComplete()) {
|
|
|
|
emit countChanged();
|
2015-12-10 05:06:13 +02:00
|
|
|
return;
|
2016-01-06 20:24:56 +02:00
|
|
|
}
|
2015-12-10 05:06:13 +02:00
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
|
int modelIndex = index + i;
|
|
|
|
QDeclarativeItem *item = d->model->item(modelIndex);
|
|
|
|
if (item) {
|
|
|
|
QDeclarative_setParent_noEvent(item, parentItem());
|
|
|
|
item->setParentItem(parentItem());
|
|
|
|
if (modelIndex < d->deletables.count())
|
|
|
|
item->stackBefore(d->deletables.at(modelIndex));
|
|
|
|
else
|
|
|
|
item->stackBefore(this);
|
|
|
|
d->deletables.insert(modelIndex, item);
|
|
|
|
emit itemAdded(modelIndex, item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
emit countChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QDeclarativeRepeater::itemsRemoved(int index, int count)
|
|
|
|
{
|
|
|
|
Q_D(QDeclarativeRepeater);
|
2016-01-06 20:24:56 +02:00
|
|
|
if (count <= 0)
|
2015-12-10 05:06:13 +02:00
|
|
|
return;
|
2016-01-06 20:24:56 +02:00
|
|
|
|
|
|
|
if (!isComponentComplete()) {
|
|
|
|
emit countChanged();
|
|
|
|
return;
|
|
|
|
}
|
2015-12-10 05:06:13 +02:00
|
|
|
while (count--) {
|
|
|
|
QDeclarativeItem *item = d->deletables.takeAt(index);
|
|
|
|
emit itemRemoved(index, item);
|
|
|
|
if (item)
|
|
|
|
d->model->release(item);
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
emit countChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QDeclarativeRepeater::itemsMoved(int from, int to, int count)
|
|
|
|
{
|
|
|
|
Q_D(QDeclarativeRepeater);
|
|
|
|
if (!isComponentComplete() || count <= 0)
|
|
|
|
return;
|
|
|
|
if (from + count > d->deletables.count()) {
|
|
|
|
regenerate();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
QList<QDeclarativeItem*> removed;
|
|
|
|
int removedCount = count;
|
|
|
|
while (removedCount--)
|
|
|
|
removed << d->deletables.takeAt(from);
|
|
|
|
for (int i = 0; i < count; ++i)
|
|
|
|
d->deletables.insert(to + i, removed.at(i));
|
|
|
|
d->deletables.last()->stackBefore(this);
|
|
|
|
for (int i = d->model->count()-1; i > 0; --i) {
|
|
|
|
QDeclarativeItem *item = d->deletables.at(i-1);
|
|
|
|
item->stackBefore(d->deletables.at(i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QDeclarativeRepeater::modelReset()
|
|
|
|
{
|
|
|
|
if (!isComponentComplete())
|
|
|
|
return;
|
|
|
|
regenerate();
|
|
|
|
emit countChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
#include <moc_qdeclarativerepeater_p.h>
|