QConcatenateTablesProxyModel: cache roleNames()

The return value of this function only changes when models are added
or removed, and some users call this function often (an earlier
version of QGenericItemModel did, possibly QML does?), so cache the
result of the function instead of re-creating the QHash on every call.

Use QAbstractItemModelPrivate::defaultRoleNames() so we don't need
an instance to get the base class' contents. Amend the comment in
QAIM::roleNames() asking to keep the two in sync.

Amends 5ffb9d7ae6d60fb370b79f8222dab7d7e628fa4f.

[ChangeLog][Important Behavior Changes][QtCore][QConcatenateTablesProxyModel] The
roleNames property is only updated (lazily) on addSourceModel() and
removeSourceModel() now (was: on every call of the function). If your
source models change their roleNames() dynamically, you need to call
invalidateRoleNamesCache() manually when they do.

Pick-to: 6.9
Change-Id: I32edc93ff9ff7b252ca04f0d61b54b2244d0be48
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Marc Mutz 2025-03-17 16:09:07 +01:00
parent ed92f6c22f
commit f3256e059f
2 changed files with 22 additions and 5 deletions

View File

@ -2660,6 +2660,7 @@ QHash<int,QByteArray> QAbstractItemModel::roleNames() const
{
// if the return value ever becomes dependent on *this, also change the following overrides:
// - QFileSystemModel
// - QConcatenateTablesProxyModel
return QAbstractItemModelPrivate::defaultRoleNames();
}

View File

@ -61,6 +61,7 @@ public:
ConnArray connections;
};
QList<ModelInfo> m_models;
mutable QHash<int, QByteArray> m_roleNames;
QList<ModelInfo>::const_iterator findSourceModel(const QAbstractItemModel *m) const
{
@ -77,6 +78,9 @@ public:
// for columns{AboutToBe,}{Inserted,Removed}
int m_newColumnCount;
mutable uint m_roleNamesDirty : 1;
uint m_reserved : 31;
// for layoutAboutToBeChanged/layoutChanged
QList<QPersistentModelIndex> layoutChangePersistentIndexes;
QList<QModelIndex> layoutChangeProxyIndexes;
@ -85,7 +89,9 @@ public:
QConcatenateTablesProxyModelPrivate::QConcatenateTablesProxyModelPrivate()
: m_rowCount(0),
m_columnCount(0),
m_newColumnCount(0)
m_newColumnCount(0),
m_roleNamesDirty(true),
m_reserved(0)
{
}
@ -507,6 +513,12 @@ void QConcatenateTablesProxyModel::addSourceModel(QAbstractItemModel *sourceMode
QObjectPrivate::connect(sourceModel, &QAbstractItemModel::modelReset,
d, &QConcatenateTablesProxyModelPrivate::slotModelReset),
});
if (!d->m_roleNamesDirty) {
// do update immediately, since append() is a simple update:
const auto newRoleNames = sourceModel->roleNames();
for (const auto &[k, v] : newRoleNames.asKeyValueRange())
d->m_roleNames.insert_or_assign(k, v);
}
if (newRows > 0)
endInsertRows();
@ -533,6 +545,7 @@ void QConcatenateTablesProxyModel::removeSourceModel(QAbstractItemModel *sourceM
if (rowsRemoved > 0)
beginRemoveRows(QModelIndex(), rowsPrior, rowsPrior + rowsRemoved - 1);
d->m_models.erase(it);
d->m_roleNamesDirty = true;
d->m_rowCount -= rowsRemoved;
if (rowsRemoved > 0)
endRemoveRows();
@ -551,10 +564,13 @@ void QConcatenateTablesProxyModel::removeSourceModel(QAbstractItemModel *sourceM
QHash<int, QByteArray> QConcatenateTablesProxyModel::roleNames() const
{
Q_D(const QConcatenateTablesProxyModel);
QHash<int, QByteArray> ret = QAbstractItemModel::roleNames();
for (const auto &[model, _] : d->m_models)
ret.insert(model->roleNames());
return ret;
if (d->m_roleNamesDirty) {
d->m_roleNames = QAbstractItemModelPrivate::defaultRoleNames();
for (const auto &[model, _] : d->m_models)
d->m_roleNames.insert(model->roleNames());
d->m_roleNamesDirty = false;
}
return d->m_roleNames;
}
void QConcatenateTablesProxyModelPrivate::slotRowsAboutToBeInserted(const QModelIndex &parent,