diff options
-rw-r--r-- | src/qml/qml/qqmlvaluetype.cpp | 20 | ||||
-rw-r--r-- | src/qml/types/qqmlitemmodels.qdoc | 115 | ||||
-rw-r--r-- | src/qml/types/qqmlmodelindexvaluetype.cpp | 60 | ||||
-rw-r--r-- | src/qml/types/qqmlmodelindexvaluetype_p.h | 215 | ||||
-rw-r--r-- | src/qml/types/types.pri | 2 | ||||
-rw-r--r-- | tests/auto/qml/qml.pro | 1 | ||||
-rw-r--r-- | tests/auto/qml/qqmlitemmodels/data/itemselection.qml | 36 | ||||
-rw-r--r-- | tests/auto/qml/qqmlitemmodels/data/itemselectionrange.qml | 32 | ||||
-rw-r--r-- | tests/auto/qml/qqmlitemmodels/data/modelindex.qml | 19 | ||||
-rw-r--r-- | tests/auto/qml/qqmlitemmodels/data/modelindexconversion.qml | 9 | ||||
-rw-r--r-- | tests/auto/qml/qqmlitemmodels/data/modelindexlist.qml | 21 | ||||
-rw-r--r-- | tests/auto/qml/qqmlitemmodels/data/persistentmodelindex.qml | 23 | ||||
-rw-r--r-- | tests/auto/qml/qqmlitemmodels/qqmlitemmodels.pro | 23 | ||||
-rw-r--r-- | tests/auto/qml/qqmlitemmodels/qtestmodel.h | 322 | ||||
-rw-r--r-- | tests/auto/qml/qqmlitemmodels/testtypes.h | 148 | ||||
-rw-r--r-- | tests/auto/qml/qqmlitemmodels/tst_qqmlitemmodels.cpp | 223 |
16 files changed, 1269 insertions, 0 deletions
diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp index be14eb44c2..1528ebda37 100644 --- a/src/qml/qml/qqmlvaluetype.cpp +++ b/src/qml/qml/qqmlvaluetype.cpp @@ -37,6 +37,7 @@ #include <private/qqmlglobal_p.h> #include <QtCore/qdebug.h> #include <private/qmetaobjectbuilder_p.h> +#include <private/qqmlmodelindexvaluetype_p.h> QT_BEGIN_NAMESPACE @@ -61,6 +62,14 @@ QQmlValueTypeFactoryImpl::QQmlValueTypeFactoryImpl() { for (unsigned int ii = 0; ii < QVariant::UserType; ++ii) valueTypes[ii] = 0; + + // See types wrapped in qqmlmodelindexvaluetype_p.h + qRegisterMetaType<QModelIndexList>(); + qRegisterMetaType<QPersistentModelIndex>(); + qRegisterMetaType<QItemSelectionRange>(); + qRegisterMetaType<QItemSelection>(); + QMetaType::registerConverter<QModelIndex, QPersistentModelIndex>(&QQmlModelIndexValueType::toPersistentModelIndex); + QMetaType::registerConverter<QPersistentModelIndex, QModelIndex>(&QQmlPersistentModelIndexValueType::toModelIndex); } QQmlValueTypeFactoryImpl::~QQmlValueTypeFactoryImpl() @@ -101,12 +110,23 @@ const QMetaObject *QQmlValueTypeFactoryImpl::metaObjectForMetaType(int t) return &QQmlRectFValueType::staticMetaObject; case QVariant::EasingCurve: return &QQmlEasingValueType::staticMetaObject; + case QVariant::ModelIndex: + return &QQmlModelIndexValueType::staticMetaObject; default: if (const QMetaObject *mo = QQml_valueTypeProvider()->metaObjectForMetaType(t)) return mo; break; } + if (t == qMetaTypeId<QPersistentModelIndex>()) + return &QQmlPersistentModelIndexValueType::staticMetaObject; + else if (t == qMetaTypeId<QModelIndexList>()) + return &QQmlModelIndexListValueType::staticMetaObject; + else if (t == qMetaTypeId<QItemSelectionRange>()) + return &QQmlItemSelectionRangeValueType::staticMetaObject; + else if (t == qMetaTypeId<QItemSelection>()) + return &QQmlItemSelectionValueType::staticMetaObject; + QMetaType metaType(t); if (metaType.flags() & QMetaType::IsGadget) return metaType.metaObject(); diff --git a/src/qml/types/qqmlitemmodels.qdoc b/src/qml/types/qqmlitemmodels.qdoc new file mode 100644 index 0000000000..25c9321115 --- /dev/null +++ b/src/qml/types/qqmlitemmodels.qdoc @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \chapter QModelIndex & Co. in QML + + Since Qt 5.5, QModelIndex and QPersistentModelIndex are exposed in QML as + value-based types. Also exposed in a similar fashion are QModelIndexList, + QItemSelectionRange and QItemSelection. All objects from these types can + be passed back and forth between QML and C++ as \c var properties or plain + JavaScript variables. + + We detail here which API all these classes get exposed in QML. Please refer + to the C++ documentation for more information. + + \note Since all these types are exposed as gadgets, there are no property + change notification signals emitted. Therefore binding to their properties + may not give the expected results. This is especially true for QPersistentModelIndex. + It is perfectly possible to bind to properties holding any of those types. + + \section1 \l QModelIndex and \l QPersistentModelIndex + + \list + \li \b row : int + \li \b column : int + \li \b parent : QModelIndex + \li \b valid : bool + \li \b model : QAbstractItemModel + \li \b internalId : quint64 + \endlist + + All these properties are read-only, as their C++ counterpart. + + \note The usual caveats apply to QModelIndex in QML. If the underlying model changes + or gets deleted, it may become dangerous to access its properties. Therefore, you + should not store any QModelIndex. You can, however, store QPersistentModelIndexes + in a safe way. + + \section1 \l QItemSelectionRange + + \list + \li \b top : int + \li \b left : int + \li \b bottom : int + \li \b right : int + \li \b width : int + \li \b height : int + \li \b topLeft : QPersistentModelIndex + \li \b bottomRight : QPersistentModelIndex + \li \b parent : QModelIndex + \li \b valid : bool + \li \b empty : bool + \li \b model : QAbstractItemModel + \endlist + + All these properties are read-only, as their C++ counterpart. In addition, + we also expose the following functions: + + \list + \li bool \b{contains}(QModelIndex index) + \li bool \b{contains}(int row, int column, QModelIndex parentIndex) + \li bool \b{intersects}(QItemSelectionRange other) + \li QItemSelectionRange \b{intersected}(QItemSelectionRange other) + \endlist + + \section1 \l QModelIndexList and \l QItemSelection + + Both \l QModelIndexList and \l QItemSelection expose the following properties + and functions as part of their \l QList API: + + \list + \li \b length : int + \li object \b{at}(int i) + \li void \b{append}(object o) + \li void \b{prepend}(o) + \li void \b{insert}(int i, object o) + \li void \b{removeFirst}() + \li void \b{removeLast}() + \li void \b{removeAt}(int i) + \endlist + + In addition, \l QItemSelection also exposes the following functions: + + \list + \li void \b{select}(QModelIndex topLeft, QModelIndex bottomRight) + \li bool \b{contains}(QModelIndex index) + \li void \b{merge}(QItemSelection other, QItemSelectionModel::SelectionFlags command) + \endlist + + \sa ItemSelectionModel +*/ diff --git a/src/qml/types/qqmlmodelindexvaluetype.cpp b/src/qml/types/qqmlmodelindexvaluetype.cpp new file mode 100644 index 0000000000..03ae05b4b5 --- /dev/null +++ b/src/qml/types/qqmlmodelindexvaluetype.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module 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 "qqmlmodelindexvaluetype_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \internal +*/ +QString QQmlModelIndexValueType::propertiesString(const QModelIndex &idx) +{ + if (!idx.isValid()) + return QLatin1String("()"); + return QString(QLatin1String("(%1,%2,0x%3,%4(0x%5))")) + .arg(idx.row()).arg(idx.column()).arg(idx.internalId(), 0, 16) + .arg(idx.model()->metaObject()->className()).arg(quintptr(idx.model()), 0, 16); +} + +/*! + \internal +*/ +QString QQmlItemSelectionRangeValueType::toString() const +{ + return QString(QLatin1String("QItemSelectionRange(%1,%2)")) + .arg(reinterpret_cast<const QQmlPersistentModelIndexValueType *>(&v.topLeft())->toString()) + .arg(reinterpret_cast<const QQmlPersistentModelIndexValueType *>(&v.bottomRight())->toString()); +} + +QT_END_NAMESPACE diff --git a/src/qml/types/qqmlmodelindexvaluetype_p.h b/src/qml/types/qqmlmodelindexvaluetype_p.h new file mode 100644 index 0000000000..0e655ab3d7 --- /dev/null +++ b/src/qml/types/qqmlmodelindexvaluetype_p.h @@ -0,0 +1,215 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module 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$ +** +****************************************************************************/ + +#ifndef QQMLMODELINDEXVALUETYPE_P_H +#define QQMLMODELINDEXVALUETYPE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qabstractitemmodel.h> +#include <QtCore/qitemselectionmodel.h> + +QT_BEGIN_NAMESPACE + +struct QQmlModelIndexValueType +{ + QModelIndex v; + + Q_PROPERTY(int row READ row CONSTANT FINAL) + Q_PROPERTY(int column READ column CONSTANT FINAL) + Q_PROPERTY(QModelIndex parent READ parent FINAL) + Q_PROPERTY(bool valid READ isValid CONSTANT FINAL) + Q_PROPERTY(QAbstractItemModel *model READ model CONSTANT FINAL) + Q_PROPERTY(quint64 internalId READ internalId CONSTANT FINAL) + Q_GADGET + +public: + Q_INVOKABLE QString toString() const + { return QLatin1String("QModelIndex") + propertiesString(v); } + + inline int row() const Q_DECL_NOTHROW { return v.row(); } + inline int column() const Q_DECL_NOTHROW { return v.column(); } + inline QModelIndex parent() const { return v.parent(); } + inline bool isValid() const Q_DECL_NOTHROW { return v.isValid(); } + inline QAbstractItemModel *model() const Q_DECL_NOTHROW + { return const_cast<QAbstractItemModel *>(v.model()); } + quint64 internalId() const { return v.internalId(); } + + static QString propertiesString(const QModelIndex &idx); + + static QPersistentModelIndex toPersistentModelIndex(const QModelIndex &index) + { return QPersistentModelIndex(index); } +}; + +struct QQmlPersistentModelIndexValueType +{ + QPersistentModelIndex v; + + Q_PROPERTY(int row READ row FINAL) + Q_PROPERTY(int column READ column FINAL) + Q_PROPERTY(QModelIndex parent READ parent FINAL) + Q_PROPERTY(bool valid READ isValid FINAL) + Q_PROPERTY(QAbstractItemModel *model READ model FINAL) + Q_PROPERTY(quint64 internalId READ internalId FINAL) + Q_GADGET + +public: + Q_INVOKABLE QString toString() const + { return QLatin1String("QPersistentModelIndex") + QQmlModelIndexValueType::propertiesString(v); } + + inline int row() const { return v.row(); } + inline int column() const { return v.column(); } + inline QModelIndex parent() const { return v.parent(); } + inline bool isValid() const { return v.isValid(); } + inline QAbstractItemModel *model() const { return const_cast<QAbstractItemModel *>(v.model()); } + inline quint64 internalId() const { return v.internalId(); } + + static const QModelIndex &toModelIndex(const QPersistentModelIndex &index) + { return index; } +}; + +struct QQmlItemSelectionRangeValueType +{ + QItemSelectionRange v; + + Q_PROPERTY(int top READ top FINAL) + Q_PROPERTY(int left READ left FINAL) + Q_PROPERTY(int bottom READ bottom FINAL) + Q_PROPERTY(int right READ right FINAL) + Q_PROPERTY(int width READ width FINAL) + Q_PROPERTY(int height READ height FINAL) + Q_PROPERTY(QPersistentModelIndex topLeft READ topLeft FINAL) + Q_PROPERTY(QPersistentModelIndex bottomRight READ bottomRight FINAL) + Q_PROPERTY(QModelIndex parent READ parent FINAL) + Q_PROPERTY(bool valid READ isValid FINAL) + Q_PROPERTY(bool empty READ isEmpty FINAL) + Q_PROPERTY(QAbstractItemModel *model READ model FINAL) + Q_GADGET + +public: + Q_INVOKABLE QString toString() const; + Q_INVOKABLE inline bool contains(const QModelIndex &index) const + { return v.contains(index); } + Q_INVOKABLE inline bool contains(int row, int column, const QModelIndex &parentIndex) const + { return v.contains(row, column, parentIndex); } + Q_INVOKABLE inline bool intersects(const QItemSelectionRange &other) const + { return v.intersects(other); } + Q_INVOKABLE QItemSelectionRange intersected(const QItemSelectionRange &other) const + { return v.intersected(other); } + + inline int top() const { return v.top(); } + inline int left() const { return v.left(); } + inline int bottom() const { return v.bottom(); } + inline int right() const { return v.right(); } + inline int width() const { return v.width(); } + inline int height() const { return v.height(); } + inline QPersistentModelIndex &topLeft() const { return const_cast<QPersistentModelIndex &>(v.topLeft()); } + inline QPersistentModelIndex &bottomRight() const { return const_cast<QPersistentModelIndex &>(v.bottomRight()); } + inline QModelIndex parent() const { return v.parent(); } + inline QAbstractItemModel *model() const { return const_cast<QAbstractItemModel *>(v.model()); } + inline bool isValid() const { return v.isValid(); } + inline bool isEmpty() const { return v.isEmpty(); } +}; + +template<typename V, typename T> +QString q_listToString(const QList<T> &list, const QLatin1String &typeName) +{ + QString result = typeName; + result.append(QLatin1Char('(')); + for (typename QList<T>::size_type i = 0; i < list.count(); ++i) { + if (i) + result.append(QLatin1String(", ")); + result.append(reinterpret_cast<const V *>(&list.at(i))->toString()); + } + return result.append(QLatin1Char(')')); +} + +// Invokable QList<T> API forwarding for value types +#define QLISTVALUETYPE_QML_API(T) \ + Q_PROPERTY(int length READ length FINAL) \ + Q_INVOKABLE T at(int i) { return v.at(i); } \ + Q_INVOKABLE void append(const T &o) { v.append(o); } \ + Q_INVOKABLE void prepend(const T &o) { v.prepend(o); } \ + Q_INVOKABLE void insert(int i, const T &o) { v.insert(i, o); } \ + Q_INVOKABLE void removeFirst() { v.removeFirst(); } \ + Q_INVOKABLE void removeLast() { v.removeLast(); } \ + Q_INVOKABLE void removeAt(int i) { v.removeAt(i); } \ + int length() const { return v.length(); } + +struct QQmlModelIndexListValueType +{ + QModelIndexList v; + + Q_GADGET + +public: + Q_INVOKABLE QString toString() + { return q_listToString<QQmlModelIndexValueType>(v, QLatin1String("")); } + + QLISTVALUETYPE_QML_API(QModelIndex) +}; + +struct QQmlItemSelectionValueType +{ + QItemSelection v; + + Q_GADGET + +public: + Q_INVOKABLE QString toString() + { return q_listToString<QQmlItemSelectionRangeValueType>(v, QLatin1String("QItemSelection")); } + Q_INVOKABLE void select(const QModelIndex &topLeft, const QModelIndex &bottomRight) + { v.select(topLeft, bottomRight); } + Q_INVOKABLE bool contains(const QModelIndex &index) const + { return v.contains(index); } + Q_INVOKABLE void merge(const QItemSelection &other, int command) + { v.merge(other, QItemSelectionModel::SelectionFlags(command)); } + + QLISTVALUETYPE_QML_API(QItemSelectionRange) +}; + +#undef QLISTVALUETYPE_INVOKABLE_API + +QT_END_NAMESPACE + +#endif // QQMLMODELINDEXVALUETYPE_P_H + diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri index 3e6153759d..d2e5020738 100644 --- a/src/qml/types/types.pri +++ b/src/qml/types/types.pri @@ -5,6 +5,7 @@ SOURCES += \ $$PWD/qqmllistmodel.cpp \ $$PWD/qqmllistmodelworkeragent.cpp \ $$PWD/qqmlmodelsmodule.cpp \ + $$PWD/qqmlmodelindexvaluetype.cpp \ $$PWD/qqmlobjectmodel.cpp \ $$PWD/qqmltimer.cpp \ $$PWD/qquickpackage.cpp \ @@ -20,6 +21,7 @@ HEADERS += \ $$PWD/qqmllistmodel_p_p.h \ $$PWD/qqmllistmodelworkeragent_p.h \ $$PWD/qqmlmodelsmodule_p.h \ + $$PWD/qqmlmodelindexvaluetype_p.h \ $$PWD/qqmlobjectmodel_p.h \ $$PWD/qqmltimer_p.h \ $$PWD/qquickpackage_p.h \ diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index 3449050fe8..a5c3211f0d 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -48,6 +48,7 @@ PRIVATETESTS += \ qqmllistcompositor \ qqmllistmodel \ qqmllistmodelworkerscript \ + qqmlitemmodels \ qqmltypeloader \ qqmlparser \ qquickworkerscript \ diff --git a/tests/auto/qml/qqmlitemmodels/data/itemselection.qml b/tests/auto/qml/qqmlitemmodels/data/itemselection.qml new file mode 100644 index 0000000000..57cb6436e9 --- /dev/null +++ b/tests/auto/qml/qqmlitemmodels/data/itemselection.qml @@ -0,0 +1,36 @@ +import Test 1.0 + +ItemModelsTest { + property var itemSelection + property int count + property bool contains: false + + function range(top, bottom, left, right, parent) { + if (parent === undefined) + parent = invalidModelIndex() + var topLeft = model.index(top, left, parent) + var bottomRight = model.index(bottom, right, parent) + return createItemSelectionRange(topLeft, bottomRight) + } + + onModelChanged: { + itemSelection = createItemSelection() + itemSelection.prepend(range(0, 0, 0, 5)) + itemSelection.append(range(0, 5, 0, 0)) + for (var i = 0; i < 3; i++) + itemSelection.insert(i, range(i, i + 1, i + 2, i + 3)) + + var itemSelection2 = createItemSelection() + for (i = 3; i < 6; i++) + itemSelection2.select(model.index(i, i + 1), model.index(i + 2, i + 3)) + + itemSelection.merge(itemSelection2, 2 /*ItemSelectionModel.Select*/) + + count = itemSelection.length + contains = itemSelection.contains(model.index(0, 0)) + + itemSelection.removeAt(3) + itemSelection.removeFirst() + itemSelection.removeLast() + } +} diff --git a/tests/auto/qml/qqmlitemmodels/data/itemselectionrange.qml b/tests/auto/qml/qqmlitemmodels/data/itemselectionrange.qml new file mode 100644 index 0000000000..72f732abaf --- /dev/null +++ b/tests/auto/qml/qqmlitemmodels/data/itemselectionrange.qml @@ -0,0 +1,32 @@ +import Test 1.0 + +ItemModelsTest { + property var itemSelectionRange: createItemSelectionRange(invalidModelIndex(), invalidModelIndex()) + property int top: itemSelectionRange.top + property int left: itemSelectionRange.left + property int bottom: itemSelectionRange.bottom + property int right: itemSelectionRange.right + property int width: itemSelectionRange.width + property int height: itemSelectionRange.height + property bool isValid: itemSelectionRange.valid + property bool isEmpty: itemSelectionRange.empty + property var isrModel: itemSelectionRange.model + property bool contains1: false + property bool contains2: false + property bool intersects: false + property var intersected + + onModelChanged: { + if (model) { + var parentIndex = model.index(0, 0) + var index1 = model.index(3, 0, parentIndex) + var index2 = model.index(5, 6, parentIndex) + itemSelectionRange = createItemSelectionRange(index1, index2) + + contains1 = itemSelectionRange.contains(index1) + contains2 = itemSelectionRange.contains(4, 3, parentIndex) + intersects = itemSelectionRange.intersects(createItemSelectionRange(parentIndex, parentIndex)) + intersected = itemSelectionRange.intersected(createItemSelectionRange(parentIndex, parentIndex)) + } + } +} diff --git a/tests/auto/qml/qqmlitemmodels/data/modelindex.qml b/tests/auto/qml/qqmlitemmodels/data/modelindex.qml new file mode 100644 index 0000000000..0d6e3624cb --- /dev/null +++ b/tests/auto/qml/qqmlitemmodels/data/modelindex.qml @@ -0,0 +1,19 @@ +import Test 1.0 + +ItemModelsTest { + property bool isValid: modelIndex.valid + property int row: modelIndex.row + property int column: modelIndex.column + property var parent: modelIndex.parent + property var model: modelIndex.model + property var internalId: modelIndex.internalId + + onSignalWithModelIndex: { + isValid = index.valid + row = index.row + column = index.column + parent = index.parent + model = index.model + internalId = index.internalId + } +} diff --git a/tests/auto/qml/qqmlitemmodels/data/modelindexconversion.qml b/tests/auto/qml/qqmlitemmodels/data/modelindexconversion.qml new file mode 100644 index 0000000000..91ee05eaa9 --- /dev/null +++ b/tests/auto/qml/qqmlitemmodels/data/modelindexconversion.qml @@ -0,0 +1,9 @@ +import Test 1.0 + +ItemModelsTest { + + onModelChanged: { + modelIndex = createPersistentModelIndex(model.index(0, 0)) + persistentModelIndex = model.index(1, 1) + } +} diff --git a/tests/auto/qml/qqmlitemmodels/data/modelindexlist.qml b/tests/auto/qml/qqmlitemmodels/data/modelindexlist.qml new file mode 100644 index 0000000000..44393392d3 --- /dev/null +++ b/tests/auto/qml/qqmlitemmodels/data/modelindexlist.qml @@ -0,0 +1,21 @@ +import Test 1.0 + +ItemModelsTest { + property var modelIndexList + property int count + + onModelChanged: { + modelIndexList = createModelIndexList() + modelIndexList.prepend(model.index(0, 0)) + modelIndexList.append(model.index(1, 1)) + for (var i = 0; i < 3; i++) + modelIndexList.insert(i, model.index(2 + i, 2 + i)) + + count = modelIndexList.length + modelIndex = modelIndexList.at(0) + + modelIndexList.removeAt(3) + modelIndexList.removeFirst() + modelIndexList.removeLast() + } +} diff --git a/tests/auto/qml/qqmlitemmodels/data/persistentmodelindex.qml b/tests/auto/qml/qqmlitemmodels/data/persistentmodelindex.qml new file mode 100644 index 0000000000..13037065a6 --- /dev/null +++ b/tests/auto/qml/qqmlitemmodels/data/persistentmodelindex.qml @@ -0,0 +1,23 @@ +import Test 1.0 + +ItemModelsTest { + property bool isValid: persistentModelIndex.valid + property int row: persistentModelIndex.row + property int column: persistentModelIndex.column + property var parent: persistentModelIndex.parent + property var model: persistentModelIndex.model + property var internalId: persistentModelIndex.internalId + + property var pmi + + onSignalWithPersistentModelIndex: { + isValid = index.valid + row = index.row + column = index.column + parent = index.parent + model = index.model + internalId = index.internalId + + pmi = createPersistentModelIndex(model.index(0, 0)) + } +} diff --git a/tests/auto/qml/qqmlitemmodels/qqmlitemmodels.pro b/tests/auto/qml/qqmlitemmodels/qqmlitemmodels.pro new file mode 100644 index 0000000000..f76c6d0d25 --- /dev/null +++ b/tests/auto/qml/qqmlitemmodels/qqmlitemmodels.pro @@ -0,0 +1,23 @@ +CONFIG += testcase +TARGET = tst_qqmlitemmodels +macx:CONFIG -= app_bundle + +HEADERS = qtestmodel.h testtypes.h +SOURCES += tst_qqmlitemmodels.cpp + +include (../../shared/util.pri) + +TESTDATA = data/* + +CONFIG += parallel_test + +QT += core qml testlib +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 + +DISTFILES += \ + data/modelindex.qml \ + data/persistentmodelindex.qml \ + data/itemselectionrange.qml \ + data/modelindexlist.qml \ + data/itemselection.qml \ + data/modelindexconversion.qml diff --git a/tests/auto/qml/qqmlitemmodels/qtestmodel.h b/tests/auto/qml/qqmlitemmodels/qtestmodel.h new file mode 100644 index 0000000000..bb0a169652 --- /dev/null +++ b/tests/auto/qml/qqmlitemmodels/qtestmodel.h @@ -0,0 +1,322 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef Q_TEST_MODEL_H +#define Q_TEST_MODEL_H + +#include <QtCore/qabstractitemmodel.h> + +class TestModel: public QAbstractItemModel +{ + Q_OBJECT + +public: + TestModel(QObject *parent = 0): QAbstractItemModel(parent), + fetched(false), rows(10), cols(1), levels(INT_MAX), wrongIndex(false) { init(); } + + TestModel(int _rows, int _cols, QObject *parent = 0): QAbstractItemModel(parent), + fetched(false), rows(_rows), cols(_cols), levels(INT_MAX), wrongIndex(false) { init(); } + + void init() { + decorationsEnabled = false; + alternateChildlessRows = true; + tree = new Node(rows); + } + + inline qint32 level(const QModelIndex &index) const { + Node *n = (Node *)index.internalPointer(); + if (!n) + return -1; + int l = -1; + while (n != tree) { + n = n->parent; + ++l; + } + return l; + } + + void resetModel() + { + beginResetModel(); + fetched = false; + delete tree; + tree = new Node(rows); + endResetModel(); + } + + QString displayData(const QModelIndex &idx) const + { + return QString("[%1,%2,%3,%4]").arg(idx.row()).arg(idx.column()).arg(idx.internalId()).arg(hasChildren(idx)); + } + + bool canFetchMore(const QModelIndex &) const { + return !fetched; + } + + void fetchMore(const QModelIndex &) { + fetched = true; + } + + bool hasChildren(const QModelIndex &parent = QModelIndex()) const { + bool hasFetched = fetched; + fetched = true; + bool r = QAbstractItemModel::hasChildren(parent); + fetched = hasFetched; + return r; + } + + int rowCount(const QModelIndex& parent = QModelIndex()) const { + if (!fetched) + qFatal("%s: rowCount should not be called before fetching", Q_FUNC_INFO); + if ((parent.column() > 0) || (level(parent) > levels) + || (alternateChildlessRows && parent.row() > 0 && (parent.row() & 1))) + return 0; + Node *n = (Node*)parent.internalPointer(); + if (!n) + n = tree; + return n->children.count(); + } + + int columnCount(const QModelIndex& parent = QModelIndex()) const { + if ((parent.column() > 0) || (level(parent) > levels) + || (alternateChildlessRows && parent.row() > 0 && (parent.row() & 1))) + return 0; + return cols; + } + + bool isEditable(const QModelIndex &index) const { + if (index.isValid()) + return true; + return false; + } + + Q_INVOKABLE QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const + { + if (row < 0 || column < 0 || (level(parent) > levels) || column >= cols) + return QModelIndex(); + Node *pn = (Node*)parent.internalPointer(); + if (!pn) + pn = tree; + if (row >= pn->children.count()) + return QModelIndex(); + + Node *n = pn->children.at(row); + if (!n) { + n = new Node(rows, pn); + pn->children[row] = n; + } + return createIndex(row, column, n); + } + + QModelIndex parent(const QModelIndex &index) const + { + Node *n = (Node *)index.internalPointer(); + if (!n || n->parent == tree) + return QModelIndex(); + Q_ASSERT(n->parent->parent); + int parentRow = n->parent->parent->children.indexOf(n->parent); + Q_ASSERT(parentRow != -1); + return createIndex(parentRow, 0, n->parent); + } + + QVariant data(const QModelIndex &idx, int role) const + { + if (!idx.isValid()) + return QVariant(); + + Node *pn = (Node *)idx.internalPointer(); + if (!pn) + pn = tree; + if (pn != tree) + pn = pn->parent; + if (idx.row() < 0 || idx.column() < 0 || idx.column() >= cols + || idx.row() >= pn->children.count()) { + wrongIndex = true; + qWarning("Invalid modelIndex [%d,%d,%p]", idx.row(), idx.column(), + idx.internalPointer()); + return QVariant(); + } + + if (role == Qt::DisplayRole) { + return displayData(idx); + } + + return QVariant(); + } + + bool setData(const QModelIndex &index, const QVariant &value, int role) + { + Q_UNUSED(value); + QVector<int> changedRole(1, role); + emit dataChanged(index, index, changedRole); + return true; + } + + void groupedSetData(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles) + { + emit dataChanged(topLeft, bottomRight, roles); + } + + void changeLayout(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>()) + { + emit layoutAboutToBeChanged(parents); + emit layoutChanged(parents); + } + + bool removeRows(int row, int count, const QModelIndex &parent) + { + beginRemoveRows(parent, row, row + count - 1); + Node *n = (Node *)parent.internalPointer(); + if (!n) + n = tree; + n->removeRows(row, count); + endRemoveRows(); + return true; + } + + void removeLastColumn() + { + beginRemoveColumns(QModelIndex(), cols - 1, cols - 1); + --cols; + endRemoveColumns(); + } + + void removeAllColumns() + { + beginRemoveColumns(QModelIndex(), 0, cols - 1); + cols = 0; + endRemoveColumns(); + } + + bool insertRows(int row, int count, const QModelIndex &parent) + { + beginInsertRows(parent, row, row + count - 1); + Node *n = (Node *)parent.internalPointer(); + if (!n) + n = tree; + n->addRows(row, count); + endInsertRows(); + return true; + } + + bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) + { + Q_ASSERT_X(sourceRow >= 0 && sourceRow < rowCount(sourceParent) + && count > 0 && sourceRow + count < rowCount(sourceParent) + && destinationChild >= 0 && destinationChild <= rowCount(destinationParent), + Q_FUNC_INFO, "Rows out of range."); + Q_ASSERT_X(!(sourceParent == destinationParent && destinationChild >= sourceRow && destinationChild < sourceRow + count), + Q_FUNC_INFO, "Moving rows onto themselves."); + if (!beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild)) + return false; + Node *src = (Node *)sourceParent.internalPointer(); + if (!src) + src = tree; + Node *dest = (Node *)destinationParent.internalPointer(); + if (!dest) + dest = tree; + QVector<Node *> buffer = src->children.mid(sourceRow, count); + if (src != dest) { + src->removeRows(sourceRow, count, true /* keep alive */); + dest->addRows(destinationChild, count); + } else { + QVector<Node *> &c = dest->children; + if (sourceRow < destinationChild) { + memmove(&c[sourceRow], &c[sourceRow + count], sizeof(Node *) * (destinationChild - sourceRow - count)); + destinationChild -= count; + } else { + memmove(&c[destinationChild + count], &c[destinationChild], sizeof(Node *) * (sourceRow - destinationChild)); + } + } + for (int i = 0; i < count; i++) { + Node *n = buffer[i]; + n->parent = dest; + dest->children[i + destinationChild] = n; + } + + endMoveRows(); + return true; + } + + void setDecorationsEnabled(bool enable) + { + decorationsEnabled = enable; + } + + mutable bool fetched; + bool decorationsEnabled; + bool alternateChildlessRows; + int rows, cols; + int levels; + mutable bool wrongIndex; + + struct Node { + Node *parent; + QVector<Node *> children; + + Node(int rows, Node *p = 0) : parent(p) + { + addRows(0, rows); + } + + ~Node() + { + foreach (Node *n, children) + delete n; + } + + void addRows(int row, int count) + { + if (count > 0) { + children.reserve(children.count() + count); + children.insert(row, count, (Node *)0); + } + } + + void removeRows(int row, int count, bool keepAlive = false) + { + int newCount = qMax(children.count() - count, 0); + int effectiveCountDiff = children.count() - newCount; + if (effectiveCountDiff > 0) { + if (!keepAlive) + for (int i = 0; i < effectiveCountDiff; i++) + delete children[i + row]; + children.remove(row, effectiveCountDiff); + } + } + }; + + Node *tree; +}; + +#endif // Q_TEST_MODEL_H diff --git a/tests/auto/qml/qqmlitemmodels/testtypes.h b/tests/auto/qml/qqmlitemmodels/testtypes.h new file mode 100644 index 0000000000..5345609cd3 --- /dev/null +++ b/tests/auto/qml/qqmlitemmodels/testtypes.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef TESTTYPES_H +#define TESTTYPES_H + +#include <QtCore/qabstractitemmodel.h> +#include <QtCore/qitemselectionmodel.h> +#include "qdebug.h" + +class ItemModelsTest : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(QModelIndex modelIndex READ modelIndex WRITE setModelIndex NOTIFY changed) + Q_PROPERTY(QPersistentModelIndex persistentModelIndex READ persistentModelIndex WRITE setPersistentModelIndex NOTIFY changed) + +public: + QModelIndex modelIndex() const + { + return m_modelIndex; + } + + QPersistentModelIndex persistentModelIndex() const + { + return m_persistentModelIndex; + } + + void emitChanged() + { + emit changed(); + } + + void emitSignalWithModelIndex(const QModelIndex &index) + { + emit signalWithModelIndex(index); + } + + void emitSignalWithPersistentModelIndex(const QPersistentModelIndex &index) + { + emit signalWithPersistentModelIndex(index); + } + + QAbstractItemModel * model() const + { + return m_model; + } + + Q_INVOKABLE QModelIndex invalidModelIndex() const + { + return QModelIndex(); + } + + Q_INVOKABLE QModelIndexList createModelIndexList() const + { + return QModelIndexList(); + } + + Q_INVOKABLE QItemSelectionRange createItemSelectionRange(const QModelIndex &tl, const QModelIndex &br) const + { + return QItemSelectionRange(tl, br); + } + + Q_INVOKABLE QItemSelection createItemSelection() + { + return QItemSelection(); + } + + Q_INVOKABLE QPersistentModelIndex createPersistentModelIndex(const QModelIndex &index) + { + return QPersistentModelIndex(index); + } + +public slots: + void setModelIndex(const QModelIndex &arg) + { + if (m_modelIndex == arg) + return; + + m_modelIndex = arg; + emit changed(); + } + + void setPersistentModelIndex(const QPersistentModelIndex &arg) + { + if (m_persistentModelIndex == arg) + return; + + m_persistentModelIndex = arg; + emit changed(); + } + + void setModel(QAbstractItemModel *arg) + { + if (m_model == arg) + return; + + m_model = arg; + emit modelChanged(arg); + } + +signals: + void changed(); + + void signalWithModelIndex(QModelIndex index); + void signalWithPersistentModelIndex(QPersistentModelIndex index); + + void modelChanged(QAbstractItemModel * arg); + +private: + QModelIndex m_modelIndex; + QPersistentModelIndex m_persistentModelIndex; + QAbstractItemModel *m_model; +}; + +#endif // TESTTYPES_H + diff --git a/tests/auto/qml/qqmlitemmodels/tst_qqmlitemmodels.cpp b/tests/auto/qml/qqmlitemmodels/tst_qqmlitemmodels.cpp new file mode 100644 index 0000000000..d7e3931376 --- /dev/null +++ b/tests/auto/qml/qqmlitemmodels/tst_qqmlitemmodels.cpp @@ -0,0 +1,223 @@ +/**************************************************************************** +** +** 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 <qtest.h> +#include <QQmlEngine> +#include <QQmlComponent> +#include <QDebug> +#include <QStringListModel> +#include "../../shared/util.h" +#include "testtypes.h" +#include "qtestmodel.h" + +#define INIT_TEST_OBJECT(fileName, object) \ + QQmlComponent component_##object(&engine, testFileUrl(fileName)); \ + QScopedPointer<ItemModelsTest>object(qobject_cast<ItemModelsTest *>(component_##object.create())); \ + + +class tst_qqmlitemmodels : public QQmlDataTest +{ + Q_OBJECT + +public: + tst_qqmlitemmodels() {} + +private slots: + void initTestCase(); + + void modelIndex(); + void persistentModelIndex(); + void modelIndexConversion(); + void itemSelectionRange(); + void itemSelection(); + void modelIndexList(); + +private: + QQmlEngine engine; +}; + +void tst_qqmlitemmodels::initTestCase() +{ + QQmlDataTest::initTestCase(); + qmlRegisterType<ItemModelsTest>("Test", 1, 0, "ItemModelsTest"); +} + +void tst_qqmlitemmodels::modelIndex() +{ + INIT_TEST_OBJECT("modelindex.qml", object); + TestModel model(10, 10); + + QModelIndex index = object->modelIndex(); + for (int i = 0; i < 5; i++) { + QCOMPARE(object->property("isValid").toBool(), index.isValid()); + QCOMPARE(object->property("row").toInt(), index.row()); + QCOMPARE(object->property("column").toInt(), index.column()); + QCOMPARE(object->property("parent").toModelIndex(), index.parent()); + QCOMPARE(object->property("model").value<QAbstractItemModel *>(), index.model()); + QCOMPARE(object->property("internalId").toULongLong(), index.internalId()); + + if (i < 3) { + index = model.index(2 + i, 4 - i, index); + object->setModelIndex(index); + } else if (i < 4) { + index = model.index(2 + i, 4 - i); + object->emitSignalWithModelIndex(index); + } + } +} + +void tst_qqmlitemmodels::persistentModelIndex() +{ + INIT_TEST_OBJECT("persistentmodelindex.qml", object); + TestModel model(10, 10); + + QPersistentModelIndex index = object->persistentModelIndex(); + for (int i = 0; i < 5; i++) { + QCOMPARE(object->property("isValid").toBool(), index.isValid()); + QCOMPARE(object->property("row").toInt(), index.row()); + QCOMPARE(object->property("column").toInt(), index.column()); + QCOMPARE(object->property("parent").toModelIndex(), index.parent()); + QCOMPARE(object->property("model").value<QAbstractItemModel *>(), index.model()); + QCOMPARE(object->property("internalId").toULongLong(), index.internalId()); + + if (i < 2) { + index = model.index(2 + i, 4 - i, index); + object->setPersistentModelIndex(index); + } else if (i < 3) { + model.removeRow(2); + QVERIFY(!index.isValid()); // QPersistentModelIndex should update + object->emitChanged(); // Help QML get the new values as QPMI doesn't emit anything + } else if (i < 4) { + index = model.index(2 + i, 4 - i); + object->emitSignalWithPersistentModelIndex(index); + } + } + + const QVariant &pmiVariant = object->property("pmi"); + QCOMPARE(pmiVariant.type(), QVariant::UserType); + QCOMPARE(pmiVariant.userType(), qMetaTypeId<QPersistentModelIndex>()); + QCOMPARE(pmiVariant.value<QPersistentModelIndex>(), QPersistentModelIndex(model.index(0, 0))); +} + +void tst_qqmlitemmodels::itemSelectionRange() +{ + INIT_TEST_OBJECT("itemselectionrange.qml", object); + TestModel model(10, 10); + + for (int i = 0; i < 2; i++) { + const QVariant &isrVariant = object->property("itemSelectionRange"); + QCOMPARE(isrVariant.type(), QVariant::UserType); + QCOMPARE(isrVariant.userType(), qMetaTypeId<QItemSelectionRange>()); + const QItemSelectionRange &isr = isrVariant.value<QItemSelectionRange>(); + if (i > 0) { + QModelIndex parentIndex = model.index(0, 0); + QCOMPARE(QModelIndex(isr.topLeft()), model.index(3, 0, parentIndex)); + QCOMPARE(QModelIndex(isr.bottomRight()), model.index(5, 6, parentIndex)); + } else { + QCOMPARE(QModelIndex(isr.topLeft()), QModelIndex()); + QCOMPARE(QModelIndex(isr.bottomRight()), QModelIndex()); + } + + QCOMPARE(object->property("top").toInt(), isr.top()); + QCOMPARE(object->property("left").toInt(), isr.left()); + QCOMPARE(object->property("bottom").toInt(), isr.bottom()); + QCOMPARE(object->property("right").toInt(), isr.right()); + QCOMPARE(object->property("width").toInt(), isr.width()); + QCOMPARE(object->property("height").toInt(), isr.height()); + QCOMPARE(object->property("isValid").toBool(), isr.isValid()); + QCOMPARE(object->property("isEmpty").toBool(), isr.isEmpty()); + QCOMPARE(object->property("isrModel").value<QAbstractItemModel *>(), isr.model()); + + // Set model for the 2nd iteration and test again + object->setModel(&model); + } + + // Check API function calls + QVERIFY(object->property("contains1").toBool()); + QVERIFY(object->property("contains2").toBool()); + QVERIFY(!object->property("intersects").toBool()); + const QVariant &isrVariant = object->property("intersected"); + QCOMPARE(isrVariant.type(), QVariant::UserType); + QCOMPARE(isrVariant.userType(), qMetaTypeId<QItemSelectionRange>()); +} + +void tst_qqmlitemmodels::modelIndexConversion() +{ + INIT_TEST_OBJECT("modelindexconversion.qml", object); + TestModel model(10, 10); + object->setModel(&model); + + QCOMPARE(object->modelIndex(), model.index(0, 0)); + QCOMPARE(object->persistentModelIndex(), QPersistentModelIndex(model.index(1, 1))); +} + +void tst_qqmlitemmodels::itemSelection() +{ + INIT_TEST_OBJECT("itemselection.qml", object); + TestModel model(10, 10); + + object->setModel(&model); + QCOMPARE(object->property("count").toInt(), 8); + QCOMPARE(object->property("contains").toBool(), true); + + QVariant milVariant = object->property("itemSelection"); + QCOMPARE(milVariant.type(), QVariant::UserType); + QCOMPARE(milVariant.userType(), qMetaTypeId<QItemSelection>()); + + const QItemSelection &mil = milVariant.value<QItemSelection>(); + QCOMPARE(mil.count(), 5); +} + +void tst_qqmlitemmodels::modelIndexList() +{ + INIT_TEST_OBJECT("modelindexlist.qml", object); + TestModel model(10, 10); + + object->setModel(&model); + QCOMPARE(object->property("count").toInt(), 5); + + QVariant milVariant = object->property("modelIndexList"); + QCOMPARE(milVariant.type(), QVariant::UserType); + QCOMPARE(milVariant.userType(), qMetaTypeId<QModelIndexList>()); + + const QModelIndexList &mil = milVariant.value<QModelIndexList>(); + QCOMPARE(mil.count(), 2); + QCOMPARE(mil.at(0), model.index(3, 3)); + QCOMPARE(mil.at(1), model.index(4, 4)); +} + +#undef INIT_TEST_OBJECT + +QTEST_MAIN(tst_qqmlitemmodels) + +#include "tst_qqmlitemmodels.moc" |