aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items
diff options
context:
space:
mode:
authorRichard Moe Gustavsen <richard.gustavsen@qt.io>2018-07-20 14:05:09 +0200
committerSimon Hausmann <simon.hausmann@qt.io>2018-08-02 09:50:35 +0000
commit382812cd2917180a160917d24b11825a7eeb2cf1 (patch)
treedac6fbf8260e90e0347e23b5f863128d9689607c /src/quick/items
parentca1f69757421845f563ebe90a5880509839efb31 (diff)
QQuickTableView: implement support for reusing delegate items
This patch will make use of the recent changes in QQmlTableInstanceModel to support reusing delegate items. The API in TableView to enable this will mainly be a new property "reuseItems". This property is true by default. By setting it to false, reusing items will never happen. When an item is reused, the signal "TableView.reused" is emitted after the fact, in case the delegate item needs to execute some extra code during the process. Likewise, a signal "TableView.pooled" is emitted when the item is pooled. From an implementation point of view, TableView only need to do two things to enable reusing of items. First, whenever it releases items, it provides a second argument to release(), informing QQmlTableInstanceModel if the item can be reused. Second, it needs to call drainReusePool() at appropriate times to ensure that no item will be kept alive in the pool for too long. Change-Id: I830e2eace776302ac58946733566208aa8954159 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
Diffstat (limited to 'src/quick/items')
-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
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();