From eb7db5934b453eea2946ed7ae9a188c44467cf23 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Mon, 18 May 2015 17:37:19 +0200 Subject: ObjectModel: add API for dynamic changes Following the ListModel API: - object get(index) - append(object) - insert(int index, object) - move(int from, int to, int n) - remove(int index, int n) [ChangeLog][QtQml][ObjectModel] Added get(), append(), insert(), move() and remove() methods. Change-Id: I592e55b7c4c933a1100191bf5a9405944b347172 Reviewed-by: Alan Alpert Reviewed-by: Gabriel de Dietrich --- src/qml/types/qqmlmodelsmodule.cpp | 1 + src/qml/types/qqmlobjectmodel.cpp | 204 +++++++++++++++++++-- src/qml/types/qqmlobjectmodel_p.h | 11 +- tests/auto/qml/qml.pro | 3 +- tests/auto/qml/qqmlobjectmodel/qqmlobjectmodel.pro | 10 + .../qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp | 134 ++++++++++++++ tests/auto/qmltest/objectmodel/tst_objectmodel.qml | 100 ++++++++++ .../auto/quick/qquicklistview/data/objectmodel.qml | 24 +++ .../quick/qquicklistview/tst_qquicklistview.cpp | 62 +++++++ .../auto/quick/qquickrepeater/data/objectmodel.qml | 28 +++ .../quick/qquickrepeater/tst_qquickrepeater.cpp | 60 ++++++ 11 files changed, 620 insertions(+), 17 deletions(-) create mode 100644 tests/auto/qml/qqmlobjectmodel/qqmlobjectmodel.pro create mode 100644 tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp create mode 100644 tests/auto/qmltest/objectmodel/tst_objectmodel.qml create mode 100644 tests/auto/quick/qquicklistview/data/objectmodel.qml create mode 100644 tests/auto/quick/qquickrepeater/data/objectmodel.qml diff --git a/src/qml/types/qqmlmodelsmodule.cpp b/src/qml/types/qqmlmodelsmodule.cpp index 3e53efd8b9..062d30c252 100644 --- a/src/qml/types/qqmlmodelsmodule.cpp +++ b/src/qml/types/qqmlmodelsmodule.cpp @@ -48,6 +48,7 @@ void QQmlModelsModule::defineModule() qmlRegisterType(uri, 2, 1, "DelegateModel"); qmlRegisterType(uri, 2, 1, "DelegateModelGroup"); qmlRegisterType(uri, 2, 1, "ObjectModel"); + qmlRegisterType(uri, 2, 3, "ObjectModel"); qmlRegisterType(uri, 2, 2, "ItemSelectionModel"); } diff --git a/src/qml/types/qqmlobjectmodel.cpp b/src/qml/types/qqmlobjectmodel.cpp index 1d892beabf..0076c26950 100644 --- a/src/qml/types/qqmlobjectmodel.cpp +++ b/src/qml/types/qqmlobjectmodel.cpp @@ -36,10 +36,12 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -67,9 +69,8 @@ public: QQmlObjectModelPrivate() : QObjectPrivate() {} static void children_append(QQmlListProperty *prop, QObject *item) { - static_cast(prop->data)->children.append(Item(item)); - static_cast(prop->data)->itemAppended(); - static_cast(prop->data)->emitChildrenChanged(); + int index = static_cast(prop->data)->children.count(); + static_cast(prop->data)->insert(index, item); } static int children_count(QQmlListProperty *prop) { @@ -81,33 +82,77 @@ public: } static void children_clear(QQmlListProperty *prop) { - static_cast(prop->data)->itemCleared(static_cast(prop->data)->children); - static_cast(prop->data)->children.clear(); - static_cast(prop->data)->emitChildrenChanged(); + static_cast(prop->data)->clear(); } - void itemAppended() { + void insert(int index, QObject *item) { Q_Q(QQmlObjectModel); - QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.last().item); - attached->setIndex(children.count()-1); + children.insert(index, Item(item)); + for (int i = index; i < children.count(); ++i) { + QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(i).item); + attached->setIndex(i); + } QQmlChangeSet changeSet; - changeSet.insert(children.count() - 1, 1); + changeSet.insert(index, 1); emit q->modelUpdated(changeSet, false); emit q->countChanged(); + emit q->childrenChanged(); } - void itemCleared(const QList &children) { + void move(int from, int to, int n) { Q_Q(QQmlObjectModel); - foreach (const Item &child, children) - emit q->destroyingItem(child.item); - emit q->countChanged(); + if (from > to) { + // Only move forwards - flip if backwards moving + int tfrom = from; + int tto = to; + from = tto; + to = tto+n; + n = tfrom-tto; + } + + QPODVector store; + for (int i = 0; i < to - from; ++i) + store.append(children[from + n + i]); + for (int i = 0; i < n; ++i) + store.append(children[from + i]); + + for (int i = 0; i < store.count(); ++i) { + children[from + i] = store[i]; + QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(from + i).item); + attached->setIndex(from + i); + } + + QQmlChangeSet changeSet; + changeSet.move(from, to, n, -1); + emit q->modelUpdated(changeSet, false); + emit q->childrenChanged(); } - void emitChildrenChanged() { + void remove(int index, int n) { Q_Q(QQmlObjectModel); + for (int i = index; i < index + n; ++i) { + QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(i).item); + attached->setIndex(-1); + } + children.erase(children.begin() + index, children.begin() + index + n); + for (int i = index; i < children.count(); ++i) { + QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(i).item); + attached->setIndex(i); + } + QQmlChangeSet changeSet; + changeSet.remove(index, n); + emit q->modelUpdated(changeSet, false); + emit q->countChanged(); emit q->childrenChanged(); } + void clear() { + Q_Q(QQmlObjectModel); + foreach (const Item &child, children) + emit q->destroyingItem(child.item); + remove(0, children.count()); + } + int indexOf(QObject *item) const { for (int i = 0; i < children.count(); ++i) if (children.at(i).item == item) @@ -258,4 +303,133 @@ QQmlObjectModelAttached *QQmlObjectModel::qmlAttachedProperties(QObject *obj) return QQmlObjectModelAttached::properties(obj); } +/*! + \qmlmethod object QtQml.Models::ObjectModel::get(int index) + \since 5.6 + + Returns the item at \a index in the model. This allows the item + to be accessed or modified from JavaScript: + + \code + Component.onCompleted: { + objectModel.append(objectComponent.createObject()) + console.log(objectModel.get(0).objectName); + objectModel.get(0).objectName = "first"; + } + \endcode + + The \a index must be an element in the list. + + \sa append() +*/ +QObject *QQmlObjectModel::get(int index) const +{ + Q_D(const QQmlObjectModel); + if (index < 0 || index >= d->children.count()) + return 0; + return d->children.at(index).item; +} + +/*! + \qmlmethod QtQml.Models::ObjectModel::append(object item) + \since 5.6 + + Appends a new item to the end of the model. + + \code + objectModel.append(objectComponent.createObject()) + \endcode + + \sa insert(), remove() +*/ +void QQmlObjectModel::append(QObject *object) +{ + Q_D(QQmlObjectModel); + d->insert(count(), object); +} + +/*! + \qmlmethod QtQml.Models::ObjectModel::insert(int index, object item) + \since 5.6 + + Inserts a new item to the model at position \a index. + + \code + objectModel.insert(2, objectComponent.createObject()) + \endcode + + The \a index must be to an existing item in the list, or one past + the end of the list (equivalent to append). + + \sa append(), remove() +*/ +void QQmlObjectModel::insert(int index, QObject *object) +{ + Q_D(QQmlObjectModel); + if (index < 0 || index > count()) { + qmlInfo(this) << tr("insert: index %1 out of range").arg(index); + return; + } + d->insert(index, object); +} + +/*! + \qmlmethod QtQml.Models::ObjectModel::move(int from, int to, int n = 1) + \since 5.6 + + Moves \a n items \a from one position \a to another. + + The from and to ranges must exist; for example, to move the first 3 items + to the end of the model: + + \code + objectModel.move(0, objectModel.count - 3, 3) + \endcode + + \sa append() +*/ +void QQmlObjectModel::move(int from, int to, int n) +{ + Q_D(QQmlObjectModel); + if (n <= 0 || from == to) + return; + if (from < 0 || to < 0 || from + n > count() || to + n > count()) { + qmlInfo(this) << tr("move: out of range"); + return; + } + d->move(from, to, n); +} + +/*! + \qmlmethod QtQml.Models::ObjectModel::remove(int index, int n = 1) + \since 5.6 + + Removes the items at \a index from the model. + + \sa clear() +*/ +void QQmlObjectModel::remove(int index, int n) +{ + Q_D(QQmlObjectModel); + if (index < 0 || n <= 0 || index + n > count()) { + qmlInfo(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+n).arg(count()); + return; + } + d->remove(index, n); +} + +/*! + \qmlmethod QtQml.Models::ObjectModel::clear() + \since 5.6 + + Clears all items from the model. + + \sa append(), remove() +*/ +void QQmlObjectModel::clear() +{ + Q_D(QQmlObjectModel); + d->clear(); +} + QT_END_NAMESPACE diff --git a/src/qml/types/qqmlobjectmodel_p.h b/src/qml/types/qqmlobjectmodel_p.h index 4c37a5ac30..9f56ad7121 100644 --- a/src/qml/types/qqmlobjectmodel_p.h +++ b/src/qml/types/qqmlobjectmodel_p.h @@ -107,6 +107,15 @@ public: static QQmlObjectModelAttached *qmlAttachedProperties(QObject *obj); + Q_REVISION(3) Q_INVOKABLE QObject *get(int index) const; + Q_REVISION(3) Q_INVOKABLE void append(QObject *object); + Q_REVISION(3) Q_INVOKABLE void insert(int index, QObject *object); + Q_REVISION(3) Q_INVOKABLE void move(int from, int to, int n = 1); + Q_REVISION(3) Q_INVOKABLE void remove(int index, int n = 1); + +public Q_SLOTS: + Q_REVISION(3) void clear(); + Q_SIGNALS: void childrenChanged(); @@ -120,7 +129,7 @@ class QQmlObjectModelAttached : public QObject public: QQmlObjectModelAttached(QObject *parent) - : QObject(parent), m_index(0) {} + : QObject(parent), m_index(-1) {} ~QQmlObjectModelAttached() { attachedProperties.remove(parent()); } diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index a5c3211f0d..d9d31ae267 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -59,7 +59,8 @@ PRIVATETESTS += \ qqmlenginecleanup \ v4misc \ qqmltranslation \ - qqmlimport + qqmlimport \ + qqmlobjectmodel qtHaveModule(widgets) { PUBLICTESTS += \ diff --git a/tests/auto/qml/qqmlobjectmodel/qqmlobjectmodel.pro b/tests/auto/qml/qqmlobjectmodel/qqmlobjectmodel.pro new file mode 100644 index 0000000000..f8232f8854 --- /dev/null +++ b/tests/auto/qml/qqmlobjectmodel/qqmlobjectmodel.pro @@ -0,0 +1,10 @@ +CONFIG += testcase +TARGET = tst_qqmlobjectmodel +osx:CONFIG -= app_bundle + +SOURCES += tst_qqmlobjectmodel.cpp + +CONFIG += parallel_test + +QT += qml testlib +QT += core-private qml-private diff --git a/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp b/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp new file mode 100644 index 0000000000..001739e38d --- /dev/null +++ b/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include +#include + +class tst_QQmlObjectModel : public QObject +{ + Q_OBJECT + +private slots: + void changes(); +}; + +static bool compareItems(QQmlObjectModel *model, const QObjectList &items) +{ + for (int i = 0; i < items.count(); ++i) { + if (model->get(i) != items.at(i)) + return false; + } + return true; +} + +void tst_QQmlObjectModel::changes() +{ + QQmlObjectModel model; + + QSignalSpy countSpy(&model, SIGNAL(countChanged())); + QSignalSpy childrenSpy(&model, SIGNAL(childrenChanged())); + + int count = 0; + int countSignals = 0; + int childrenSignals = 0; + + QObjectList items; + QObject item0, item1, item2, item3; + + // append(item0) -> [item0] + model.append(&item0); items.append(&item0); + QCOMPARE(model.count(), ++count); + QVERIFY(compareItems(&model, items)); + QCOMPARE(countSpy.count(), ++countSignals); + QCOMPARE(childrenSpy.count(), ++childrenSignals); + + // insert(0, item1) -> [item1, item0] + model.insert(0, &item1); items.insert(0, &item1); + QCOMPARE(model.count(), ++count); + QVERIFY(compareItems(&model, items)); + QCOMPARE(countSpy.count(), ++countSignals); + QCOMPARE(childrenSpy.count(), ++childrenSignals); + + // append(item2) -> [item1, item0, item2] + model.append(&item2); items.append(&item2); + QCOMPARE(model.count(), ++count); + QVERIFY(compareItems(&model, items)); + QCOMPARE(countSpy.count(), ++countSignals); + QCOMPARE(childrenSpy.count(), ++childrenSignals); + + // insert(2, item3) -> [item1, item0, item3, item2] + model.insert(2, &item3); items.insert(2, &item3); + QCOMPARE(model.count(), ++count); + QVERIFY(compareItems(&model, items)); + QCOMPARE(countSpy.count(), ++countSignals); + QCOMPARE(childrenSpy.count(), ++childrenSignals); + + // move(0, 1) -> [item0, item1, item3, item2] + model.move(0, 1); items.move(0, 1); + QCOMPARE(model.count(), count); + QVERIFY(compareItems(&model, items)); + QCOMPARE(countSpy.count(), countSignals); + QCOMPARE(childrenSpy.count(), ++childrenSignals); + + // move(3, 2) -> [item0, item1, item2, item3] + model.move(3, 2); items.move(3, 2); + QCOMPARE(model.count(), count); + QVERIFY(compareItems(&model, items)); + QCOMPARE(countSpy.count(), countSignals); + QCOMPARE(childrenSpy.count(), ++childrenSignals); + + // remove(0) -> [item1, item2, item3] + model.remove(0); items.removeAt(0); + QCOMPARE(model.count(), --count); + QVERIFY(compareItems(&model, items)); + QCOMPARE(countSpy.count(), ++countSignals); + QCOMPARE(childrenSpy.count(), ++childrenSignals); + + // remove(2) -> [item1, item2] + model.remove(2); items.removeAt(2); + QCOMPARE(model.count(), --count); + QVERIFY(compareItems(&model, items)); + QCOMPARE(countSpy.count(), ++countSignals); + QCOMPARE(childrenSpy.count(), ++childrenSignals); + + // clear() -> [] + model.clear(); items.clear(); + QCOMPARE(model.count(), 0); + QVERIFY(compareItems(&model, items)); + QCOMPARE(countSpy.count(), ++countSignals); + QCOMPARE(childrenSpy.count(), ++childrenSignals); +} + +QTEST_MAIN(tst_QQmlObjectModel) + +#include "tst_qqmlobjectmodel.moc" diff --git a/tests/auto/qmltest/objectmodel/tst_objectmodel.qml b/tests/auto/qmltest/objectmodel/tst_objectmodel.qml new file mode 100644 index 0000000000..e8205a1486 --- /dev/null +++ b/tests/auto/qmltest/objectmodel/tst_objectmodel.qml @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQml 2.1 +import QtQml.Models 2.3 +import QtTest 1.1 + +TestCase { + name: "ObjectModel" + + ObjectModel { + id: model + QtObject { id: static0 } + QtObject { id: static1 } + QtObject { id: static2 } + } + + Component { id: object; QtObject { } } + + function test_attached_index() { + compare(model.count, 3) + compare(static0.ObjectModel.index, 0) + compare(static1.ObjectModel.index, 1) + compare(static2.ObjectModel.index, 2) + + var dynamic0 = object.createObject(model, {objectName: "dynamic0"}) + compare(dynamic0.ObjectModel.index, -1) + model.append(dynamic0) // -> [static0, static1, static2, dynamic0] + compare(model.count, 4) + for (var i = 0; i < model.count; ++i) + compare(model.get(i).ObjectModel.index, i) + + var dynamic1 = object.createObject(model, {objectName: "dynamic1"}) + compare(dynamic1.ObjectModel.index, -1) + model.insert(0, dynamic1) // -> [dynamic1, static0, static1, static2, dynamic0] + compare(model.count, 5) + for (i = 0; i < model.count; ++i) + compare(model.get(i).ObjectModel.index, i) + + model.move(1, 3) // -> [dynamic1, static1, static2, static0, dynamic0] + compare(model.count, 5) + for (i = 0; i < model.count; ++i) + compare(model.get(i).ObjectModel.index, i) + + model.move(4, 0) // -> [dynamic0, dynamic1, static1, static2, static0] + compare(model.count, 5) + for (i = 0; i < model.count; ++i) + compare(model.get(i).ObjectModel.index, i) + + model.remove(1) // -> [dynamic0, static1, static2, static0] + compare(model.count, 4) + compare(dynamic1.ObjectModel.index, -1) + for (i = 0; i < model.count; ++i) + compare(model.get(i).ObjectModel.index, i) + + model.clear() + compare(static0.ObjectModel.index, -1) + compare(static1.ObjectModel.index, -1) + compare(static2.ObjectModel.index, -1) + compare(dynamic0.ObjectModel.index, -1) + compare(dynamic1.ObjectModel.index, -1) + } +} diff --git a/tests/auto/quick/qquicklistview/data/objectmodel.qml b/tests/auto/quick/qquicklistview/data/objectmodel.qml new file mode 100644 index 0000000000..5c23d64cd3 --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/objectmodel.qml @@ -0,0 +1,24 @@ +import QtQuick 2.0 +import QtQml.Models 2.1 + +ListView { + width: 360 + height: 360 + model: ObjectModel { + Rectangle { + width: 20 + height: 20 + color: "red" + } + Rectangle { + width: 20 + height: 20 + color: "green" + } + Rectangle { + width: 20 + height: 20 + color: "blue" + } + } +} diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index 3e19b005b7..28025c3090 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -240,6 +240,7 @@ private slots: void QTBUG_39492(); void jsArrayChange(); + void objectModel(); private: template void items(const QUrl &source); @@ -8001,6 +8002,67 @@ void tst_QQuickListView::jsArrayChange() QCOMPARE(spy.count(), 1); } +static bool compareObjectModel(QQuickListView *listview, QQmlObjectModel *model) +{ + if (listview->count() != model->count()) + return false; + for (int i = 0; i < listview->count(); ++i) { + listview->setCurrentIndex(i); + if (listview->currentItem() != model->get(i)) + return false; + } + return true; +} + +void tst_QQuickListView::objectModel() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("objectmodel.qml")); + + QQuickListView *listview = qobject_cast(component.create()); + QVERIFY(listview); + + QQmlObjectModel *model = listview->model().value(); + QVERIFY(model); + + listview->setCurrentIndex(0); + QVERIFY(listview->currentItem()); + QCOMPARE(listview->currentItem()->property("color").toString(), QColor("red").name()); + + listview->setCurrentIndex(1); + QVERIFY(listview->currentItem()); + QCOMPARE(listview->currentItem()->property("color").toString(), QColor("green").name()); + + listview->setCurrentIndex(2); + QVERIFY(listview->currentItem()); + QCOMPARE(listview->currentItem()->property("color").toString(), QColor("blue").name()); + + QQuickItem *item0 = new QQuickItem(listview); + item0->setSize(QSizeF(20, 20)); + model->append(item0); + QCOMPARE(model->count(), 4); + QVERIFY(compareObjectModel(listview, model)); + + QQuickItem *item1 = new QQuickItem(listview); + item1->setSize(QSizeF(20, 20)); + model->insert(0, item1); + QCOMPARE(model->count(), 5); + QVERIFY(compareObjectModel(listview, model)); + + model->move(1, 2, 3); + QVERIFY(compareObjectModel(listview, model)); + + model->remove(2, 2); + QCOMPARE(model->count(), 3); + QVERIFY(compareObjectModel(listview, model)); + + model->clear(); + QCOMPARE(model->count(), 0); + QCOMPARE(listview->count(), 0); + + delete listview; +} + QTEST_MAIN(tst_QQuickListView) #include "tst_qquicklistview.moc" diff --git a/tests/auto/quick/qquickrepeater/data/objectmodel.qml b/tests/auto/quick/qquickrepeater/data/objectmodel.qml new file mode 100644 index 0000000000..780f53e802 --- /dev/null +++ b/tests/auto/quick/qquickrepeater/data/objectmodel.qml @@ -0,0 +1,28 @@ +import QtQuick 2.0 +import QtQml.Models 2.1 + +Row { + width: 360 + height: 360 + + Repeater { + objectName: "repeater" + model: ObjectModel { + Rectangle { + width: 20 + height: 20 + color: "red" + } + Rectangle { + width: 20 + height: 20 + color: "green" + } + Rectangle { + width: 20 + height: 20 + color: "blue" + } + } + } +} diff --git a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp index 71b1ffa0d6..3d0bb8f1ed 100644 --- a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp +++ b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include "../../shared/util.h" #include "../shared/viewtestutil.h" @@ -77,6 +78,7 @@ private slots: void clearRemovalOrder(); void destroyCount(); void stackingOrder(); + void objectModel(); }; class TestObject : public QObject @@ -913,6 +915,64 @@ void tst_QQuickRepeater::stackingOrder() } while (count < 3); } +static bool compareObjectModel(QQuickRepeater *repeater, QQmlObjectModel *model) +{ + if (repeater->count() != model->count()) + return false; + for (int i = 0; i < repeater->count(); ++i) { + if (repeater->itemAt(i) != model->get(i)) + return false; + } + return true; +} + +void tst_QQuickRepeater::objectModel() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("objectmodel.qml")); + + QQuickItem *positioner = qobject_cast(component.create()); + QVERIFY(positioner); + + QQuickRepeater *repeater = findItem(positioner, "repeater"); + QVERIFY(repeater); + + QQmlObjectModel *model = repeater->model().value(); + QVERIFY(model); + + QVERIFY(repeater->itemAt(0)); + QVERIFY(repeater->itemAt(1)); + QVERIFY(repeater->itemAt(2)); + QCOMPARE(repeater->itemAt(0)->property("color").toString(), QColor("red").name()); + QCOMPARE(repeater->itemAt(1)->property("color").toString(), QColor("green").name()); + QCOMPARE(repeater->itemAt(2)->property("color").toString(), QColor("blue").name()); + + QQuickItem *item0 = new QQuickItem(positioner); + item0->setSize(QSizeF(20, 20)); + model->append(item0); + QCOMPARE(model->count(), 4); + QVERIFY(compareObjectModel(repeater, model)); + + QQuickItem *item1 = new QQuickItem(positioner); + item1->setSize(QSizeF(20, 20)); + model->insert(0, item1); + QCOMPARE(model->count(), 5); + QVERIFY(compareObjectModel(repeater, model)); + + model->move(1, 2, 3); + QVERIFY(compareObjectModel(repeater, model)); + + model->remove(2, 2); + QCOMPARE(model->count(), 3); + QVERIFY(compareObjectModel(repeater, model)); + + model->clear(); + QCOMPARE(model->count(), 0); + QCOMPARE(repeater->count(), 0); + + delete positioner; +} + QTEST_MAIN(tst_QQuickRepeater) #include "tst_qquickrepeater.moc" -- cgit v1.2.3