qtdeclarative/src/qmlmodels/qqmltableinstancemodel.cpp

487 lines
18 KiB
C++
Raw Normal View History

QQmlTableInstanceModel: add new TableView helper class This patch will add a new model class: QQmlTableInstanceModel. It will be used by QQuickTableView to communicate with the data model, instead of using QQmlDelegateModel. The reason we abandon QQmlDelegateModel for QQuickTableView, is because QQmlDelegateModel was written from the start to only support list models, not table models. And to work around this, we proxy table models as a list models, and combine all columns to form one big list. This as some disadvantages. First and foremost because QQmlDelegateModel is using QQmlListCompositor internally. QQmlListCompositor can combine many different list models into one big list model, and divide them into groups. Any change done to a list model will also be mirrored in the list compositor. The compositor will further create QQmlChangeSets describing such model changes, which will then be emitted by QQmlDelegateModel so that the view can transition in the same changes. This flow is especially bad when adding/removing a new row in a table. In that case, since the table looks like as a list, the list compositor will need to update its own internal structures for each item in the new row. So if you have 1000 columns, 1000 updates will be processed. Even worse, since the list compositor create QQmlChangeSets for each item in the row, the view will end up receiving 1000 signals about the change. Combine this with the fact that QQmlDelegateModel contains a lot of undocumented complex code for dealing with groups, which is not needed or used by TableView (or ListView for that sake *), adding a new QQmlTableInstanceModel that understands table models, is the right solution. Auto testing the new class will be done from QQuickTableView, once it takes it into use. * Note: The fact that TableView/ListView is using QQmlDelegateModel internally to communicate with the data model is a private implementation detail. The application will never have access to this model (and can therefore not create any groups on it etc). The application can, however, create its own DelegateModel and assigns it to TableView/ListView. But this is a different issue, and will continue to work as before. Change-Id: If1c35c27e75f236161df84ecb1d919aa3050f5a0 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-06-23 09:33:04 +00:00
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml 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$
**
****************************************************************************/
#include "qqmltableinstancemodel_p.h"
#include "qqmlabstractdelegatecomponent_p.h"
QQmlTableInstanceModel: add new TableView helper class This patch will add a new model class: QQmlTableInstanceModel. It will be used by QQuickTableView to communicate with the data model, instead of using QQmlDelegateModel. The reason we abandon QQmlDelegateModel for QQuickTableView, is because QQmlDelegateModel was written from the start to only support list models, not table models. And to work around this, we proxy table models as a list models, and combine all columns to form one big list. This as some disadvantages. First and foremost because QQmlDelegateModel is using QQmlListCompositor internally. QQmlListCompositor can combine many different list models into one big list model, and divide them into groups. Any change done to a list model will also be mirrored in the list compositor. The compositor will further create QQmlChangeSets describing such model changes, which will then be emitted by QQmlDelegateModel so that the view can transition in the same changes. This flow is especially bad when adding/removing a new row in a table. In that case, since the table looks like as a list, the list compositor will need to update its own internal structures for each item in the new row. So if you have 1000 columns, 1000 updates will be processed. Even worse, since the list compositor create QQmlChangeSets for each item in the row, the view will end up receiving 1000 signals about the change. Combine this with the fact that QQmlDelegateModel contains a lot of undocumented complex code for dealing with groups, which is not needed or used by TableView (or ListView for that sake *), adding a new QQmlTableInstanceModel that understands table models, is the right solution. Auto testing the new class will be done from QQuickTableView, once it takes it into use. * Note: The fact that TableView/ListView is using QQmlDelegateModel internally to communicate with the data model is a private implementation detail. The application will never have access to this model (and can therefore not create any groups on it etc). The application can, however, create its own DelegateModel and assigns it to TableView/ListView. But this is a different issue, and will continue to work as before. Change-Id: If1c35c27e75f236161df84ecb1d919aa3050f5a0 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-06-23 09:33:04 +00:00
#include <QtCore/QTimer>
#include <QtQml/private/qqmlincubator_p.h>
#include <QtQmlModels/private/qqmlchangeset_p.h>
QQmlTableInstanceModel: add new TableView helper class This patch will add a new model class: QQmlTableInstanceModel. It will be used by QQuickTableView to communicate with the data model, instead of using QQmlDelegateModel. The reason we abandon QQmlDelegateModel for QQuickTableView, is because QQmlDelegateModel was written from the start to only support list models, not table models. And to work around this, we proxy table models as a list models, and combine all columns to form one big list. This as some disadvantages. First and foremost because QQmlDelegateModel is using QQmlListCompositor internally. QQmlListCompositor can combine many different list models into one big list model, and divide them into groups. Any change done to a list model will also be mirrored in the list compositor. The compositor will further create QQmlChangeSets describing such model changes, which will then be emitted by QQmlDelegateModel so that the view can transition in the same changes. This flow is especially bad when adding/removing a new row in a table. In that case, since the table looks like as a list, the list compositor will need to update its own internal structures for each item in the new row. So if you have 1000 columns, 1000 updates will be processed. Even worse, since the list compositor create QQmlChangeSets for each item in the row, the view will end up receiving 1000 signals about the change. Combine this with the fact that QQmlDelegateModel contains a lot of undocumented complex code for dealing with groups, which is not needed or used by TableView (or ListView for that sake *), adding a new QQmlTableInstanceModel that understands table models, is the right solution. Auto testing the new class will be done from QQuickTableView, once it takes it into use. * Note: The fact that TableView/ListView is using QQmlDelegateModel internally to communicate with the data model is a private implementation detail. The application will never have access to this model (and can therefore not create any groups on it etc). The application can, however, create its own DelegateModel and assigns it to TableView/ListView. But this is a different issue, and will continue to work as before. Change-Id: If1c35c27e75f236161df84ecb1d919aa3050f5a0 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-06-23 09:33:04 +00:00
#include <QtQml/private/qqmlcomponent_p.h>
QT_BEGIN_NAMESPACE
const char* kModelItemTag = "_tableinstancemodel_modelItem";
bool QQmlTableInstanceModel::isDoneIncubating(QQmlDelegateModelItem *modelItem)
{
if (!modelItem->incubationTask)
return true;
const auto status = modelItem->incubationTask->status();
return (status == QQmlIncubator::Ready) || (status == QQmlIncubator::Error);
}
void QQmlTableInstanceModel::deleteModelItemLater(QQmlDelegateModelItem *modelItem)
{
Q_ASSERT(modelItem);
delete modelItem->object;
modelItem->object = nullptr;
if (modelItem->contextData) {
modelItem->contextData->invalidate();
Q_ASSERT(modelItem->contextData->refCount == 1);
modelItem->contextData = nullptr;
}
modelItem->deleteLater();
}
QQmlTableInstanceModel::QQmlTableInstanceModel(QQmlContext *qmlContext, QObject *parent)
: QQmlInstanceModel(*(new QObjectPrivate()), parent)
, m_qmlContext(qmlContext)
, m_metaType(new QQmlDelegateModelItemMetaType(m_qmlContext->engine()->handle(), nullptr, QStringList()),
QQmlRefPointer<QQmlDelegateModelItemMetaType>::Adopt)
QQmlTableInstanceModel: add new TableView helper class This patch will add a new model class: QQmlTableInstanceModel. It will be used by QQuickTableView to communicate with the data model, instead of using QQmlDelegateModel. The reason we abandon QQmlDelegateModel for QQuickTableView, is because QQmlDelegateModel was written from the start to only support list models, not table models. And to work around this, we proxy table models as a list models, and combine all columns to form one big list. This as some disadvantages. First and foremost because QQmlDelegateModel is using QQmlListCompositor internally. QQmlListCompositor can combine many different list models into one big list model, and divide them into groups. Any change done to a list model will also be mirrored in the list compositor. The compositor will further create QQmlChangeSets describing such model changes, which will then be emitted by QQmlDelegateModel so that the view can transition in the same changes. This flow is especially bad when adding/removing a new row in a table. In that case, since the table looks like as a list, the list compositor will need to update its own internal structures for each item in the new row. So if you have 1000 columns, 1000 updates will be processed. Even worse, since the list compositor create QQmlChangeSets for each item in the row, the view will end up receiving 1000 signals about the change. Combine this with the fact that QQmlDelegateModel contains a lot of undocumented complex code for dealing with groups, which is not needed or used by TableView (or ListView for that sake *), adding a new QQmlTableInstanceModel that understands table models, is the right solution. Auto testing the new class will be done from QQuickTableView, once it takes it into use. * Note: The fact that TableView/ListView is using QQmlDelegateModel internally to communicate with the data model is a private implementation detail. The application will never have access to this model (and can therefore not create any groups on it etc). The application can, however, create its own DelegateModel and assigns it to TableView/ListView. But this is a different issue, and will continue to work as before. Change-Id: If1c35c27e75f236161df84ecb1d919aa3050f5a0 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-06-23 09:33:04 +00:00
{
}
void QQmlTableInstanceModel::useImportVersion(int minorVersion)
{
m_adaptorModel.useImportVersion(minorVersion);
}
QQmlTableInstanceModel: add new TableView helper class This patch will add a new model class: QQmlTableInstanceModel. It will be used by QQuickTableView to communicate with the data model, instead of using QQmlDelegateModel. The reason we abandon QQmlDelegateModel for QQuickTableView, is because QQmlDelegateModel was written from the start to only support list models, not table models. And to work around this, we proxy table models as a list models, and combine all columns to form one big list. This as some disadvantages. First and foremost because QQmlDelegateModel is using QQmlListCompositor internally. QQmlListCompositor can combine many different list models into one big list model, and divide them into groups. Any change done to a list model will also be mirrored in the list compositor. The compositor will further create QQmlChangeSets describing such model changes, which will then be emitted by QQmlDelegateModel so that the view can transition in the same changes. This flow is especially bad when adding/removing a new row in a table. In that case, since the table looks like as a list, the list compositor will need to update its own internal structures for each item in the new row. So if you have 1000 columns, 1000 updates will be processed. Even worse, since the list compositor create QQmlChangeSets for each item in the row, the view will end up receiving 1000 signals about the change. Combine this with the fact that QQmlDelegateModel contains a lot of undocumented complex code for dealing with groups, which is not needed or used by TableView (or ListView for that sake *), adding a new QQmlTableInstanceModel that understands table models, is the right solution. Auto testing the new class will be done from QQuickTableView, once it takes it into use. * Note: The fact that TableView/ListView is using QQmlDelegateModel internally to communicate with the data model is a private implementation detail. The application will never have access to this model (and can therefore not create any groups on it etc). The application can, however, create its own DelegateModel and assigns it to TableView/ListView. But this is a different issue, and will continue to work as before. Change-Id: If1c35c27e75f236161df84ecb1d919aa3050f5a0 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-06-23 09:33:04 +00:00
QQmlTableInstanceModel::~QQmlTableInstanceModel()
{
for (const auto modelItem : m_modelItems) {
// No item in m_modelItems should be referenced at this point. The view
// should release all its items before it deletes this model. Only model items
// that are still being incubated should be left for us to delete.
Q_ASSERT(modelItem->objectRef == 0);
Q_ASSERT(modelItem->incubationTask);
// Check that we are not being deleted while we're
// in the process of e.g emitting a created signal.
Q_ASSERT(modelItem->scriptRef == 0);
if (modelItem->object) {
delete modelItem->object;
modelItem->object = nullptr;
modelItem->contextData->invalidate();
modelItem->contextData = nullptr;
}
}
QQmlTableInstanceModel: implement support for reusing delegate items This patch will make it possible for TableView to reuse delegate items. The API lets TableView (or any kind of view in theory) specify if an item is reusable at the time the item is released. Reusing delegate items is specified per item, meaning that the view can choose to release some items as reusable, but others not. If the view never releases any items as reusable, no items will be added to the pool, and hence, no items will be reused. If the view releases an item as reusable, the model will move it to the reuse pool rather than destroying it (if the items ref-count is zero, and not persisted). The next time the view asks the model for a new item, the model will first check if the pool already contains an item, and if so, use it. Otherwise it will create a new item, like before. Items in the pool are supposed to rest there for a short while. Ideally only from the time items are flicked out on one side of the view, until we reuse them when new items are flicked in on the oppsite side. One big reason for this is that we have no way of hibernating items in QML, so they will effectively stay alive while inside the pool. This is not ideal, but still a huge performance improvement over what we had before, where all items are always created from scratch. If hibernating objects will ever be possible, it should be easy to extends the current logic to take advantage of this. Already existing tests for QQuickTableView should verify that no regressions are introduced with this patch. Since recycling of items is driven from the view, auto tests for this functionality is included with the patch for implementing reuse support in TableView (coming in a subsequent patch). Change-Id: I28aa687251ce3e7e1de0b1c799fdbf44d8867d45 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-07-19 11:06:19 +00:00
deleteAllFinishedIncubationTasks();
qDeleteAll(m_modelItems);
QQmlTableInstanceModel: implement support for reusing delegate items This patch will make it possible for TableView to reuse delegate items. The API lets TableView (or any kind of view in theory) specify if an item is reusable at the time the item is released. Reusing delegate items is specified per item, meaning that the view can choose to release some items as reusable, but others not. If the view never releases any items as reusable, no items will be added to the pool, and hence, no items will be reused. If the view releases an item as reusable, the model will move it to the reuse pool rather than destroying it (if the items ref-count is zero, and not persisted). The next time the view asks the model for a new item, the model will first check if the pool already contains an item, and if so, use it. Otherwise it will create a new item, like before. Items in the pool are supposed to rest there for a short while. Ideally only from the time items are flicked out on one side of the view, until we reuse them when new items are flicked in on the oppsite side. One big reason for this is that we have no way of hibernating items in QML, so they will effectively stay alive while inside the pool. This is not ideal, but still a huge performance improvement over what we had before, where all items are always created from scratch. If hibernating objects will ever be possible, it should be easy to extends the current logic to take advantage of this. Already existing tests for QQuickTableView should verify that no regressions are introduced with this patch. Since recycling of items is driven from the view, auto tests for this functionality is included with the patch for implementing reuse support in TableView (coming in a subsequent patch). Change-Id: I28aa687251ce3e7e1de0b1c799fdbf44d8867d45 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-07-19 11:06:19 +00:00
drainReusableItemsPool(0);
}
QQmlComponent *QQmlTableInstanceModel::resolveDelegate(int index)
{
if (m_delegateChooser) {
const int row = m_adaptorModel.rowAt(index);
const int column = m_adaptorModel.columnAt(index);
QQmlComponent *delegate = nullptr;
QQmlAbstractDelegateComponent *chooser = m_delegateChooser;
do {
delegate = chooser->delegate(&m_adaptorModel, row, column);
chooser = qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
} while (chooser);
return delegate;
}
return m_delegate;
}
QQmlTableInstanceModel: implement support for reusing delegate items This patch will make it possible for TableView to reuse delegate items. The API lets TableView (or any kind of view in theory) specify if an item is reusable at the time the item is released. Reusing delegate items is specified per item, meaning that the view can choose to release some items as reusable, but others not. If the view never releases any items as reusable, no items will be added to the pool, and hence, no items will be reused. If the view releases an item as reusable, the model will move it to the reuse pool rather than destroying it (if the items ref-count is zero, and not persisted). The next time the view asks the model for a new item, the model will first check if the pool already contains an item, and if so, use it. Otherwise it will create a new item, like before. Items in the pool are supposed to rest there for a short while. Ideally only from the time items are flicked out on one side of the view, until we reuse them when new items are flicked in on the oppsite side. One big reason for this is that we have no way of hibernating items in QML, so they will effectively stay alive while inside the pool. This is not ideal, but still a huge performance improvement over what we had before, where all items are always created from scratch. If hibernating objects will ever be possible, it should be easy to extends the current logic to take advantage of this. Already existing tests for QQuickTableView should verify that no regressions are introduced with this patch. Since recycling of items is driven from the view, auto tests for this functionality is included with the patch for implementing reuse support in TableView (coming in a subsequent patch). Change-Id: I28aa687251ce3e7e1de0b1c799fdbf44d8867d45 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-07-19 11:06:19 +00:00
QQmlDelegateModelItem *QQmlTableInstanceModel::resolveModelItem(int index)
{
// Check if an item for the given index is already loaded and ready
QQmlDelegateModelItem *modelItem = m_modelItems.value(index, nullptr);
if (modelItem)
return modelItem;
QQmlComponent *delegate = resolveDelegate(index);
if (!delegate)
return nullptr;
QQmlTableInstanceModel: implement support for reusing delegate items This patch will make it possible for TableView to reuse delegate items. The API lets TableView (or any kind of view in theory) specify if an item is reusable at the time the item is released. Reusing delegate items is specified per item, meaning that the view can choose to release some items as reusable, but others not. If the view never releases any items as reusable, no items will be added to the pool, and hence, no items will be reused. If the view releases an item as reusable, the model will move it to the reuse pool rather than destroying it (if the items ref-count is zero, and not persisted). The next time the view asks the model for a new item, the model will first check if the pool already contains an item, and if so, use it. Otherwise it will create a new item, like before. Items in the pool are supposed to rest there for a short while. Ideally only from the time items are flicked out on one side of the view, until we reuse them when new items are flicked in on the oppsite side. One big reason for this is that we have no way of hibernating items in QML, so they will effectively stay alive while inside the pool. This is not ideal, but still a huge performance improvement over what we had before, where all items are always created from scratch. If hibernating objects will ever be possible, it should be easy to extends the current logic to take advantage of this. Already existing tests for QQuickTableView should verify that no regressions are introduced with this patch. Since recycling of items is driven from the view, auto tests for this functionality is included with the patch for implementing reuse support in TableView (coming in a subsequent patch). Change-Id: I28aa687251ce3e7e1de0b1c799fdbf44d8867d45 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-07-19 11:06:19 +00:00
// Check if the pool contains an item that can be reused
modelItem = m_reusableItemsPool.takeItem(delegate, index);
QQmlTableInstanceModel: implement support for reusing delegate items This patch will make it possible for TableView to reuse delegate items. The API lets TableView (or any kind of view in theory) specify if an item is reusable at the time the item is released. Reusing delegate items is specified per item, meaning that the view can choose to release some items as reusable, but others not. If the view never releases any items as reusable, no items will be added to the pool, and hence, no items will be reused. If the view releases an item as reusable, the model will move it to the reuse pool rather than destroying it (if the items ref-count is zero, and not persisted). The next time the view asks the model for a new item, the model will first check if the pool already contains an item, and if so, use it. Otherwise it will create a new item, like before. Items in the pool are supposed to rest there for a short while. Ideally only from the time items are flicked out on one side of the view, until we reuse them when new items are flicked in on the oppsite side. One big reason for this is that we have no way of hibernating items in QML, so they will effectively stay alive while inside the pool. This is not ideal, but still a huge performance improvement over what we had before, where all items are always created from scratch. If hibernating objects will ever be possible, it should be easy to extends the current logic to take advantage of this. Already existing tests for QQuickTableView should verify that no regressions are introduced with this patch. Since recycling of items is driven from the view, auto tests for this functionality is included with the patch for implementing reuse support in TableView (coming in a subsequent patch). Change-Id: I28aa687251ce3e7e1de0b1c799fdbf44d8867d45 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-07-19 11:06:19 +00:00
if (modelItem) {
reuseItem(modelItem, index);
m_modelItems.insert(index, modelItem);
return modelItem;
}
// Create a new item from scratch
modelItem = m_adaptorModel.createItem(m_metaType, index);
if (modelItem) {
modelItem->delegate = delegate;
m_modelItems.insert(index, modelItem);
return modelItem;
}
qWarning() << Q_FUNC_INFO << "failed creating a model item for index: " << index;
return nullptr;
QQmlTableInstanceModel: add new TableView helper class This patch will add a new model class: QQmlTableInstanceModel. It will be used by QQuickTableView to communicate with the data model, instead of using QQmlDelegateModel. The reason we abandon QQmlDelegateModel for QQuickTableView, is because QQmlDelegateModel was written from the start to only support list models, not table models. And to work around this, we proxy table models as a list models, and combine all columns to form one big list. This as some disadvantages. First and foremost because QQmlDelegateModel is using QQmlListCompositor internally. QQmlListCompositor can combine many different list models into one big list model, and divide them into groups. Any change done to a list model will also be mirrored in the list compositor. The compositor will further create QQmlChangeSets describing such model changes, which will then be emitted by QQmlDelegateModel so that the view can transition in the same changes. This flow is especially bad when adding/removing a new row in a table. In that case, since the table looks like as a list, the list compositor will need to update its own internal structures for each item in the new row. So if you have 1000 columns, 1000 updates will be processed. Even worse, since the list compositor create QQmlChangeSets for each item in the row, the view will end up receiving 1000 signals about the change. Combine this with the fact that QQmlDelegateModel contains a lot of undocumented complex code for dealing with groups, which is not needed or used by TableView (or ListView for that sake *), adding a new QQmlTableInstanceModel that understands table models, is the right solution. Auto testing the new class will be done from QQuickTableView, once it takes it into use. * Note: The fact that TableView/ListView is using QQmlDelegateModel internally to communicate with the data model is a private implementation detail. The application will never have access to this model (and can therefore not create any groups on it etc). The application can, however, create its own DelegateModel and assigns it to TableView/ListView. But this is a different issue, and will continue to work as before. Change-Id: If1c35c27e75f236161df84ecb1d919aa3050f5a0 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-06-23 09:33:04 +00:00
}
QObject *QQmlTableInstanceModel::object(int index, QQmlIncubator::IncubationMode incubationMode)
{
Q_ASSERT(m_delegate);
Q_ASSERT(index >= 0 && index < m_adaptorModel.count());
Q_ASSERT(m_qmlContext && m_qmlContext->isValid());
QQmlTableInstanceModel: implement support for reusing delegate items This patch will make it possible for TableView to reuse delegate items. The API lets TableView (or any kind of view in theory) specify if an item is reusable at the time the item is released. Reusing delegate items is specified per item, meaning that the view can choose to release some items as reusable, but others not. If the view never releases any items as reusable, no items will be added to the pool, and hence, no items will be reused. If the view releases an item as reusable, the model will move it to the reuse pool rather than destroying it (if the items ref-count is zero, and not persisted). The next time the view asks the model for a new item, the model will first check if the pool already contains an item, and if so, use it. Otherwise it will create a new item, like before. Items in the pool are supposed to rest there for a short while. Ideally only from the time items are flicked out on one side of the view, until we reuse them when new items are flicked in on the oppsite side. One big reason for this is that we have no way of hibernating items in QML, so they will effectively stay alive while inside the pool. This is not ideal, but still a huge performance improvement over what we had before, where all items are always created from scratch. If hibernating objects will ever be possible, it should be easy to extends the current logic to take advantage of this. Already existing tests for QQuickTableView should verify that no regressions are introduced with this patch. Since recycling of items is driven from the view, auto tests for this functionality is included with the patch for implementing reuse support in TableView (coming in a subsequent patch). Change-Id: I28aa687251ce3e7e1de0b1c799fdbf44d8867d45 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-07-19 11:06:19 +00:00
QQmlDelegateModelItem *modelItem = resolveModelItem(index);
if (!modelItem)
return nullptr;
QQmlTableInstanceModel: add new TableView helper class This patch will add a new model class: QQmlTableInstanceModel. It will be used by QQuickTableView to communicate with the data model, instead of using QQmlDelegateModel. The reason we abandon QQmlDelegateModel for QQuickTableView, is because QQmlDelegateModel was written from the start to only support list models, not table models. And to work around this, we proxy table models as a list models, and combine all columns to form one big list. This as some disadvantages. First and foremost because QQmlDelegateModel is using QQmlListCompositor internally. QQmlListCompositor can combine many different list models into one big list model, and divide them into groups. Any change done to a list model will also be mirrored in the list compositor. The compositor will further create QQmlChangeSets describing such model changes, which will then be emitted by QQmlDelegateModel so that the view can transition in the same changes. This flow is especially bad when adding/removing a new row in a table. In that case, since the table looks like as a list, the list compositor will need to update its own internal structures for each item in the new row. So if you have 1000 columns, 1000 updates will be processed. Even worse, since the list compositor create QQmlChangeSets for each item in the row, the view will end up receiving 1000 signals about the change. Combine this with the fact that QQmlDelegateModel contains a lot of undocumented complex code for dealing with groups, which is not needed or used by TableView (or ListView for that sake *), adding a new QQmlTableInstanceModel that understands table models, is the right solution. Auto testing the new class will be done from QQuickTableView, once it takes it into use. * Note: The fact that TableView/ListView is using QQmlDelegateModel internally to communicate with the data model is a private implementation detail. The application will never have access to this model (and can therefore not create any groups on it etc). The application can, however, create its own DelegateModel and assigns it to TableView/ListView. But this is a different issue, and will continue to work as before. Change-Id: If1c35c27e75f236161df84ecb1d919aa3050f5a0 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-06-23 09:33:04 +00:00
if (modelItem->object) {
// The model item has already been incubated. So
// just bump the ref-count and return it.
modelItem->referenceObject();
return modelItem->object;
}
QQmlTableInstanceModel: implement support for reusing delegate items This patch will make it possible for TableView to reuse delegate items. The API lets TableView (or any kind of view in theory) specify if an item is reusable at the time the item is released. Reusing delegate items is specified per item, meaning that the view can choose to release some items as reusable, but others not. If the view never releases any items as reusable, no items will be added to the pool, and hence, no items will be reused. If the view releases an item as reusable, the model will move it to the reuse pool rather than destroying it (if the items ref-count is zero, and not persisted). The next time the view asks the model for a new item, the model will first check if the pool already contains an item, and if so, use it. Otherwise it will create a new item, like before. Items in the pool are supposed to rest there for a short while. Ideally only from the time items are flicked out on one side of the view, until we reuse them when new items are flicked in on the oppsite side. One big reason for this is that we have no way of hibernating items in QML, so they will effectively stay alive while inside the pool. This is not ideal, but still a huge performance improvement over what we had before, where all items are always created from scratch. If hibernating objects will ever be possible, it should be easy to extends the current logic to take advantage of this. Already existing tests for QQuickTableView should verify that no regressions are introduced with this patch. Since recycling of items is driven from the view, auto tests for this functionality is included with the patch for implementing reuse support in TableView (coming in a subsequent patch). Change-Id: I28aa687251ce3e7e1de0b1c799fdbf44d8867d45 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-07-19 11:06:19 +00:00
// The object is not ready, and needs to be incubated
incubateModelItem(modelItem, incubationMode);
QQmlTableInstanceModel: add new TableView helper class This patch will add a new model class: QQmlTableInstanceModel. It will be used by QQuickTableView to communicate with the data model, instead of using QQmlDelegateModel. The reason we abandon QQmlDelegateModel for QQuickTableView, is because QQmlDelegateModel was written from the start to only support list models, not table models. And to work around this, we proxy table models as a list models, and combine all columns to form one big list. This as some disadvantages. First and foremost because QQmlDelegateModel is using QQmlListCompositor internally. QQmlListCompositor can combine many different list models into one big list model, and divide them into groups. Any change done to a list model will also be mirrored in the list compositor. The compositor will further create QQmlChangeSets describing such model changes, which will then be emitted by QQmlDelegateModel so that the view can transition in the same changes. This flow is especially bad when adding/removing a new row in a table. In that case, since the table looks like as a list, the list compositor will need to update its own internal structures for each item in the new row. So if you have 1000 columns, 1000 updates will be processed. Even worse, since the list compositor create QQmlChangeSets for each item in the row, the view will end up receiving 1000 signals about the change. Combine this with the fact that QQmlDelegateModel contains a lot of undocumented complex code for dealing with groups, which is not needed or used by TableView (or ListView for that sake *), adding a new QQmlTableInstanceModel that understands table models, is the right solution. Auto testing the new class will be done from QQuickTableView, once it takes it into use. * Note: The fact that TableView/ListView is using QQmlDelegateModel internally to communicate with the data model is a private implementation detail. The application will never have access to this model (and can therefore not create any groups on it etc). The application can, however, create its own DelegateModel and assigns it to TableView/ListView. But this is a different issue, and will continue to work as before. Change-Id: If1c35c27e75f236161df84ecb1d919aa3050f5a0 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-06-23 09:33:04 +00:00
if (!isDoneIncubating(modelItem))
return nullptr;
QQmlTableInstanceModel: implement support for reusing delegate items This patch will make it possible for TableView to reuse delegate items. The API lets TableView (or any kind of view in theory) specify if an item is reusable at the time the item is released. Reusing delegate items is specified per item, meaning that the view can choose to release some items as reusable, but others not. If the view never releases any items as reusable, no items will be added to the pool, and hence, no items will be reused. If the view releases an item as reusable, the model will move it to the reuse pool rather than destroying it (if the items ref-count is zero, and not persisted). The next time the view asks the model for a new item, the model will first check if the pool already contains an item, and if so, use it. Otherwise it will create a new item, like before. Items in the pool are supposed to rest there for a short while. Ideally only from the time items are flicked out on one side of the view, until we reuse them when new items are flicked in on the oppsite side. One big reason for this is that we have no way of hibernating items in QML, so they will effectively stay alive while inside the pool. This is not ideal, but still a huge performance improvement over what we had before, where all items are always created from scratch. If hibernating objects will ever be possible, it should be easy to extends the current logic to take advantage of this. Already existing tests for QQuickTableView should verify that no regressions are introduced with this patch. Since recycling of items is driven from the view, auto tests for this functionality is included with the patch for implementing reuse support in TableView (coming in a subsequent patch). Change-Id: I28aa687251ce3e7e1de0b1c799fdbf44d8867d45 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-07-19 11:06:19 +00:00
// Incubation is done, so the task should be removed
QQmlTableInstanceModel: add new TableView helper class This patch will add a new model class: QQmlTableInstanceModel. It will be used by QQuickTableView to communicate with the data model, instead of using QQmlDelegateModel. The reason we abandon QQmlDelegateModel for QQuickTableView, is because QQmlDelegateModel was written from the start to only support list models, not table models. And to work around this, we proxy table models as a list models, and combine all columns to form one big list. This as some disadvantages. First and foremost because QQmlDelegateModel is using QQmlListCompositor internally. QQmlListCompositor can combine many different list models into one big list model, and divide them into groups. Any change done to a list model will also be mirrored in the list compositor. The compositor will further create QQmlChangeSets describing such model changes, which will then be emitted by QQmlDelegateModel so that the view can transition in the same changes. This flow is especially bad when adding/removing a new row in a table. In that case, since the table looks like as a list, the list compositor will need to update its own internal structures for each item in the new row. So if you have 1000 columns, 1000 updates will be processed. Even worse, since the list compositor create QQmlChangeSets for each item in the row, the view will end up receiving 1000 signals about the change. Combine this with the fact that QQmlDelegateModel contains a lot of undocumented complex code for dealing with groups, which is not needed or used by TableView (or ListView for that sake *), adding a new QQmlTableInstanceModel that understands table models, is the right solution. Auto testing the new class will be done from QQuickTableView, once it takes it into use. * Note: The fact that TableView/ListView is using QQmlDelegateModel internally to communicate with the data model is a private implementation detail. The application will never have access to this model (and can therefore not create any groups on it etc). The application can, however, create its own DelegateModel and assigns it to TableView/ListView. But this is a different issue, and will continue to work as before. Change-Id: If1c35c27e75f236161df84ecb1d919aa3050f5a0 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-06-23 09:33:04 +00:00
Q_ASSERT(!modelItem->incubationTask);
if (!modelItem->object) {
// The object was incubated synchronously (otherwise we would return above). But since
// we have no object, the incubation must have failed. And when we have no object, there
// should be no object references either. And there should also not be any internal script
// refs at this point. So we delete the model item.
Q_ASSERT(!modelItem->isObjectReferenced());
Q_ASSERT(!modelItem->isReferenced());
m_modelItems.remove(modelItem->index);
delete modelItem;
return nullptr;
}
// Incubation was completed sync and successful
modelItem->referenceObject();
return modelItem->object;
}
QQmlTableInstanceModel: implement support for reusing delegate items This patch will make it possible for TableView to reuse delegate items. The API lets TableView (or any kind of view in theory) specify if an item is reusable at the time the item is released. Reusing delegate items is specified per item, meaning that the view can choose to release some items as reusable, but others not. If the view never releases any items as reusable, no items will be added to the pool, and hence, no items will be reused. If the view releases an item as reusable, the model will move it to the reuse pool rather than destroying it (if the items ref-count is zero, and not persisted). The next time the view asks the model for a new item, the model will first check if the pool already contains an item, and if so, use it. Otherwise it will create a new item, like before. Items in the pool are supposed to rest there for a short while. Ideally only from the time items are flicked out on one side of the view, until we reuse them when new items are flicked in on the oppsite side. One big reason for this is that we have no way of hibernating items in QML, so they will effectively stay alive while inside the pool. This is not ideal, but still a huge performance improvement over what we had before, where all items are always created from scratch. If hibernating objects will ever be possible, it should be easy to extends the current logic to take advantage of this. Already existing tests for QQuickTableView should verify that no regressions are introduced with this patch. Since recycling of items is driven from the view, auto tests for this functionality is included with the patch for implementing reuse support in TableView (coming in a subsequent patch). Change-Id: I28aa687251ce3e7e1de0b1c799fdbf44d8867d45 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-07-19 11:06:19 +00:00
QQmlInstanceModel::ReleaseFlags QQmlTableInstanceModel::release(QObject *object, ReusableFlag reusable)
QQmlTableInstanceModel: add new TableView helper class This patch will add a new model class: QQmlTableInstanceModel. It will be used by QQuickTableView to communicate with the data model, instead of using QQmlDelegateModel. The reason we abandon QQmlDelegateModel for QQuickTableView, is because QQmlDelegateModel was written from the start to only support list models, not table models. And to work around this, we proxy table models as a list models, and combine all columns to form one big list. This as some disadvantages. First and foremost because QQmlDelegateModel is using QQmlListCompositor internally. QQmlListCompositor can combine many different list models into one big list model, and divide them into groups. Any change done to a list model will also be mirrored in the list compositor. The compositor will further create QQmlChangeSets describing such model changes, which will then be emitted by QQmlDelegateModel so that the view can transition in the same changes. This flow is especially bad when adding/removing a new row in a table. In that case, since the table looks like as a list, the list compositor will need to update its own internal structures for each item in the new row. So if you have 1000 columns, 1000 updates will be processed. Even worse, since the list compositor create QQmlChangeSets for each item in the row, the view will end up receiving 1000 signals about the change. Combine this with the fact that QQmlDelegateModel contains a lot of undocumented complex code for dealing with groups, which is not needed or used by TableView (or ListView for that sake *), adding a new QQmlTableInstanceModel that understands table models, is the right solution. Auto testing the new class will be done from QQuickTableView, once it takes it into use. * Note: The fact that TableView/ListView is using QQmlDelegateModel internally to communicate with the data model is a private implementation detail. The application will never have access to this model (and can therefore not create any groups on it etc). The application can, however, create its own DelegateModel and assigns it to TableView/ListView. But this is a different issue, and will continue to work as before. Change-Id: If1c35c27e75f236161df84ecb1d919aa3050f5a0 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-06-23 09:33:04 +00:00
{
Q_ASSERT(object);
auto modelItem = qvariant_cast<QQmlDelegateModelItem *>(object->property(kModelItemTag));
Q_ASSERT(modelItem);
if (!modelItem->releaseObject())
return QQmlDelegateModel::Referenced;
if (modelItem->isReferenced()) {
// We still have an internal reference to this object, which means that we are told to release an
// object while the createdItem signal for it is still on the stack. This can happen when objects
// are e.g delivered async, and the user flicks back and forth quicker than the loading can catch
// up with. The view might then find that the object is no longer visible and should be released.
// We detect this case in incubatorStatusChanged(), and delete it there instead. But from the callers
QQmlTableInstanceModel: implement support for reusing delegate items This patch will make it possible for TableView to reuse delegate items. The API lets TableView (or any kind of view in theory) specify if an item is reusable at the time the item is released. Reusing delegate items is specified per item, meaning that the view can choose to release some items as reusable, but others not. If the view never releases any items as reusable, no items will be added to the pool, and hence, no items will be reused. If the view releases an item as reusable, the model will move it to the reuse pool rather than destroying it (if the items ref-count is zero, and not persisted). The next time the view asks the model for a new item, the model will first check if the pool already contains an item, and if so, use it. Otherwise it will create a new item, like before. Items in the pool are supposed to rest there for a short while. Ideally only from the time items are flicked out on one side of the view, until we reuse them when new items are flicked in on the oppsite side. One big reason for this is that we have no way of hibernating items in QML, so they will effectively stay alive while inside the pool. This is not ideal, but still a huge performance improvement over what we had before, where all items are always created from scratch. If hibernating objects will ever be possible, it should be easy to extends the current logic to take advantage of this. Already existing tests for QQuickTableView should verify that no regressions are introduced with this patch. Since recycling of items is driven from the view, auto tests for this functionality is included with the patch for implementing reuse support in TableView (coming in a subsequent patch). Change-Id: I28aa687251ce3e7e1de0b1c799fdbf44d8867d45 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-07-19 11:06:19 +00:00
// point of view, it should consider it destroyed.
QQmlTableInstanceModel: add new TableView helper class This patch will add a new model class: QQmlTableInstanceModel. It will be used by QQuickTableView to communicate with the data model, instead of using QQmlDelegateModel. The reason we abandon QQmlDelegateModel for QQuickTableView, is because QQmlDelegateModel was written from the start to only support list models, not table models. And to work around this, we proxy table models as a list models, and combine all columns to form one big list. This as some disadvantages. First and foremost because QQmlDelegateModel is using QQmlListCompositor internally. QQmlListCompositor can combine many different list models into one big list model, and divide them into groups. Any change done to a list model will also be mirrored in the list compositor. The compositor will further create QQmlChangeSets describing such model changes, which will then be emitted by QQmlDelegateModel so that the view can transition in the same changes. This flow is especially bad when adding/removing a new row in a table. In that case, since the table looks like as a list, the list compositor will need to update its own internal structures for each item in the new row. So if you have 1000 columns, 1000 updates will be processed. Even worse, since the list compositor create QQmlChangeSets for each item in the row, the view will end up receiving 1000 signals about the change. Combine this with the fact that QQmlDelegateModel contains a lot of undocumented complex code for dealing with groups, which is not needed or used by TableView (or ListView for that sake *), adding a new QQmlTableInstanceModel that understands table models, is the right solution. Auto testing the new class will be done from QQuickTableView, once it takes it into use. * Note: The fact that TableView/ListView is using QQmlDelegateModel internally to communicate with the data model is a private implementation detail. The application will never have access to this model (and can therefore not create any groups on it etc). The application can, however, create its own DelegateModel and assigns it to TableView/ListView. But this is a different issue, and will continue to work as before. Change-Id: If1c35c27e75f236161df84ecb1d919aa3050f5a0 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-06-23 09:33:04 +00:00
return QQmlDelegateModel::Destroyed;
}
QQmlTableInstanceModel: implement support for reusing delegate items This patch will make it possible for TableView to reuse delegate items. The API lets TableView (or any kind of view in theory) specify if an item is reusable at the time the item is released. Reusing delegate items is specified per item, meaning that the view can choose to release some items as reusable, but others not. If the view never releases any items as reusable, no items will be added to the pool, and hence, no items will be reused. If the view releases an item as reusable, the model will move it to the reuse pool rather than destroying it (if the items ref-count is zero, and not persisted). The next time the view asks the model for a new item, the model will first check if the pool already contains an item, and if so, use it. Otherwise it will create a new item, like before. Items in the pool are supposed to rest there for a short while. Ideally only from the time items are flicked out on one side of the view, until we reuse them when new items are flicked in on the oppsite side. One big reason for this is that we have no way of hibernating items in QML, so they will effectively stay alive while inside the pool. This is not ideal, but still a huge performance improvement over what we had before, where all items are always created from scratch. If hibernating objects will ever be possible, it should be easy to extends the current logic to take advantage of this. Already existing tests for QQuickTableView should verify that no regressions are introduced with this patch. Since recycling of items is driven from the view, auto tests for this functionality is included with the patch for implementing reuse support in TableView (coming in a subsequent patch). Change-Id: I28aa687251ce3e7e1de0b1c799fdbf44d8867d45 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-07-19 11:06:19 +00:00
// The item is not referenced by anyone
QQmlTableInstanceModel: add new TableView helper class This patch will add a new model class: QQmlTableInstanceModel. It will be used by QQuickTableView to communicate with the data model, instead of using QQmlDelegateModel. The reason we abandon QQmlDelegateModel for QQuickTableView, is because QQmlDelegateModel was written from the start to only support list models, not table models. And to work around this, we proxy table models as a list models, and combine all columns to form one big list. This as some disadvantages. First and foremost because QQmlDelegateModel is using QQmlListCompositor internally. QQmlListCompositor can combine many different list models into one big list model, and divide them into groups. Any change done to a list model will also be mirrored in the list compositor. The compositor will further create QQmlChangeSets describing such model changes, which will then be emitted by QQmlDelegateModel so that the view can transition in the same changes. This flow is especially bad when adding/removing a new row in a table. In that case, since the table looks like as a list, the list compositor will need to update its own internal structures for each item in the new row. So if you have 1000 columns, 1000 updates will be processed. Even worse, since the list compositor create QQmlChangeSets for each item in the row, the view will end up receiving 1000 signals about the change. Combine this with the fact that QQmlDelegateModel contains a lot of undocumented complex code for dealing with groups, which is not needed or used by TableView (or ListView for that sake *), adding a new QQmlTableInstanceModel that understands table models, is the right solution. Auto testing the new class will be done from QQuickTableView, once it takes it into use. * Note: The fact that TableView/ListView is using QQmlDelegateModel internally to communicate with the data model is a private implementation detail. The application will never have access to this model (and can therefore not create any groups on it etc). The application can, however, create its own DelegateModel and assigns it to TableView/ListView. But this is a different issue, and will continue to work as before. Change-Id: If1c35c27e75f236161df84ecb1d919aa3050f5a0 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-06-23 09:33:04 +00:00
m_modelItems.remove(modelItem->index);
QQmlTableInstanceModel: implement support for reusing delegate items This patch will make it possible for TableView to reuse delegate items. The API lets TableView (or any kind of view in theory) specify if an item is reusable at the time the item is released. Reusing delegate items is specified per item, meaning that the view can choose to release some items as reusable, but others not. If the view never releases any items as reusable, no items will be added to the pool, and hence, no items will be reused. If the view releases an item as reusable, the model will move it to the reuse pool rather than destroying it (if the items ref-count is zero, and not persisted). The next time the view asks the model for a new item, the model will first check if the pool already contains an item, and if so, use it. Otherwise it will create a new item, like before. Items in the pool are supposed to rest there for a short while. Ideally only from the time items are flicked out on one side of the view, until we reuse them when new items are flicked in on the oppsite side. One big reason for this is that we have no way of hibernating items in QML, so they will effectively stay alive while inside the pool. This is not ideal, but still a huge performance improvement over what we had before, where all items are always created from scratch. If hibernating objects will ever be possible, it should be easy to extends the current logic to take advantage of this. Already existing tests for QQuickTableView should verify that no regressions are introduced with this patch. Since recycling of items is driven from the view, auto tests for this functionality is included with the patch for implementing reuse support in TableView (coming in a subsequent patch). Change-Id: I28aa687251ce3e7e1de0b1c799fdbf44d8867d45 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-07-19 11:06:19 +00:00
if (reusable == Reusable) {
m_reusableItemsPool.insertItem(modelItem);
emit itemPooled(modelItem->index, modelItem->object);
return QQmlInstanceModel::Pooled;
QQmlTableInstanceModel: implement support for reusing delegate items This patch will make it possible for TableView to reuse delegate items. The API lets TableView (or any kind of view in theory) specify if an item is reusable at the time the item is released. Reusing delegate items is specified per item, meaning that the view can choose to release some items as reusable, but others not. If the view never releases any items as reusable, no items will be added to the pool, and hence, no items will be reused. If the view releases an item as reusable, the model will move it to the reuse pool rather than destroying it (if the items ref-count is zero, and not persisted). The next time the view asks the model for a new item, the model will first check if the pool already contains an item, and if so, use it. Otherwise it will create a new item, like before. Items in the pool are supposed to rest there for a short while. Ideally only from the time items are flicked out on one side of the view, until we reuse them when new items are flicked in on the oppsite side. One big reason for this is that we have no way of hibernating items in QML, so they will effectively stay alive while inside the pool. This is not ideal, but still a huge performance improvement over what we had before, where all items are always created from scratch. If hibernating objects will ever be possible, it should be easy to extends the current logic to take advantage of this. Already existing tests for QQuickTableView should verify that no regressions are introduced with this patch. Since recycling of items is driven from the view, auto tests for this functionality is included with the patch for implementing reuse support in TableView (coming in a subsequent patch). Change-Id: I28aa687251ce3e7e1de0b1c799fdbf44d8867d45 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-07-19 11:06:19 +00:00
}
// The item is not reused or referenced by anyone, so just delete it
destroyModelItem(modelItem);
return QQmlInstanceModel::Destroyed;
}
QQmlTableInstanceModel: add new TableView helper class This patch will add a new model class: QQmlTableInstanceModel. It will be used by QQuickTableView to communicate with the data model, instead of using QQmlDelegateModel. The reason we abandon QQmlDelegateModel for QQuickTableView, is because QQmlDelegateModel was written from the start to only support list models, not table models. And to work around this, we proxy table models as a list models, and combine all columns to form one big list. This as some disadvantages. First and foremost because QQmlDelegateModel is using QQmlListCompositor internally. QQmlListCompositor can combine many different list models into one big list model, and divide them into groups. Any change done to a list model will also be mirrored in the list compositor. The compositor will further create QQmlChangeSets describing such model changes, which will then be emitted by QQmlDelegateModel so that the view can transition in the same changes. This flow is especially bad when adding/removing a new row in a table. In that case, since the table looks like as a list, the list compositor will need to update its own internal structures for each item in the new row. So if you have 1000 columns, 1000 updates will be processed. Even worse, since the list compositor create QQmlChangeSets for each item in the row, the view will end up receiving 1000 signals about the change. Combine this with the fact that QQmlDelegateModel contains a lot of undocumented complex code for dealing with groups, which is not needed or used by TableView (or ListView for that sake *), adding a new QQmlTableInstanceModel that understands table models, is the right solution. Auto testing the new class will be done from QQuickTableView, once it takes it into use. * Note: The fact that TableView/ListView is using QQmlDelegateModel internally to communicate with the data model is a private implementation detail. The application will never have access to this model (and can therefore not create any groups on it etc). The application can, however, create its own DelegateModel and assigns it to TableView/ListView. But this is a different issue, and will continue to work as before. Change-Id: If1c35c27e75f236161df84ecb1d919aa3050f5a0 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-06-23 09:33:04 +00:00
void QQmlTableInstanceModel::destroyModelItem(QQmlDelegateModelItem *modelItem)
{
emit destroyingItem(modelItem->object);
modelItem->destroyObject();
QQmlTableInstanceModel: add new TableView helper class This patch will add a new model class: QQmlTableInstanceModel. It will be used by QQuickTableView to communicate with the data model, instead of using QQmlDelegateModel. The reason we abandon QQmlDelegateModel for QQuickTableView, is because QQmlDelegateModel was written from the start to only support list models, not table models. And to work around this, we proxy table models as a list models, and combine all columns to form one big list. This as some disadvantages. First and foremost because QQmlDelegateModel is using QQmlListCompositor internally. QQmlListCompositor can combine many different list models into one big list model, and divide them into groups. Any change done to a list model will also be mirrored in the list compositor. The compositor will further create QQmlChangeSets describing such model changes, which will then be emitted by QQmlDelegateModel so that the view can transition in the same changes. This flow is especially bad when adding/removing a new row in a table. In that case, since the table looks like as a list, the list compositor will need to update its own internal structures for each item in the new row. So if you have 1000 columns, 1000 updates will be processed. Even worse, since the list compositor create QQmlChangeSets for each item in the row, the view will end up receiving 1000 signals about the change. Combine this with the fact that QQmlDelegateModel contains a lot of undocumented complex code for dealing with groups, which is not needed or used by TableView (or ListView for that sake *), adding a new QQmlTableInstanceModel that understands table models, is the right solution. Auto testing the new class will be done from QQuickTableView, once it takes it into use. * Note: The fact that TableView/ListView is using QQmlDelegateModel internally to communicate with the data model is a private implementation detail. The application will never have access to this model (and can therefore not create any groups on it etc). The application can, however, create its own DelegateModel and assigns it to TableView/ListView. But this is a different issue, and will continue to work as before. Change-Id: If1c35c27e75f236161df84ecb1d919aa3050f5a0 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-06-23 09:33:04 +00:00
delete modelItem;
}
void QQmlTableInstanceModel::cancel(int index)
{
auto modelItem = m_modelItems.value(index);
Q_ASSERT(modelItem);
// Since the view expects the item to be incubating, there should be
// an incubation task. And since the incubation is not done, no-one
// should yet have received, and therfore hold a reference to, the object.
Q_ASSERT(modelItem->incubationTask);
Q_ASSERT(!modelItem->isObjectReferenced());
m_modelItems.remove(index);
if (modelItem->object)
delete modelItem->object;
// modelItem->incubationTask will be deleted from the modelItems destructor
delete modelItem;
}
QQmlTableInstanceModel: implement support for reusing delegate items This patch will make it possible for TableView to reuse delegate items. The API lets TableView (or any kind of view in theory) specify if an item is reusable at the time the item is released. Reusing delegate items is specified per item, meaning that the view can choose to release some items as reusable, but others not. If the view never releases any items as reusable, no items will be added to the pool, and hence, no items will be reused. If the view releases an item as reusable, the model will move it to the reuse pool rather than destroying it (if the items ref-count is zero, and not persisted). The next time the view asks the model for a new item, the model will first check if the pool already contains an item, and if so, use it. Otherwise it will create a new item, like before. Items in the pool are supposed to rest there for a short while. Ideally only from the time items are flicked out on one side of the view, until we reuse them when new items are flicked in on the oppsite side. One big reason for this is that we have no way of hibernating items in QML, so they will effectively stay alive while inside the pool. This is not ideal, but still a huge performance improvement over what we had before, where all items are always created from scratch. If hibernating objects will ever be possible, it should be easy to extends the current logic to take advantage of this. Already existing tests for QQuickTableView should verify that no regressions are introduced with this patch. Since recycling of items is driven from the view, auto tests for this functionality is included with the patch for implementing reuse support in TableView (coming in a subsequent patch). Change-Id: I28aa687251ce3e7e1de0b1c799fdbf44d8867d45 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-07-19 11:06:19 +00:00
void QQmlTableInstanceModel::drainReusableItemsPool(int maxPoolTime)
{
m_reusableItemsPool.drain(maxPoolTime, [=](QQmlDelegateModelItem *modelItem){ destroyModelItem(modelItem); });
QQmlTableInstanceModel: implement support for reusing delegate items This patch will make it possible for TableView to reuse delegate items. The API lets TableView (or any kind of view in theory) specify if an item is reusable at the time the item is released. Reusing delegate items is specified per item, meaning that the view can choose to release some items as reusable, but others not. If the view never releases any items as reusable, no items will be added to the pool, and hence, no items will be reused. If the view releases an item as reusable, the model will move it to the reuse pool rather than destroying it (if the items ref-count is zero, and not persisted). The next time the view asks the model for a new item, the model will first check if the pool already contains an item, and if so, use it. Otherwise it will create a new item, like before. Items in the pool are supposed to rest there for a short while. Ideally only from the time items are flicked out on one side of the view, until we reuse them when new items are flicked in on the oppsite side. One big reason for this is that we have no way of hibernating items in QML, so they will effectively stay alive while inside the pool. This is not ideal, but still a huge performance improvement over what we had before, where all items are always created from scratch. If hibernating objects will ever be possible, it should be easy to extends the current logic to take advantage of this. Already existing tests for QQuickTableView should verify that no regressions are introduced with this patch. Since recycling of items is driven from the view, auto tests for this functionality is included with the patch for implementing reuse support in TableView (coming in a subsequent patch). Change-Id: I28aa687251ce3e7e1de0b1c799fdbf44d8867d45 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-07-19 11:06:19 +00:00
}
void QQmlTableInstanceModel::reuseItem(QQmlDelegateModelItem *item, int newModelIndex)
{
// Update the context properties index, row and column on
// the delegate item, and inform the application about it.
// Note that we set alwaysEmit to true, to force all bindings
// to be reevaluated, even if the index didn't change (since
// the model can have changed size since last usage).
const bool alwaysEmit = true;
QQmlTableInstanceModel: implement support for reusing delegate items This patch will make it possible for TableView to reuse delegate items. The API lets TableView (or any kind of view in theory) specify if an item is reusable at the time the item is released. Reusing delegate items is specified per item, meaning that the view can choose to release some items as reusable, but others not. If the view never releases any items as reusable, no items will be added to the pool, and hence, no items will be reused. If the view releases an item as reusable, the model will move it to the reuse pool rather than destroying it (if the items ref-count is zero, and not persisted). The next time the view asks the model for a new item, the model will first check if the pool already contains an item, and if so, use it. Otherwise it will create a new item, like before. Items in the pool are supposed to rest there for a short while. Ideally only from the time items are flicked out on one side of the view, until we reuse them when new items are flicked in on the oppsite side. One big reason for this is that we have no way of hibernating items in QML, so they will effectively stay alive while inside the pool. This is not ideal, but still a huge performance improvement over what we had before, where all items are always created from scratch. If hibernating objects will ever be possible, it should be easy to extends the current logic to take advantage of this. Already existing tests for QQuickTableView should verify that no regressions are introduced with this patch. Since recycling of items is driven from the view, auto tests for this functionality is included with the patch for implementing reuse support in TableView (coming in a subsequent patch). Change-Id: I28aa687251ce3e7e1de0b1c799fdbf44d8867d45 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-07-19 11:06:19 +00:00
const int newRow = m_adaptorModel.rowAt(newModelIndex);
const int newColumn = m_adaptorModel.columnAt(newModelIndex);
item->setModelIndex(newModelIndex, newRow, newColumn, alwaysEmit);
QQmlTableInstanceModel: implement support for reusing delegate items This patch will make it possible for TableView to reuse delegate items. The API lets TableView (or any kind of view in theory) specify if an item is reusable at the time the item is released. Reusing delegate items is specified per item, meaning that the view can choose to release some items as reusable, but others not. If the view never releases any items as reusable, no items will be added to the pool, and hence, no items will be reused. If the view releases an item as reusable, the model will move it to the reuse pool rather than destroying it (if the items ref-count is zero, and not persisted). The next time the view asks the model for a new item, the model will first check if the pool already contains an item, and if so, use it. Otherwise it will create a new item, like before. Items in the pool are supposed to rest there for a short while. Ideally only from the time items are flicked out on one side of the view, until we reuse them when new items are flicked in on the oppsite side. One big reason for this is that we have no way of hibernating items in QML, so they will effectively stay alive while inside the pool. This is not ideal, but still a huge performance improvement over what we had before, where all items are always created from scratch. If hibernating objects will ever be possible, it should be easy to extends the current logic to take advantage of this. Already existing tests for QQuickTableView should verify that no regressions are introduced with this patch. Since recycling of items is driven from the view, auto tests for this functionality is included with the patch for implementing reuse support in TableView (coming in a subsequent patch). Change-Id: I28aa687251ce3e7e1de0b1c799fdbf44d8867d45 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-07-19 11:06:19 +00:00
// Notify the application that all 'dynamic'/role-based context data has
// changed as well (their getter function will use the updated index).
auto const itemAsList = QList<QQmlDelegateModelItem *>() << item;
auto const updateAllRoles = QVector<int>();
m_adaptorModel.notify(itemAsList, newModelIndex, 1, updateAllRoles);
// Inform the view that the item is recycled. This will typically result
// in the view updating its own attached delegate item properties.
emit itemReused(newModelIndex, item->object);
}
void QQmlTableInstanceModel::incubateModelItem(QQmlDelegateModelItem *modelItem, QQmlIncubator::IncubationMode incubationMode)
{
// Guard the model item temporarily so that it's not deleted from
// incubatorStatusChanged(), in case the incubation is done synchronously.
modelItem->scriptRef++;
if (modelItem->incubationTask) {
// We're already incubating the model item from a previous request. If the previous call requested
// the item async, but the current request needs it sync, we need to force-complete the incubation.
const bool sync = (incubationMode == QQmlIncubator::Synchronous || incubationMode == QQmlIncubator::AsynchronousIfNested);
if (sync && modelItem->incubationTask->incubationMode() == QQmlIncubator::Asynchronous)
modelItem->incubationTask->forceCompletion();
} else {
modelItem->incubationTask = new QQmlTableInstanceModelIncubationTask(this, modelItem, incubationMode);
QQmlContextData *ctxt = new QQmlContextData;
QQmlContext *creationContext = modelItem->delegate->creationContext();
ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_qmlContext.data()));
ctxt->contextObject = modelItem;
modelItem->contextData = ctxt;
QQmlComponentPrivate::get(modelItem->delegate)->incubateObject(
modelItem->incubationTask,
modelItem->delegate,
m_qmlContext->engine(),
ctxt,
QQmlContextData::get(m_qmlContext));
}
// Remove the temporary guard
modelItem->scriptRef--;
}
QQmlTableInstanceModel: add new TableView helper class This patch will add a new model class: QQmlTableInstanceModel. It will be used by QQuickTableView to communicate with the data model, instead of using QQmlDelegateModel. The reason we abandon QQmlDelegateModel for QQuickTableView, is because QQmlDelegateModel was written from the start to only support list models, not table models. And to work around this, we proxy table models as a list models, and combine all columns to form one big list. This as some disadvantages. First and foremost because QQmlDelegateModel is using QQmlListCompositor internally. QQmlListCompositor can combine many different list models into one big list model, and divide them into groups. Any change done to a list model will also be mirrored in the list compositor. The compositor will further create QQmlChangeSets describing such model changes, which will then be emitted by QQmlDelegateModel so that the view can transition in the same changes. This flow is especially bad when adding/removing a new row in a table. In that case, since the table looks like as a list, the list compositor will need to update its own internal structures for each item in the new row. So if you have 1000 columns, 1000 updates will be processed. Even worse, since the list compositor create QQmlChangeSets for each item in the row, the view will end up receiving 1000 signals about the change. Combine this with the fact that QQmlDelegateModel contains a lot of undocumented complex code for dealing with groups, which is not needed or used by TableView (or ListView for that sake *), adding a new QQmlTableInstanceModel that understands table models, is the right solution. Auto testing the new class will be done from QQuickTableView, once it takes it into use. * Note: The fact that TableView/ListView is using QQmlDelegateModel internally to communicate with the data model is a private implementation detail. The application will never have access to this model (and can therefore not create any groups on it etc). The application can, however, create its own DelegateModel and assigns it to TableView/ListView. But this is a different issue, and will continue to work as before. Change-Id: If1c35c27e75f236161df84ecb1d919aa3050f5a0 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-06-23 09:33:04 +00:00
void QQmlTableInstanceModel::incubatorStatusChanged(QQmlTableInstanceModelIncubationTask *incubationTask, QQmlIncubator::Status status)
{
QQmlDelegateModelItem *modelItem = incubationTask->modelItemToIncubate;
Q_ASSERT(modelItem->incubationTask);
modelItem->incubationTask = nullptr;
incubationTask->modelItemToIncubate = nullptr;
if (status == QQmlIncubator::Ready) {
// Tag the incubated object with the model item for easy retrieval upon release etc.
modelItem->object->setProperty(kModelItemTag, QVariant::fromValue(modelItem));
// Emit that the item has been created. What normally happens next is that the view
// upon receiving the signal asks for the model item once more. And since the item is
// now in the map, it will be returned directly.
Q_ASSERT(modelItem->object);
modelItem->scriptRef++;
emit createdItem(modelItem->index, modelItem->object);
modelItem->scriptRef--;
} else if (status == QQmlIncubator::Error) {
qWarning() << "Error incubating delegate:" << incubationTask->errors();
}
if (!modelItem->isReferenced() && !modelItem->isObjectReferenced()) {
// We have no internal reference to the model item, and the view has no
// reference to the incubated object. So just delete the model item.
// Note that being here means that the object was incubated _async_
// (otherwise modelItem->isReferenced() would be true).
m_modelItems.remove(modelItem->index);
if (modelItem->object) {
modelItem->scriptRef++;
emit destroyingItem(modelItem->object);
modelItem->scriptRef--;
Q_ASSERT(!modelItem->isReferenced());
}
deleteModelItemLater(modelItem);
}
deleteIncubationTaskLater(incubationTask);
}
QQmlIncubator::Status QQmlTableInstanceModel::incubationStatus(int index) {
const auto modelItem = m_modelItems.value(index, nullptr);
if (!modelItem)
return QQmlIncubator::Null;
if (modelItem->incubationTask)
return modelItem->incubationTask->status();
// Since we clear the incubation task when we're done
// incubating, it means that the status is Ready.
return QQmlIncubator::Ready;
}
void QQmlTableInstanceModel::deleteIncubationTaskLater(QQmlIncubator *incubationTask)
{
// We often need to post-delete incubation tasks, since we cannot
// delete them while we're in the middle of an incubation change callback.
Q_ASSERT(!m_finishedIncubationTasks.contains(incubationTask));
m_finishedIncubationTasks.append(incubationTask);
if (m_finishedIncubationTasks.count() == 1)
QTimer::singleShot(1, this, &QQmlTableInstanceModel::deleteAllFinishedIncubationTasks);
}
void QQmlTableInstanceModel::deleteAllFinishedIncubationTasks()
{
qDeleteAll(m_finishedIncubationTasks);
m_finishedIncubationTasks.clear();
}
QVariant QQmlTableInstanceModel::model() const
{
return m_adaptorModel.model();
}
void QQmlTableInstanceModel::setModel(const QVariant &model)
{
QQmlTableInstanceModel: implement support for reusing delegate items This patch will make it possible for TableView to reuse delegate items. The API lets TableView (or any kind of view in theory) specify if an item is reusable at the time the item is released. Reusing delegate items is specified per item, meaning that the view can choose to release some items as reusable, but others not. If the view never releases any items as reusable, no items will be added to the pool, and hence, no items will be reused. If the view releases an item as reusable, the model will move it to the reuse pool rather than destroying it (if the items ref-count is zero, and not persisted). The next time the view asks the model for a new item, the model will first check if the pool already contains an item, and if so, use it. Otherwise it will create a new item, like before. Items in the pool are supposed to rest there for a short while. Ideally only from the time items are flicked out on one side of the view, until we reuse them when new items are flicked in on the oppsite side. One big reason for this is that we have no way of hibernating items in QML, so they will effectively stay alive while inside the pool. This is not ideal, but still a huge performance improvement over what we had before, where all items are always created from scratch. If hibernating objects will ever be possible, it should be easy to extends the current logic to take advantage of this. Already existing tests for QQuickTableView should verify that no regressions are introduced with this patch. Since recycling of items is driven from the view, auto tests for this functionality is included with the patch for implementing reuse support in TableView (coming in a subsequent patch). Change-Id: I28aa687251ce3e7e1de0b1c799fdbf44d8867d45 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-07-19 11:06:19 +00:00
// Pooled items are still accessible/alive for the application, and
// needs to stay in sync with the model. So we need to drain the pool
// completely when the model changes.
drainReusableItemsPool(0);
if (auto const aim = abstractItemModel())
disconnect(aim, &QAbstractItemModel::dataChanged, this, &QQmlTableInstanceModel::dataChangedCallback);
QQmlTableInstanceModel: add new TableView helper class This patch will add a new model class: QQmlTableInstanceModel. It will be used by QQuickTableView to communicate with the data model, instead of using QQmlDelegateModel. The reason we abandon QQmlDelegateModel for QQuickTableView, is because QQmlDelegateModel was written from the start to only support list models, not table models. And to work around this, we proxy table models as a list models, and combine all columns to form one big list. This as some disadvantages. First and foremost because QQmlDelegateModel is using QQmlListCompositor internally. QQmlListCompositor can combine many different list models into one big list model, and divide them into groups. Any change done to a list model will also be mirrored in the list compositor. The compositor will further create QQmlChangeSets describing such model changes, which will then be emitted by QQmlDelegateModel so that the view can transition in the same changes. This flow is especially bad when adding/removing a new row in a table. In that case, since the table looks like as a list, the list compositor will need to update its own internal structures for each item in the new row. So if you have 1000 columns, 1000 updates will be processed. Even worse, since the list compositor create QQmlChangeSets for each item in the row, the view will end up receiving 1000 signals about the change. Combine this with the fact that QQmlDelegateModel contains a lot of undocumented complex code for dealing with groups, which is not needed or used by TableView (or ListView for that sake *), adding a new QQmlTableInstanceModel that understands table models, is the right solution. Auto testing the new class will be done from QQuickTableView, once it takes it into use. * Note: The fact that TableView/ListView is using QQmlDelegateModel internally to communicate with the data model is a private implementation detail. The application will never have access to this model (and can therefore not create any groups on it etc). The application can, however, create its own DelegateModel and assigns it to TableView/ListView. But this is a different issue, and will continue to work as before. Change-Id: If1c35c27e75f236161df84ecb1d919aa3050f5a0 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-06-23 09:33:04 +00:00
m_adaptorModel.setModel(model, this, m_qmlContext->engine());
if (auto const aim = abstractItemModel())
connect(aim, &QAbstractItemModel::dataChanged, this, &QQmlTableInstanceModel::dataChangedCallback);
}
void QQmlTableInstanceModel::dataChangedCallback(const QModelIndex &begin, const QModelIndex &end, const QVector<int> &roles)
{
// This function is called when model data has changed. In that case, we tell the adaptor model
// to go through all the items we have created, find the ones that are affected, and notify that
// their model data has changed. This will in turn update QML bindings inside the delegate items.
int numberOfRowsChanged = end.row() - begin.row() + 1;
int numberOfColumnsChanged = end.column() - begin.column() + 1;
for (int column = 0; column < numberOfColumnsChanged; ++column) {
const int columnIndex = begin.column() + column;
const int rowIndex = begin.row() + (columnIndex * rows());
m_adaptorModel.notify(m_modelItems.values(), rowIndex, numberOfRowsChanged, roles);
}
QQmlTableInstanceModel: add new TableView helper class This patch will add a new model class: QQmlTableInstanceModel. It will be used by QQuickTableView to communicate with the data model, instead of using QQmlDelegateModel. The reason we abandon QQmlDelegateModel for QQuickTableView, is because QQmlDelegateModel was written from the start to only support list models, not table models. And to work around this, we proxy table models as a list models, and combine all columns to form one big list. This as some disadvantages. First and foremost because QQmlDelegateModel is using QQmlListCompositor internally. QQmlListCompositor can combine many different list models into one big list model, and divide them into groups. Any change done to a list model will also be mirrored in the list compositor. The compositor will further create QQmlChangeSets describing such model changes, which will then be emitted by QQmlDelegateModel so that the view can transition in the same changes. This flow is especially bad when adding/removing a new row in a table. In that case, since the table looks like as a list, the list compositor will need to update its own internal structures for each item in the new row. So if you have 1000 columns, 1000 updates will be processed. Even worse, since the list compositor create QQmlChangeSets for each item in the row, the view will end up receiving 1000 signals about the change. Combine this with the fact that QQmlDelegateModel contains a lot of undocumented complex code for dealing with groups, which is not needed or used by TableView (or ListView for that sake *), adding a new QQmlTableInstanceModel that understands table models, is the right solution. Auto testing the new class will be done from QQuickTableView, once it takes it into use. * Note: The fact that TableView/ListView is using QQmlDelegateModel internally to communicate with the data model is a private implementation detail. The application will never have access to this model (and can therefore not create any groups on it etc). The application can, however, create its own DelegateModel and assigns it to TableView/ListView. But this is a different issue, and will continue to work as before. Change-Id: If1c35c27e75f236161df84ecb1d919aa3050f5a0 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-06-23 09:33:04 +00:00
}
QQmlComponent *QQmlTableInstanceModel::delegate() const
{
return m_delegate;
}
void QQmlTableInstanceModel::setDelegate(QQmlComponent *delegate)
{
if (m_delegate == delegate)
return;
m_delegateChooser = nullptr;
if (delegate) {
QQmlAbstractDelegateComponent *adc =
qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
if (adc)
m_delegateChooser = adc;
}
QQmlTableInstanceModel: add new TableView helper class This patch will add a new model class: QQmlTableInstanceModel. It will be used by QQuickTableView to communicate with the data model, instead of using QQmlDelegateModel. The reason we abandon QQmlDelegateModel for QQuickTableView, is because QQmlDelegateModel was written from the start to only support list models, not table models. And to work around this, we proxy table models as a list models, and combine all columns to form one big list. This as some disadvantages. First and foremost because QQmlDelegateModel is using QQmlListCompositor internally. QQmlListCompositor can combine many different list models into one big list model, and divide them into groups. Any change done to a list model will also be mirrored in the list compositor. The compositor will further create QQmlChangeSets describing such model changes, which will then be emitted by QQmlDelegateModel so that the view can transition in the same changes. This flow is especially bad when adding/removing a new row in a table. In that case, since the table looks like as a list, the list compositor will need to update its own internal structures for each item in the new row. So if you have 1000 columns, 1000 updates will be processed. Even worse, since the list compositor create QQmlChangeSets for each item in the row, the view will end up receiving 1000 signals about the change. Combine this with the fact that QQmlDelegateModel contains a lot of undocumented complex code for dealing with groups, which is not needed or used by TableView (or ListView for that sake *), adding a new QQmlTableInstanceModel that understands table models, is the right solution. Auto testing the new class will be done from QQuickTableView, once it takes it into use. * Note: The fact that TableView/ListView is using QQmlDelegateModel internally to communicate with the data model is a private implementation detail. The application will never have access to this model (and can therefore not create any groups on it etc). The application can, however, create its own DelegateModel and assigns it to TableView/ListView. But this is a different issue, and will continue to work as before. Change-Id: If1c35c27e75f236161df84ecb1d919aa3050f5a0 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-06-23 09:33:04 +00:00
m_delegate = delegate;
}
const QAbstractItemModel *QQmlTableInstanceModel::abstractItemModel() const
{
return m_adaptorModel.adaptsAim() ? m_adaptorModel.aim() : nullptr;
QQmlTableInstanceModel: add new TableView helper class This patch will add a new model class: QQmlTableInstanceModel. It will be used by QQuickTableView to communicate with the data model, instead of using QQmlDelegateModel. The reason we abandon QQmlDelegateModel for QQuickTableView, is because QQmlDelegateModel was written from the start to only support list models, not table models. And to work around this, we proxy table models as a list models, and combine all columns to form one big list. This as some disadvantages. First and foremost because QQmlDelegateModel is using QQmlListCompositor internally. QQmlListCompositor can combine many different list models into one big list model, and divide them into groups. Any change done to a list model will also be mirrored in the list compositor. The compositor will further create QQmlChangeSets describing such model changes, which will then be emitted by QQmlDelegateModel so that the view can transition in the same changes. This flow is especially bad when adding/removing a new row in a table. In that case, since the table looks like as a list, the list compositor will need to update its own internal structures for each item in the new row. So if you have 1000 columns, 1000 updates will be processed. Even worse, since the list compositor create QQmlChangeSets for each item in the row, the view will end up receiving 1000 signals about the change. Combine this with the fact that QQmlDelegateModel contains a lot of undocumented complex code for dealing with groups, which is not needed or used by TableView (or ListView for that sake *), adding a new QQmlTableInstanceModel that understands table models, is the right solution. Auto testing the new class will be done from QQuickTableView, once it takes it into use. * Note: The fact that TableView/ListView is using QQmlDelegateModel internally to communicate with the data model is a private implementation detail. The application will never have access to this model (and can therefore not create any groups on it etc). The application can, however, create its own DelegateModel and assigns it to TableView/ListView. But this is a different issue, and will continue to work as before. Change-Id: If1c35c27e75f236161df84ecb1d919aa3050f5a0 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-06-23 09:33:04 +00:00
}
// --------------------------------------------------------
void QQmlTableInstanceModelIncubationTask::setInitialState(QObject *object)
{
initializeRequiredProperties(modelItemToIncubate, object);
if (QQmlIncubatorPrivate::get(this)->requiredProperties().empty()) {
modelItemToIncubate->object = object;
emit tableInstanceModel->initItem(modelItemToIncubate->index, object);
} else {
object->deleteLater();
}
QQmlTableInstanceModel: add new TableView helper class This patch will add a new model class: QQmlTableInstanceModel. It will be used by QQuickTableView to communicate with the data model, instead of using QQmlDelegateModel. The reason we abandon QQmlDelegateModel for QQuickTableView, is because QQmlDelegateModel was written from the start to only support list models, not table models. And to work around this, we proxy table models as a list models, and combine all columns to form one big list. This as some disadvantages. First and foremost because QQmlDelegateModel is using QQmlListCompositor internally. QQmlListCompositor can combine many different list models into one big list model, and divide them into groups. Any change done to a list model will also be mirrored in the list compositor. The compositor will further create QQmlChangeSets describing such model changes, which will then be emitted by QQmlDelegateModel so that the view can transition in the same changes. This flow is especially bad when adding/removing a new row in a table. In that case, since the table looks like as a list, the list compositor will need to update its own internal structures for each item in the new row. So if you have 1000 columns, 1000 updates will be processed. Even worse, since the list compositor create QQmlChangeSets for each item in the row, the view will end up receiving 1000 signals about the change. Combine this with the fact that QQmlDelegateModel contains a lot of undocumented complex code for dealing with groups, which is not needed or used by TableView (or ListView for that sake *), adding a new QQmlTableInstanceModel that understands table models, is the right solution. Auto testing the new class will be done from QQuickTableView, once it takes it into use. * Note: The fact that TableView/ListView is using QQmlDelegateModel internally to communicate with the data model is a private implementation detail. The application will never have access to this model (and can therefore not create any groups on it etc). The application can, however, create its own DelegateModel and assigns it to TableView/ListView. But this is a different issue, and will continue to work as before. Change-Id: If1c35c27e75f236161df84ecb1d919aa3050f5a0 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-06-23 09:33:04 +00:00
}
void QQmlTableInstanceModelIncubationTask::statusChanged(QQmlIncubator::Status status)
{
if (!QQmlTableInstanceModel::isDoneIncubating(modelItemToIncubate))
return;
// We require the view to cancel any ongoing load
// requests before the tableInstanceModel is destructed.
Q_ASSERT(tableInstanceModel);
tableInstanceModel->incubatorStatusChanged(this, status);
}
QQmlTableInstanceModel: implement support for reusing delegate items This patch will make it possible for TableView to reuse delegate items. The API lets TableView (or any kind of view in theory) specify if an item is reusable at the time the item is released. Reusing delegate items is specified per item, meaning that the view can choose to release some items as reusable, but others not. If the view never releases any items as reusable, no items will be added to the pool, and hence, no items will be reused. If the view releases an item as reusable, the model will move it to the reuse pool rather than destroying it (if the items ref-count is zero, and not persisted). The next time the view asks the model for a new item, the model will first check if the pool already contains an item, and if so, use it. Otherwise it will create a new item, like before. Items in the pool are supposed to rest there for a short while. Ideally only from the time items are flicked out on one side of the view, until we reuse them when new items are flicked in on the oppsite side. One big reason for this is that we have no way of hibernating items in QML, so they will effectively stay alive while inside the pool. This is not ideal, but still a huge performance improvement over what we had before, where all items are always created from scratch. If hibernating objects will ever be possible, it should be easy to extends the current logic to take advantage of this. Already existing tests for QQuickTableView should verify that no regressions are introduced with this patch. Since recycling of items is driven from the view, auto tests for this functionality is included with the patch for implementing reuse support in TableView (coming in a subsequent patch). Change-Id: I28aa687251ce3e7e1de0b1c799fdbf44d8867d45 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-07-19 11:06:19 +00:00
#include "moc_qqmltableinstancemodel_p.cpp"
QQmlTableInstanceModel: add new TableView helper class This patch will add a new model class: QQmlTableInstanceModel. It will be used by QQuickTableView to communicate with the data model, instead of using QQmlDelegateModel. The reason we abandon QQmlDelegateModel for QQuickTableView, is because QQmlDelegateModel was written from the start to only support list models, not table models. And to work around this, we proxy table models as a list models, and combine all columns to form one big list. This as some disadvantages. First and foremost because QQmlDelegateModel is using QQmlListCompositor internally. QQmlListCompositor can combine many different list models into one big list model, and divide them into groups. Any change done to a list model will also be mirrored in the list compositor. The compositor will further create QQmlChangeSets describing such model changes, which will then be emitted by QQmlDelegateModel so that the view can transition in the same changes. This flow is especially bad when adding/removing a new row in a table. In that case, since the table looks like as a list, the list compositor will need to update its own internal structures for each item in the new row. So if you have 1000 columns, 1000 updates will be processed. Even worse, since the list compositor create QQmlChangeSets for each item in the row, the view will end up receiving 1000 signals about the change. Combine this with the fact that QQmlDelegateModel contains a lot of undocumented complex code for dealing with groups, which is not needed or used by TableView (or ListView for that sake *), adding a new QQmlTableInstanceModel that understands table models, is the right solution. Auto testing the new class will be done from QQuickTableView, once it takes it into use. * Note: The fact that TableView/ListView is using QQmlDelegateModel internally to communicate with the data model is a private implementation detail. The application will never have access to this model (and can therefore not create any groups on it etc). The application can, however, create its own DelegateModel and assigns it to TableView/ListView. But this is a different issue, and will continue to work as before. Change-Id: If1c35c27e75f236161df84ecb1d919aa3050f5a0 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-06-23 09:33:04 +00:00
QT_END_NAMESPACE