diff options
Diffstat (limited to 'src/quick/items')
-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 |
3 files changed, 88 insertions, 9 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(); |