ObjectModel: add API for dynamic changes
authorJ-P Nurmi <jpnurmi@theqtcompany.com>
Mon, 18 May 2015 15:37:19 +0000 (17:37 +0200)
committerJ-P Nurmi <jpnurmi@theqtcompany.com>
Wed, 10 Jun 2015 06:56:18 +0000 (06:56 +0000)
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 <aalpert@blackberry.com>
Reviewed-by: Gabriel de Dietrich <gabriel.dedietrich@theqtcompany.com>
src/qml/types/qqmlmodelsmodule.cpp
src/qml/types/qqmlobjectmodel.cpp
src/qml/types/qqmlobjectmodel_p.h
tests/auto/qml/qml.pro
tests/auto/qml/qqmlobjectmodel/qqmlobjectmodel.pro [new file with mode: 0644]
tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp [new file with mode: 0644]
tests/auto/qmltest/objectmodel/tst_objectmodel.qml [new file with mode: 0644]
tests/auto/quick/qquicklistview/data/objectmodel.qml [new file with mode: 0644]
tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
tests/auto/quick/qquickrepeater/data/objectmodel.qml [new file with mode: 0644]
tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp

index 3e53efd..062d30c 100644 (file)
@@ -48,6 +48,7 @@ void QQmlModelsModule::defineModule()
     qmlRegisterType<QQmlDelegateModel>(uri, 2, 1, "DelegateModel");
     qmlRegisterType<QQmlDelegateModelGroup>(uri, 2, 1, "DelegateModelGroup");
     qmlRegisterType<QQmlObjectModel>(uri, 2, 1, "ObjectModel");
+    qmlRegisterType<QQmlObjectModel,3>(uri, 2, 3, "ObjectModel");
 
     qmlRegisterType<QItemSelectionModel>(uri, 2, 2, "ItemSelectionModel");
 }
index 1d892be..0076c26 100644 (file)
 #include <QtCore/qcoreapplication.h>
 #include <QtQml/qqmlcontext.h>
 #include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlinfo.h>
 
 #include <private/qqmlchangeset_p.h>
 #include <private/qqmlglobal_p.h>
 #include <private/qobject_p.h>
+#include <private/qpodvector_p.h>
 
 #include <QtCore/qhash.h>
 #include <QtCore/qlist.h>
@@ -67,9 +69,8 @@ public:
     QQmlObjectModelPrivate() : QObjectPrivate() {}
 
     static void children_append(QQmlListProperty<QObject> *prop, QObject *item) {
-        static_cast<QQmlObjectModelPrivate *>(prop->data)->children.append(Item(item));
-        static_cast<QQmlObjectModelPrivate *>(prop->data)->itemAppended();
-        static_cast<QQmlObjectModelPrivate *>(prop->data)->emitChildrenChanged();
+        int index = static_cast<QQmlObjectModelPrivate *>(prop->data)->children.count();
+        static_cast<QQmlObjectModelPrivate *>(prop->data)->insert(index, item);
     }
 
     static int children_count(QQmlListProperty<QObject> *prop) {
@@ -81,33 +82,77 @@ public:
     }
 
     static void children_clear(QQmlListProperty<QObject> *prop) {
-        static_cast<QQmlObjectModelPrivate *>(prop->data)->itemCleared(static_cast<QQmlObjectModelPrivate *>(prop->data)->children);
-        static_cast<QQmlObjectModelPrivate *>(prop->data)->children.clear();
-        static_cast<QQmlObjectModelPrivate *>(prop->data)->emitChildrenChanged();
+        static_cast<QQmlObjectModelPrivate *>(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<Item> &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<QQmlObjectModelPrivate::Item, 4> 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
index 4c37a5a..9f56ad7 100644 (file)
@@ -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());
     }
index a5c3211..d9d31ae 100644 (file)
@@ -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 (file)
index 0000000..f8232f8
--- /dev/null
@@ -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 (file)
index 0000000..001739e
--- /dev/null
@@ -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 <QtQml/private/qqmlobjectmodel_p.h>
+#include <QtTest/qsignalspy.h>
+#include <QtTest/qtest.h>
+
+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 (file)
index 0000000..e8205a1
--- /dev/null
@@ -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 (file)
index 0000000..5c23d64
--- /dev/null
@@ -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"
+        }
+    }
+}
index 3e19b00..28025c3 100644 (file)
@@ -240,6 +240,7 @@ private slots:
     void QTBUG_39492();
 
     void jsArrayChange();
+    void objectModel();
 
 private:
     template <class T> 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<QQuickListView *>(component.create());
+    QVERIFY(listview);
+
+    QQmlObjectModel *model = listview->model().value<QQmlObjectModel *>();
+    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 (file)
index 0000000..780f53e
--- /dev/null
@@ -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"
+            }
+        }
+    }
+}
index 71b1ffa..3d0bb8f 100644 (file)
@@ -41,6 +41,7 @@
 #include <private/qquickrepeater_p.h>
 #include <QtQuick/private/qquicktext_p.h>
 #include <QtQml/private/qqmllistmodel_p.h>
+#include <QtQml/private/qqmlobjectmodel_p.h>
 
 #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<QQuickItem *>(component.create());
+    QVERIFY(positioner);
+
+    QQuickRepeater *repeater = findItem<QQuickRepeater>(positioner, "repeater");
+    QVERIFY(repeater);
+
+    QQmlObjectModel *model = repeater->model().value<QQmlObjectModel *>();
+    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"