diff options
-rw-r--r-- | src/quick/items/qquicktableview.cpp | 84 | ||||
-rw-r--r-- | src/quick/items/qquicktableview_p.h | 7 | ||||
-rw-r--r-- | src/quick/items/qquicktableview_p_p.h | 6 | ||||
-rw-r--r-- | tests/auto/quick/qquicktableview/data/countingtableview.qml | 14 | ||||
-rw-r--r-- | tests/auto/quick/qquicktableview/data/qqmllistpropertymodel.qml | 85 | ||||
-rw-r--r-- | tests/auto/quick/qquicktableview/tst_qquicktableview.cpp | 218 |
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" |