aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quick/items/qquicktableview.cpp84
-rw-r--r--src/quick/items/qquicktableview_p.h7
-rw-r--r--src/quick/items/qquicktableview_p_p.h6
-rw-r--r--tests/auto/quick/qquicktableview/data/countingtableview.qml14
-rw-r--r--tests/auto/quick/qquicktableview/data/qqmllistpropertymodel.qml85
-rw-r--r--tests/auto/quick/qquicktableview/tst_qquicktableview.cpp218
6 files changed, 394 insertions, 20 deletions
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp
index 54f759efd7..7216cbd918 100644
--- a/src/quick/items/qquicktableview.cpp
+++ b/src/quick/items/qquicktableview.cpp
@@ -61,6 +61,16 @@ Q_LOGGING_CATEGORY(lcTableViewDelegateLifecycle, "qt.quick.tableview.lifecycle")
static const Qt::Edge allTableEdges[] = { Qt::LeftEdge, Qt::RightEdge, Qt::TopEdge, Qt::BottomEdge };
static const int kBufferTimerInterval = 300;
+// Set the maximum life time of an item in the pool to be at least the number of
+// dimensions, which for a table is two. The reason is that the user might flick
+// both e.g the left column and the top row out before a new right column and bottom
+// row gets flicked in. This means we will end up with one column plus one row of
+// items in the pool. And flicking in a new column and a new row will typically happen
+// in separate updatePolish calls (unless you flick them both in at exactly the same
+// time). This means that we should allow flicked out items to stay in the pool for at least
+// two load cycles, to keep more items in circulation instead of deleting them prematurely.
+static const int kMaxPoolTime = 2;
+
static QLine rectangleEdge(const QRect &rect, Qt::Edge tableEdge)
{
switch (tableEdge) {
@@ -372,16 +382,26 @@ void QQuickTableViewPrivate::releaseLoadedItems() {
auto const tmpList = loadedItems;
loadedItems.clear();
for (FxTableItem *item : tmpList)
- releaseItem(item);
+ releaseItem(item, QQmlTableInstanceModel::NotReusable);
}
-void QQuickTableViewPrivate::releaseItem(FxTableItem *fxTableItem)
+void QQuickTableViewPrivate::releaseItem(FxTableItem *fxTableItem, QQmlTableInstanceModel::ReusableFlag reusableFlag)
{
- if (fxTableItem->item) {
- if (fxTableItem->ownItem)
- delete fxTableItem->item;
- else if (model->release(fxTableItem->item) != QQmlInstanceModel::Destroyed)
- fxTableItem->item->setParentItem(nullptr);
+ Q_TABLEVIEW_ASSERT(fxTableItem->item, fxTableItem->index);
+
+ if (fxTableItem->ownItem) {
+ delete fxTableItem->item;
+ } else {
+ // Only QQmlTableInstanceModel supports reusing items
+ auto releaseFlag = tableModel ?
+ tableModel->release(fxTableItem->item, reusableFlag) :
+ model->release(fxTableItem->item);
+
+ if (releaseFlag != QQmlInstanceModel::Destroyed) {
+ // When items are not released, it typically means that the item is reused, or
+ // that the model is an ObjectModel. If so, we just hide the item instead.
+ fxTableItem->setVisible(false);
+ }
}
delete fxTableItem;
@@ -408,7 +428,7 @@ void QQuickTableViewPrivate::unloadItem(const QPoint &cell)
{
const int modelIndex = modelIndexAtCell(cell);
Q_TABLEVIEW_ASSERT(loadedItems.contains(modelIndex), modelIndex << cell);
- releaseItem(loadedItems.take(modelIndex));
+ releaseItem(loadedItems.take(modelIndex), reusableFlag);
}
void QQuickTableViewPrivate::unloadItems(const QLine &items)
@@ -849,6 +869,12 @@ void QQuickTableViewPrivate::processLoadRequest()
loadRequest.markAsDone();
qCDebug(lcTableViewDelegateLifecycle()) << "request completed! Table:" << tableLayoutToString();
+
+ if (tableModel) {
+ // Whenever we're done loading a row or column, we drain the
+ // table models reuse pool of superfluous items that weren't reused.
+ tableModel->drainReusableItemsPool(kMaxPoolTime);
+ }
}
void QQuickTableViewPrivate::beginRebuildTable()
@@ -1106,6 +1132,22 @@ void QQuickTableViewPrivate::initItemCallback(int modelIndex, QObject *object)
attached->setTableView(q);
}
+void QQuickTableViewPrivate::itemPooledCallback(int modelIndex, QObject *object)
+{
+ Q_UNUSED(modelIndex);
+
+ if (auto attached = getAttachedObject(object))
+ emit attached->pooled();
+}
+
+void QQuickTableViewPrivate::itemReusedCallback(int modelIndex, QObject *object)
+{
+ Q_UNUSED(modelIndex);
+
+ if (auto attached = getAttachedObject(object))
+ emit attached->reused();
+}
+
void QQuickTableViewPrivate::connectToModel()
{
Q_TABLEVIEW_ASSERT(model, "");
@@ -1113,6 +1155,11 @@ void QQuickTableViewPrivate::connectToModel()
QObjectPrivate::connect(model, &QQmlInstanceModel::createdItem, this, &QQuickTableViewPrivate::itemCreatedCallback);
QObjectPrivate::connect(model, &QQmlInstanceModel::initItem, this, &QQuickTableViewPrivate::initItemCallback);
+ if (tableModel) {
+ QObjectPrivate::connect(tableModel, &QQmlTableInstanceModel::itemPooled, this, &QQuickTableViewPrivate::itemPooledCallback);
+ QObjectPrivate::connect(tableModel, &QQmlTableInstanceModel::itemReused, this, &QQuickTableViewPrivate::itemReusedCallback);
+ }
+
if (auto const aim = model->abstractItemModel()) {
// When the model exposes a QAIM, we connect to it directly. This means that if the current model is
// a QQmlDelegateModel, we just ignore all the change sets it emits. In most cases, the model will instead
@@ -1139,6 +1186,11 @@ void QQuickTableViewPrivate::disconnectFromModel()
QObjectPrivate::disconnect(model, &QQmlInstanceModel::createdItem, this, &QQuickTableViewPrivate::itemCreatedCallback);
QObjectPrivate::disconnect(model, &QQmlInstanceModel::initItem, this, &QQuickTableViewPrivate::initItemCallback);
+ if (tableModel) {
+ QObjectPrivate::disconnect(tableModel, &QQmlTableInstanceModel::itemPooled, this, &QQuickTableViewPrivate::itemPooledCallback);
+ QObjectPrivate::disconnect(tableModel, &QQmlTableInstanceModel::itemReused, this, &QQuickTableViewPrivate::itemReusedCallback);
+ }
+
if (auto const aim = model->abstractItemModel()) {
disconnect(aim, &QAbstractItemModel::dataChanged, this, &QQuickTableViewPrivate::dataChangedCallback);
disconnect(aim, &QAbstractItemModel::rowsMoved, this, &QQuickTableViewPrivate::rowsMovedCallback);
@@ -1458,6 +1510,22 @@ void QQuickTableView::setDelegate(QQmlComponent *newDelegate)
emit delegateChanged();
}
+bool QQuickTableView::reuseItems() const
+{
+ return bool(d_func()->reusableFlag == QQmlTableInstanceModel::Reusable);
+}
+
+void QQuickTableView::setReuseItems(bool reuse)
+{
+ Q_D(QQuickTableView);
+ if (reuseItems() == reuse)
+ return;
+
+ d->reusableFlag = reuse ? QQmlTableInstanceModel::Reusable : QQmlTableInstanceModel::NotReusable;
+
+ emit reuseItemsChanged();
+}
+
QQuickTableViewAttached *QQuickTableView::qmlAttachedProperties(QObject *obj)
{
return new QQuickTableViewAttached(obj);
diff --git a/src/quick/items/qquicktableview_p.h b/src/quick/items/qquicktableview_p.h
index fe18de5cc4..fa7561554e 100644
--- a/src/quick/items/qquicktableview_p.h
+++ b/src/quick/items/qquicktableview_p.h
@@ -78,6 +78,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTableView : public QQuickFlickable
Q_PROPERTY(QJSValue columnWidthProvider READ columnWidthProvider WRITE setColumnWidthProvider NOTIFY columnWidthProviderChanged)
Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged)
Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
+ Q_PROPERTY(bool reuseItems READ reuseItems WRITE setReuseItems NOTIFY reuseItemsChanged)
public:
QQuickTableView(QQuickItem *parent = nullptr);
@@ -118,6 +119,9 @@ public:
QQmlComponent *delegate() const;
void setDelegate(QQmlComponent *);
+ bool reuseItems() const;
+ void setReuseItems(bool reuseItems);
+
static QQuickTableViewAttached *qmlAttachedProperties(QObject *);
Q_SIGNALS:
@@ -134,6 +138,7 @@ Q_SIGNALS:
void columnWidthProviderChanged();
void modelChanged();
void delegateChanged();
+ void reuseItemsChanged();
protected:
void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
@@ -165,6 +170,8 @@ public:
Q_SIGNALS:
void tableViewChanged();
+ void pooled();
+ void reused();
private:
QPointer<QQuickTableView> m_tableview;
diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h
index 1fc73018e9..7e8561850a 100644
--- a/src/quick/items/qquicktableview_p_p.h
+++ b/src/quick/items/qquicktableview_p_p.h
@@ -204,6 +204,8 @@ public:
QTimer cacheBufferDelayTimer;
bool hasBufferedItems = false;
+ QQmlTableInstanceModel::ReusableFlag reusableFlag = QQmlTableInstanceModel::Reusable;
+
bool blockItemCreatedCallback = false;
bool tableInvalid = false;
bool tableRebuilding = false;
@@ -264,7 +266,7 @@ public:
FxTableItem *createFxTableItem(const QPoint &cell, QQmlIncubator::IncubationMode incubationMode);
FxTableItem *loadFxTableItem(const QPoint &cell, QQmlIncubator::IncubationMode incubationMode);
- void releaseItem(FxTableItem *fxTableItem);
+ void releaseItem(FxTableItem *fxTableItem, QQmlTableInstanceModel::ReusableFlag reusableFlag);
void releaseLoadedItems();
void clear();
@@ -291,6 +293,8 @@ public:
void initItemCallback(int modelIndex, QObject *item);
void itemCreatedCallback(int modelIndex, QObject *object);
+ void itemPooledCallback(int modelIndex, QObject *object);
+ void itemReusedCallback(int modelIndex, QObject *object);
void modelUpdated(const QQmlChangeSet &changeSet, bool reset);
void connectToModel();
diff --git a/tests/auto/quick/qquicktableview/data/countingtableview.qml b/tests/auto/quick/qquicktableview/data/countingtableview.qml
index 1e8e9f43fb..c736d570b8 100644
--- a/tests/auto/quick/qquicktableview/data/countingtableview.qml
+++ b/tests/auto/quick/qquicktableview/data/countingtableview.qml
@@ -47,8 +47,13 @@ Item {
height: 450
property alias tableView: tableView
+
+ // currentDelegateCount is the number of currently visible items
property int currentDelegateCount: 0
+ // maxDelegateCount is the largest number of items that has ever been visible at the same time
property int maxDelegateCount: 0
+ // delegatesCreatedCount is the number of items created during the lifetime of the test
+ property int delegatesCreatedCount: 0
TableView {
id: tableView
@@ -68,11 +73,18 @@ Item {
implicitHeight: 50
color: "lightgray"
border.width: 1
+
+ property int pooledCount: 0
+ property int reusedCount: 0
+ TableView.onPooled: pooledCount++;
+ TableView.onReused: reusedCount++;
+
Text {
anchors.centerIn: parent
- text: modelData
+ text: column
}
Component.onCompleted: {
+ delegatesCreatedCount++;
currentDelegateCount++;
maxDelegateCount = Math.max(maxDelegateCount, currentDelegateCount);
}
diff --git a/tests/auto/quick/qquicktableview/data/qqmllistpropertymodel.qml b/tests/auto/quick/qquicktableview/data/qqmllistpropertymodel.qml
new file mode 100644
index 0000000000..c866af8526
--- /dev/null
+++ b/tests/auto/quick/qquicktableview/data/qqmllistpropertymodel.qml
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtQuick.Window 2.3
+import Qt.labs.tableview 1.0
+
+Item {
+ id: root
+ width: 640
+ height: 450
+
+ property alias tableView: tableView
+
+ TableView {
+ id: tableView
+ width: 600
+ height: 400
+ anchors.margins: 1
+ clip: true
+ delegate: tableViewDelegate
+ cacheBuffer: 0
+ }
+
+ Item {
+ Repeater {
+ model: 100
+ Item { property string someCustomProperty: index }
+ }
+ Component.onCompleted: tableView.model = children
+ }
+
+ Component {
+ id: tableViewDelegate
+ Rectangle {
+ objectName: "tableViewDelegate"
+ implicitWidth: 100
+ implicitHeight: 50
+ color: "lightgray"
+ border.width: 1
+
+ Text {
+ anchors.centerIn: parent
+ text: column
+ }
+ }
+ }
+
+}
diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
index 1cedde4f79..86839b61dc 100644
--- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
+++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
@@ -50,6 +50,7 @@ using namespace QQuickVisualTestUtil;
static const char* kTableViewPropName = "tableView";
static const char* kDelegateObjectName = "tableViewDelegate";
+static const char *kDelegatesCreatedCountProp = "delegatesCreatedCount";
Q_DECLARE_METATYPE(QMarginsF);
@@ -112,6 +113,12 @@ private slots:
void flickOvershoot();
void checkRowColumnCount();
void modelSignals();
+ void checkIfDelegatesAreReused_data();
+ void checkIfDelegatesAreReused();
+ void checkContextProperties_data();
+ void checkContextProperties();
+ void checkContextPropertiesQQmlListProperyModel_data();
+ void checkContextPropertiesQQmlListProperyModel();
};
tst_QQuickTableView::tst_QQuickTableView()
@@ -757,11 +764,16 @@ void tst_QQuickTableView::flick_data()
{
QTest::addColumn<QSizeF>("spacing");
QTest::addColumn<QMarginsF>("margins");
-
- QTest::newRow("s:0 m:0") << QSizeF(0, 0) << QMarginsF(0, 0, 0, 0);
- QTest::newRow("s:5 m:0") << QSizeF(5, 5) << QMarginsF(0, 0, 0, 0);
- QTest::newRow("s:0 m:20") << QSizeF(0, 0) << QMarginsF(20, 20, 20, 20);
- QTest::newRow("s:5 m:20") << QSizeF(5, 5) << QMarginsF(20, 20, 20, 20);
+ QTest::addColumn<bool>("reuseItems");
+
+ QTest::newRow("s:0 m:0 reuse") << QSizeF(0, 0) << QMarginsF(0, 0, 0, 0) << true;
+ QTest::newRow("s:5 m:0 reuse") << QSizeF(5, 5) << QMarginsF(0, 0, 0, 0) << true;
+ QTest::newRow("s:0 m:20 reuse") << QSizeF(0, 0) << QMarginsF(20, 20, 20, 20) << true;
+ QTest::newRow("s:5 m:20 reuse") << QSizeF(5, 5) << QMarginsF(20, 20, 20, 20) << true;
+ QTest::newRow("s:0 m:0") << QSizeF(0, 0) << QMarginsF(0, 0, 0, 0) << false;
+ QTest::newRow("s:5 m:0") << QSizeF(5, 5) << QMarginsF(0, 0, 0, 0) << false;
+ QTest::newRow("s:0 m:20") << QSizeF(0, 0) << QMarginsF(20, 20, 20, 20) << false;
+ QTest::newRow("s:5 m:20") << QSizeF(5, 5) << QMarginsF(20, 20, 20, 20) << false;
}
void tst_QQuickTableView::flick()
@@ -770,6 +782,7 @@ void tst_QQuickTableView::flick()
// with different table configurations.
QFETCH(QSizeF, spacing);
QFETCH(QMarginsF, margins);
+ QFETCH(bool, reuseItems);
LOAD_TABLEVIEW("plaintableview.qml");
const qreal delegateWidth = 100;
@@ -788,6 +801,7 @@ void tst_QQuickTableView::flick()
tableView->setRightMargin(margins.right());
tableView->setBottomMargin(margins.bottom());
tableView->setCacheBuffer(0);
+ tableView->setReuseItems(reuseItems);
tableView->setWidth(margins.left() + (visualColumnCount * cellWidth) - spacing.width());
tableView->setHeight(margins.top() + (visualRowCount * cellHeight) - spacing.height());
@@ -821,11 +835,16 @@ void tst_QQuickTableView::flickOvershoot_data()
{
QTest::addColumn<QSizeF>("spacing");
QTest::addColumn<QMarginsF>("margins");
-
- QTest::newRow("s:0 m:0") << QSizeF(0, 0) << QMarginsF(0, 0, 0, 0);
- QTest::newRow("s:5 m:0") << QSizeF(5, 5) << QMarginsF(0, 0, 0, 0);
- QTest::newRow("s:0 m:20") << QSizeF(0, 0) << QMarginsF(20, 20, 20, 20);
- QTest::newRow("s:5 m:20") << QSizeF(5, 5) << QMarginsF(20, 20, 20, 20);
+ QTest::addColumn<bool>("reuseItems");
+
+ QTest::newRow("s:0 m:0 reuse") << QSizeF(0, 0) << QMarginsF(0, 0, 0, 0) << true;
+ QTest::newRow("s:5 m:0 reuse") << QSizeF(5, 5) << QMarginsF(0, 0, 0, 0) << true;
+ QTest::newRow("s:0 m:20 reuse") << QSizeF(0, 0) << QMarginsF(20, 20, 20, 20) << true;
+ QTest::newRow("s:5 m:20 reuse") << QSizeF(5, 5) << QMarginsF(20, 20, 20, 20) << true;
+ QTest::newRow("s:0 m:0") << QSizeF(0, 0) << QMarginsF(0, 0, 0, 0) << false;
+ QTest::newRow("s:5 m:0") << QSizeF(5, 5) << QMarginsF(0, 0, 0, 0) << false;
+ QTest::newRow("s:0 m:20") << QSizeF(0, 0) << QMarginsF(20, 20, 20, 20) << false;
+ QTest::newRow("s:5 m:20") << QSizeF(5, 5) << QMarginsF(20, 20, 20, 20) << false;
}
void tst_QQuickTableView::flickOvershoot()
@@ -836,6 +855,7 @@ void tst_QQuickTableView::flickOvershoot()
// when everything is flicked out of view.
QFETCH(QSizeF, spacing);
QFETCH(QMarginsF, margins);
+ QFETCH(bool, reuseItems);
LOAD_TABLEVIEW("plaintableview.qml");
const int rowCount = 5;
@@ -857,6 +877,7 @@ void tst_QQuickTableView::flickOvershoot()
tableView->setRightMargin(margins.right());
tableView->setBottomMargin(margins.bottom());
tableView->setCacheBuffer(0);
+ tableView->setReuseItems(reuseItems);
tableView->setWidth(tableWidth - margins.right() - cellWidth / 2);
tableView->setHeight(tableHeight - margins.bottom() - cellHeight / 2);
@@ -1092,6 +1113,183 @@ void tst_QQuickTableView::modelSignals()
QCOMPARE(tableView->columns(), 1);
}
+void tst_QQuickTableView::checkIfDelegatesAreReused_data()
+{
+ QTest::addColumn<bool>("reuseItems");
+
+ QTest::newRow("reuse = true") << true;
+ QTest::newRow("reuse = false") << false;
+}
+
+void tst_QQuickTableView::checkIfDelegatesAreReused()
+{
+ // Check that we end up reusing delegate items while flicking if
+ // TableView has reuseItems set to true, but otherwise not.
+ QFETCH(bool, reuseItems);
+ LOAD_TABLEVIEW("countingtableview.qml");
+
+ const qreal delegateWidth = 100;
+ const int pageFlickCount = 1;
+
+ auto model = TestModelAsVariant(100, 100);
+ tableView->setModel(model);
+ tableView->setReuseItems(reuseItems);
+
+ // Flick half an item to the left, to force one extra column to load before we start.
+ // This will make things less complicated below, when checking how many times the items
+ // have been reused (all items will then report the same number).
+ tableView->setContentX(delegateWidth / 2);
+ tableView->polish();
+
+ WAIT_UNTIL_POLISHED;
+
+ const int visibleColumnCount = tableViewPrivate->loadedTable.width();
+ const int visibleRowCount = tableViewPrivate->loadedTable.height();
+ const int delegateCountAfterInit = view->rootObject()->property(kDelegatesCreatedCountProp).toInt();
+
+ for (int column = 1; column <= (visibleColumnCount * pageFlickCount); ++column) {
+ // Flick columns to the left (and add one pixel to ensure the left column is completely out)
+ tableView->setContentX((delegateWidth * column) + 1);
+ tableView->polish();
+
+ WAIT_UNTIL_POLISHED;
+
+ // Check that the number of delegate items created so far is what we expect.
+ const int delegatesCreatedCount = view->rootObject()->property(kDelegatesCreatedCountProp).toInt();
+ int expectedCount = delegateCountAfterInit + (reuseItems ? 0 : visibleRowCount * column);
+ QCOMPARE(delegatesCreatedCount, expectedCount);
+ }
+
+ // Check that each delegate item has been reused as many times
+ // as we have flicked pages (if reuse is enabled).
+ for (auto fxItem : tableViewPrivate->loadedItems) {
+ int pooledCount = fxItem->item->property("pooledCount").toInt();
+ int reusedCount = fxItem->item->property("reusedCount").toInt();
+ if (reuseItems) {
+ QCOMPARE(pooledCount, pageFlickCount);
+ QCOMPARE(reusedCount, pageFlickCount);
+ } else {
+ QCOMPARE(pooledCount, 0);
+ QCOMPARE(reusedCount, 0);
+ }
+ }
+}
+
+void tst_QQuickTableView::checkContextProperties_data()
+{
+ QTest::addColumn<QVariant>("model");
+ QTest::addColumn<bool>("reuseItems");
+
+ auto stringList = QStringList();
+ for (int i = 0; i < 100; ++i)
+ stringList.append(QString::number(i));
+
+ QTest::newRow("QAIM, reuse=false") << TestModelAsVariant(100, 100) << false;
+ QTest::newRow("QAIM, reuse=true") << TestModelAsVariant(100, 100) << true;
+ QTest::newRow("Number model, reuse=false") << QVariant::fromValue(100) << false;
+ QTest::newRow("Number model, reuse=true") << QVariant::fromValue(100) << true;
+ QTest::newRow("QStringList, reuse=false") << QVariant::fromValue(stringList) << false;
+ QTest::newRow("QStringList, reuse=true") << QVariant::fromValue(stringList) << true;
+}
+
+void tst_QQuickTableView::checkContextProperties()
+{
+ // Check that the context properties of the delegate items
+ // are what we expect while flicking, with or without item recycling.
+ QFETCH(QVariant, model);
+ QFETCH(bool, reuseItems);
+ LOAD_TABLEVIEW("countingtableview.qml");
+
+ const qreal delegateWidth = 100;
+ const qreal delegateHeight = 50;
+ const int rowCount = 100;
+ const int pageFlickCount = 3;
+
+ tableView->setModel(model);
+ tableView->setReuseItems(reuseItems);
+
+ WAIT_UNTIL_POLISHED;
+
+ const int visibleRowCount = qMin(tableView->rows(), qCeil(tableView->height() / delegateHeight));
+ const int visibleColumnCount = qMin(tableView->columns(), qCeil(tableView->width() / delegateWidth));
+
+ for (int row = 1; row <= (visibleRowCount * pageFlickCount); ++row) {
+ // Flick rows up
+ tableView->setContentY((delegateHeight * row) + (delegateHeight / 2));
+ tableView->polish();
+
+ WAIT_UNTIL_POLISHED;
+
+ for (int col = 0; col < visibleColumnCount; ++col) {
+ const auto item = tableViewPrivate->loadedTableItem(QPoint(col, row))->item;
+ const auto context = qmlContext(item.data());
+ const int contextIndex = context->contextProperty("index").toInt();
+ const int contextRow = context->contextProperty("row").toInt();
+ const int contextColumn = context->contextProperty("column").toInt();
+ const QString contextModelData = context->contextProperty("modelData").toString();
+
+ QCOMPARE(contextIndex, row + (col * rowCount));
+ QCOMPARE(contextRow, row);
+ QCOMPARE(contextColumn, col);
+ QCOMPARE(contextModelData, QStringLiteral("%1").arg(row));
+ }
+ }
+}
+
+void tst_QQuickTableView::checkContextPropertiesQQmlListProperyModel_data()
+{
+ QTest::addColumn<bool>("reuseItems");
+
+ QTest::newRow("reuse=false") << false;
+ QTest::newRow("reuse=true") << true;
+}
+
+void tst_QQuickTableView::checkContextPropertiesQQmlListProperyModel()
+{
+ // Check that the context properties of the delegate items
+ // are what we expect while flicking, with or without item recycling.
+ // This test hard-codes the model to be a QQmlListPropertyModel from
+ // within the qml file.
+ QFETCH(bool, reuseItems);
+ LOAD_TABLEVIEW("qqmllistpropertymodel.qml");
+
+ const qreal delegateWidth = 100;
+ const qreal delegateHeight = 50;
+ const int rowCount = 100;
+ const int pageFlickCount = 3;
+
+ tableView->setReuseItems(reuseItems);
+ tableView->polish();
+
+ WAIT_UNTIL_POLISHED;
+
+ const int visibleRowCount = qMin(tableView->rows(), qCeil(tableView->height() / delegateHeight));
+ const int visibleColumnCount = qMin(tableView->columns(), qCeil(tableView->width() / delegateWidth));
+
+ for (int row = 1; row <= (visibleRowCount * pageFlickCount); ++row) {
+ // Flick rows up
+ tableView->setContentY((delegateHeight * row) + (delegateHeight / 2));
+ tableView->polish();
+
+ WAIT_UNTIL_POLISHED;
+
+ for (int col = 0; col < visibleColumnCount; ++col) {
+ const auto item = tableViewPrivate->loadedTableItem(QPoint(col, row))->item;
+ const auto context = qmlContext(item.data());
+ const int contextIndex = context->contextProperty("index").toInt();
+ const int contextRow = context->contextProperty("row").toInt();
+ const int contextColumn = context->contextProperty("column").toInt();
+ const QObject *contextModelData = qvariant_cast<QObject *>(context->contextProperty("modelData"));
+ const QString modelDataProperty = contextModelData->property("someCustomProperty").toString();
+
+ QCOMPARE(contextIndex, row + (col * rowCount));
+ QCOMPARE(contextRow, row);
+ QCOMPARE(contextColumn, col);
+ QCOMPARE(modelDataProperty, QStringLiteral("%1").arg(row));
+ }
+ }
+}
+
QTEST_MAIN(tst_QQuickTableView)
#include "tst_qquicktableview.moc"