aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quick/items/qquicktableview.cpp113
-rw-r--r--src/quick/items/qquicktableview_p_p.h20
-rw-r--r--tests/auto/quick/qquicktableview/tst_qquicktableview.cpp42
3 files changed, 134 insertions, 41 deletions
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp
index fd8af19805..8dce0149f6 100644
--- a/src/quick/items/qquicktableview.cpp
+++ b/src/quick/items/qquicktableview.cpp
@@ -805,7 +805,7 @@ void QQuickTableViewPrivate::cancelLoadRequest()
loadRequest.markAsDone();
model->cancel(modelIndexAtCell(loadRequest.currentCell()));
- if (tableInvalid) {
+ if (rebuildState == RebuildState::NotStarted) {
// No reason to rollback already loaded edge items
// since we anyway are about to reload all items.
return;
@@ -857,13 +857,77 @@ void QQuickTableViewPrivate::processLoadRequest()
qCDebug(lcTableViewDelegateLifecycle()) << "request completed! Table:" << tableLayoutToString();
}
+void QQuickTableViewPrivate::processRebuildTable()
+{
+ moveToNextRebuildState();
+
+ if (rebuildState == RebuildState::LoadInitalTable) {
+ beginRebuildTable();
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ if (rebuildState == RebuildState::VerifyTable) {
+ if (loadedItems.isEmpty()) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "no items loaded, meaning empty model or no delegate";
+ rebuildState = RebuildState::Done;
+ return;
+ }
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ if (rebuildState == RebuildState::LayoutTable) {
+ layoutAfterLoadingInitialTable();
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ if (rebuildState == RebuildState::LoadAndUnloadAfterLayout) {
+ loadAndUnloadVisibleEdges();
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ if (rebuildState == RebuildState::PreloadColumns) {
+ if (loadedTable.right() < tableSize.width() - 1)
+ loadEdge(Qt::RightEdge, QQmlIncubator::AsynchronousIfNested);
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ if (rebuildState == RebuildState::PreloadRows) {
+ if (loadedTable.bottom() < tableSize.height() - 1)
+ loadEdge(Qt::BottomEdge, QQmlIncubator::AsynchronousIfNested);
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ if (rebuildState == RebuildState::MovePreloadedItemsToPool) {
+ while (Qt::Edge edge = nextEdgeToUnload(viewportRect))
+ unloadEdge(edge);
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ Q_TABLEVIEW_ASSERT(rebuildState == RebuildState::Done, int(rebuildState));
+}
+
+bool QQuickTableViewPrivate::moveToNextRebuildState()
+{
+ if (loadRequest.isActive()) {
+ // Items are still loading async, which means
+ // that the current state is not yet done.
+ return false;
+ }
+ rebuildState = RebuildState(int(rebuildState) + 1);
+ qCDebug(lcTableViewDelegateLifecycle()) << int(rebuildState);
+ return true;
+}
+
void QQuickTableViewPrivate::beginRebuildTable()
{
Q_Q(QQuickTableView);
- qCDebug(lcTableViewDelegateLifecycle());
-
- tableInvalid = false;
- tableRebuilding = true;
releaseLoadedItems();
loadedTable = QRect();
@@ -879,21 +943,16 @@ void QQuickTableViewPrivate::beginRebuildTable()
loadAndUnloadVisibleEdges();
}
-void QQuickTableViewPrivate::endRebuildTable()
+void QQuickTableViewPrivate::layoutAfterLoadingInitialTable()
{
- tableRebuilding = false;
-
- if (rowHeightProvider.isNull() && columnWidthProvider.isNull()) {
- // Since we have no size providers, we need to calculate the size
- // of each row and column based on the size of the delegate items.
+ if (rowHeightProvider.isNull() || columnWidthProvider.isNull()) {
+ // Since we don't have both size providers, we need to calculate the
+ // size of each row and column based on the size of the delegate items.
// This couldn't be done while we were loading the initial rows and
// columns, since during the process, we didn't have all the items
- // available yet for the calculation. So we mark that it needs to be
- // done now, from within updatePolish().
- columnRowPositionsInvalid = true;
+ // available yet for the calculation. So we do it now.
+ relayoutTable();
}
-
- qCDebug(lcTableViewDelegateLifecycle()) << tableLayoutToString();
}
void QQuickTableViewPrivate::loadInitialTopLeftItem()
@@ -1020,7 +1079,7 @@ void QQuickTableViewPrivate::drainReusePoolAfterLoadRequest()
}
void QQuickTableViewPrivate::invalidateTable() {
- tableInvalid = true;
+ rebuildState = RebuildState::NotStarted;
if (loadRequest.isActive())
cancelLoadRequest();
q_func()->polish();
@@ -1056,19 +1115,13 @@ void QQuickTableViewPrivate::updatePolish()
if (!viewportRect.isValid())
return;
- if (tableInvalid) {
- beginRebuildTable();
- if (loadRequest.isActive())
- return;
+ if (rebuildState != RebuildState::Done) {
+ processRebuildTable();
+ return;
}
- if (tableRebuilding)
- endRebuildTable();
-
- if (loadedItems.isEmpty()) {
- qCDebug(lcTableViewDelegateLifecycle()) << "no items loaded, meaning empty model or no delegate";
+ if (loadedItems.isEmpty())
return;
- }
if (columnRowPositionsInvalid)
relayoutTable();
@@ -1499,7 +1552,8 @@ qreal QQuickTableView::explicitContentWidth() const
{
Q_D(const QQuickTableView);
- if (d->tableInvalid && d->explicitContentWidth.isNull) {
+ if (d->rebuildState == QQuickTableViewPrivate::RebuildState::NotStarted
+ && d->explicitContentWidth.isNull) {
// The table is pending to be rebuilt. Since we don't
// know the contentWidth before this is done, we do the
// rebuild now, instead of waiting for the polish event.
@@ -1523,7 +1577,8 @@ qreal QQuickTableView::explicitContentHeight() const
{
Q_D(const QQuickTableView);
- if (d->tableInvalid && d->explicitContentHeight.isNull) {
+ if (d->rebuildState == QQuickTableViewPrivate::RebuildState::NotStarted
+ && d->explicitContentHeight.isNull) {
// The table is pending to be rebuilt. Since we don't
// know the contentHeight before this is done, we do the
// rebuild now, instead of waiting for the polish event.
diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h
index 936d8b8207..53fd936195 100644
--- a/src/quick/items/qquicktableview_p_p.h
+++ b/src/quick/items/qquicktableview_p_p.h
@@ -163,6 +163,18 @@ public:
}
};
+ enum class RebuildState {
+ NotStarted = 0,
+ LoadInitalTable,
+ VerifyTable,
+ LayoutTable,
+ LoadAndUnloadAfterLayout,
+ PreloadColumns,
+ PreloadRows,
+ MovePreloadedItemsToPool,
+ Done
+ };
+
public:
QQuickTableViewPrivate();
~QQuickTableViewPrivate() override;
@@ -196,6 +208,7 @@ public:
QSize tableSize;
+ RebuildState rebuildState = RebuildState::NotStarted;
TableEdgeLoadRequest loadRequest;
QPoint contentSizeBenchMarkPoint = QPoint(-1, -1);
@@ -205,8 +218,6 @@ public:
QQmlTableInstanceModel::ReusableFlag reusableFlag = QQmlTableInstanceModel::Reusable;
bool blockItemCreatedCallback = false;
- bool tableInvalid = false;
- bool tableRebuilding = false;
bool columnRowPositionsInvalid = false;
bool layoutWarningIssued = false;
bool polishing = false;
@@ -289,8 +300,11 @@ public:
void drainReusePoolAfterLoadRequest();
void cancelLoadRequest();
void processLoadRequest();
+
+ void processRebuildTable();
+ bool moveToNextRebuildState();
void beginRebuildTable();
- void endRebuildTable();
+ void layoutAfterLoadingInitialTable();
void invalidateTable();
void invalidateColumnRowPositions();
diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
index 69bed8be43..09c2850247 100644
--- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
+++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
@@ -85,6 +85,7 @@ private slots:
void setAndGetModel();
void emptyModel_data();
void emptyModel();
+ void checkPreload();
void checkZeroSizedDelegate();
void checkImplicitSizeDelegate();
void checkColumnWidthWithoutProvider();
@@ -191,6 +192,22 @@ void tst_QQuickTableView::emptyModel()
QCOMPARE(tableViewPrivate->loadedItems.count(), 0);
}
+void tst_QQuickTableView::checkPreload()
+{
+ // Check that the reuse pool is filled up with one extra row and
+ // column (pluss corner) after rebuilding the table.
+ LOAD_TABLEVIEW("plaintableview.qml");
+
+ auto model = TestModelAsVariant(100, 100);
+ tableView->setModel(model);
+
+ WAIT_UNTIL_POLISHED;
+
+ QSize visibleTableSize = tableViewPrivate->loadedTable.size();
+ int expectedPoolSize = visibleTableSize.height() + visibleTableSize.width() + 1;
+ QCOMPARE(tableViewPrivate->tableModel->poolSize(), expectedPoolSize);
+}
+
void tst_QQuickTableView::checkZeroSizedDelegate()
{
// Check that if we assign a delegate with empty width and height, we
@@ -1127,9 +1144,12 @@ void tst_QQuickTableView::checkRowColumnCount()
WAIT_UNTIL_POLISHED;
- const int tableViewCount = tableViewPrivate->loadedItems.count();
+ // We expect that the number of created items after start-up should match
+ //the size of the visible table, pluss one extra preloaded row and column.
+ const QSize visibleTableSize = tableViewPrivate->loadedTable.size();
const int qmlCountAfterInit = view->rootObject()->property(maxDelegateCountProp).toInt();
- QCOMPARE(tableViewCount, qmlCountAfterInit);
+ const int expectedCount = (visibleTableSize.width() + 1) * (visibleTableSize.height() + 1);
+ QCOMPARE(qmlCountAfterInit, expectedCount);
// This test will keep track of the maximum number of delegate items TableView
// had to show at any point while flicking (in countingtableview.qml). Because
@@ -1304,6 +1324,7 @@ void tst_QQuickTableView::checkIfDelegatesAreReused()
LOAD_TABLEVIEW("countingtableview.qml");
const qreal delegateWidth = 100;
+ const qreal delegateHeight = 50;
const int pageFlickCount = 4;
auto model = TestModelAsVariant(100, 100);
@@ -1312,13 +1333,20 @@ void tst_QQuickTableView::checkIfDelegatesAreReused()
WAIT_UNTIL_POLISHED;
- // Flick half an item to the left, to force one extra column to load before we start.
+ // Flick half an item to the side, to force one extra row and 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();
+ tableView->setContentY(delegateHeight / 2);
+ QCOMPARE(tableViewPrivate->tableModel->poolSize(), 0);
- WAIT_UNTIL_POLISHED;
+ // Some items have already been pooled and reused after we moved the content view, because
+ // we preload one extra row and column at start-up. So reset the count-properties back to 0
+ // before we continue.
+ for (auto fxItem : tableViewPrivate->loadedItems) {
+ fxItem->item->setProperty("pooledCount", 0);
+ fxItem->item->setProperty("reusedCount", 0);
+ }
const int visibleColumnCount = tableViewPrivate->loadedTable.width();
const int visibleRowCount = tableViewPrivate->loadedTable.height();
@@ -1327,10 +1355,6 @@ void tst_QQuickTableView::checkIfDelegatesAreReused()
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);