ProjectExplorer: Use QSortFilterProxyModel for TaskFilterModel

We used our own, hand-rolled filter model implementation for performance
reasons, but benchmarking shows no difference.

Change-Id: I076ffeffbd39b468308d48d23d15a69b966dbc23
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Christian Kandeler
2019-04-04 13:34:37 +02:00
parent 07645527a3
commit c960885733
3 changed files with 18 additions and 191 deletions

View File

@@ -326,174 +326,25 @@ void TaskModel::setFileNotFound(const QModelIndex &idx, bool b)
// TaskFilterModel // TaskFilterModel
///// /////
TaskFilterModel::TaskFilterModel(TaskModel *sourceModel, QObject *parent) : QAbstractItemModel(parent), TaskFilterModel::TaskFilterModel(TaskModel *sourceModel, QObject *parent)
m_sourceModel(sourceModel) : QSortFilterProxyModel(parent)
{ {
Q_ASSERT(m_sourceModel); QTC_ASSERT(sourceModel, return);
updateMapping(); setSourceModel(sourceModel);
connect(m_sourceModel, &QAbstractItemModel::rowsInserted,
this, &TaskFilterModel::handleNewRows);
connect(m_sourceModel, &QAbstractItemModel::rowsAboutToBeRemoved,
this, &TaskFilterModel::handleRowsAboutToBeRemoved);
connect(m_sourceModel, &QAbstractItemModel::rowsRemoved,
this, [this](const QModelIndex &parent, int, int) {
QTC_ASSERT(!parent.isValid(), return);
if (m_beginRemoveRowsSent) {
m_beginRemoveRowsSent = false;
endRemoveRows();
}
});
connect(m_sourceModel, &QAbstractItemModel::modelReset,
this, &TaskFilterModel::invalidateFilter);
connect(m_sourceModel, &QAbstractItemModel::dataChanged,
this, &TaskFilterModel::handleDataChanged);
m_includeUnknowns = m_includeWarnings = m_includeErrors = true; m_includeUnknowns = m_includeWarnings = m_includeErrors = true;
} }
QModelIndex TaskFilterModel::index(int row, int column, const QModelIndex &parent) const void TaskFilterModel::setFilterIncludesWarnings(bool b)
{ {
if (parent.isValid()) m_includeWarnings = b;
return QModelIndex(); m_includeUnknowns = b; // "Unknowns" are often associated with warnings
return createIndex(row, column); invalidateFilter();
} }
QModelIndex TaskFilterModel::parent(const QModelIndex &child) const bool TaskFilterModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{ {
Q_UNUSED(child) Q_UNUSED(source_parent);
return QModelIndex(); return filterAcceptsTask(taskModel()->tasks().at(source_row));
}
int TaskFilterModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return m_mapping.count();
}
int TaskFilterModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return m_sourceModel->columnCount(parent);
}
QVariant TaskFilterModel::data(const QModelIndex &index, int role) const
{
return m_sourceModel->data(mapToSource(index), role);
}
static QPair<int, int> findFilteredRange(int first, int last, const QList<int> &list)
{
auto filteredFirst = std::lower_bound(list.constBegin(), list.constEnd(), first);
auto filteredLast = std::upper_bound(filteredFirst, list.constEnd(), last);
return qMakePair(filteredFirst - list.constBegin(), filteredLast - list.constBegin() - 1);
}
void TaskFilterModel::handleNewRows(const QModelIndex &index, int first, int last)
{
QTC_ASSERT(!index.isValid(), return);
const int newItemCount = last - first + 1;
QList<int> newMapping;
for (int i = first; i <= last; ++i) {
const Task &task = m_sourceModel->task(m_sourceModel->index(i, 0));
if (filterAcceptsTask(task))
newMapping.append(i);
}
const int newMappingCount = newMapping.count();
if (!newMappingCount)
return;
int filteredFirst = -1;
if (last == m_sourceModel->rowCount() - 1)
filteredFirst = m_mapping.count();
else
filteredFirst = std::lower_bound(m_mapping.constBegin(), m_mapping.constEnd(), first) - m_mapping.constBegin();
const int filteredLast = filteredFirst + newMappingCount - 1;
beginInsertRows(QModelIndex(), filteredFirst, filteredLast);
if (filteredFirst == m_mapping.count()) {
m_mapping.append(newMapping);
} else {
const QList<int> rest = m_mapping.mid(filteredFirst);
m_mapping.reserve(m_mapping.count() + newMappingCount);
m_mapping.erase(m_mapping.begin() + filteredFirst, m_mapping.end());
m_mapping.append(newMapping);
for (int pos : rest)
m_mapping.append(pos + newItemCount);
}
endInsertRows();
}
void TaskFilterModel::handleRowsAboutToBeRemoved(const QModelIndex &index, int first, int last)
{
m_beginRemoveRowsSent = false;
QTC_ASSERT(!index.isValid(), return);
const QPair<int, int> range = findFilteredRange(first, last, m_mapping);
if (range.first <= range.second) { // remove corresponding rows in filtermodel
beginRemoveRows(QModelIndex(), range.first, range.second);
m_beginRemoveRowsSent = true;
m_mapping.erase(m_mapping.begin() + range.first, m_mapping.begin() + range.second + 1);
}
// adapt existing mapping to removed source indices
const int sourceRemovedCount = (last - first) + 1;
for (int i = range.first; i < m_mapping.count(); ++i)
m_mapping[i] = m_mapping.at(i) - sourceRemovedCount;
}
void TaskFilterModel::handleDataChanged(const QModelIndex &top, const QModelIndex &bottom)
{
const QPair<int, int> range = findFilteredRange(top.row(), bottom.row(), m_mapping);
if (range.first > range.second)
return;
emit dataChanged(index(range.first, top.column()), index(range.second, bottom.column()));
}
QModelIndex TaskFilterModel::mapFromSource(const QModelIndex &idx) const
{
if (!idx.isValid())
return QModelIndex();
auto it = std::lower_bound(m_mapping.constBegin(), m_mapping.constEnd(), idx.row());
QTC_ASSERT(it != m_mapping.constEnd() && idx.row() == *it, return QModelIndex());
return index(it - m_mapping.constBegin(), 0);
}
QModelIndex TaskFilterModel::mapToSource(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
int row = index.row();
QTC_ASSERT(row >= 0 && row < m_mapping.count(), return QModelIndex());
return m_sourceModel->index(m_mapping.at(row), index.column(), index.parent());
}
void TaskFilterModel::invalidateFilter()
{
beginResetModel();
updateMapping();
endResetModel();
}
void TaskFilterModel::updateMapping() const
{
m_mapping.clear();
for (int i = 0; i < m_sourceModel->rowCount(); ++i) {
QModelIndex index = m_sourceModel->index(i, 0);
const Task &task = m_sourceModel->task(index);
if (filterAcceptsTask(task))
m_mapping.append(i);
}
} }
bool TaskFilterModel::filterAcceptsTask(const Task &task) const bool TaskFilterModel::filterAcceptsTask(const Task &task) const

View File

@@ -25,7 +25,7 @@
#pragma once #pragma once
#include <QAbstractItemModel> #include <QSortFilterProxyModel>
#include <QIcon> #include <QIcon>
@@ -120,26 +120,17 @@ private:
int m_sizeOfLineNumber = 0; int m_sizeOfLineNumber = 0;
}; };
class TaskFilterModel : public QAbstractItemModel class TaskFilterModel : public QSortFilterProxyModel
{ {
Q_OBJECT Q_OBJECT
public: public:
TaskFilterModel(TaskModel *sourceModel, QObject *parent = nullptr); TaskFilterModel(TaskModel *sourceModel, QObject *parent = nullptr);
TaskModel *taskModel() { return m_sourceModel; } TaskModel *taskModel() const { return static_cast<TaskModel *>(sourceModel()); }
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &child) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool filterIncludesUnknowns() const { return m_includeUnknowns; }
void setFilterIncludesUnknowns(bool b) { m_includeUnknowns = b; invalidateFilter(); }
bool filterIncludesWarnings() const { return m_includeWarnings; } bool filterIncludesWarnings() const { return m_includeWarnings; }
void setFilterIncludesWarnings(bool b) { m_includeWarnings = b; invalidateFilter(); } void setFilterIncludesWarnings(bool b);
bool filterIncludesErrors() const { return m_includeErrors; } bool filterIncludesErrors() const { return m_includeErrors; }
void setFilterIncludesErrors(bool b) { m_includeErrors = b; invalidateFilter(); } void setFilterIncludesErrors(bool b) { m_includeErrors = b; invalidateFilter(); }
@@ -147,22 +138,13 @@ public:
QList<Core::Id> filteredCategories() const { return m_categoryIds; } QList<Core::Id> filteredCategories() const { return m_categoryIds; }
void setFilteredCategories(const QList<Core::Id> &categoryIds) { m_categoryIds = categoryIds; invalidateFilter(); } void setFilteredCategories(const QList<Core::Id> &categoryIds) { m_categoryIds = categoryIds; invalidateFilter(); }
Task task(const QModelIndex &index) const Task task(const QModelIndex &index) const { return taskModel()->task(mapToSource(index)); }
{ return m_sourceModel->task(mapToSource(index)); }
bool hasFile(const QModelIndex &index) const bool hasFile(const QModelIndex &index) const
{ return m_sourceModel->hasFile(mapToSource(index)); } { return taskModel()->hasFile(mapToSource(index)); }
QModelIndex mapFromSource(const QModelIndex &idx) const;
private: private:
void handleNewRows(const QModelIndex &index, int first, int last); bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
void handleRowsAboutToBeRemoved(const QModelIndex &index, int first, int last);
void handleDataChanged(const QModelIndex &top, const QModelIndex &bottom);
QModelIndex mapToSource(const QModelIndex &index) const;
void invalidateFilter();
void updateMapping() const;
bool filterAcceptsTask(const Task &task) const; bool filterAcceptsTask(const Task &task) const;
bool m_beginRemoveRowsSent = false; bool m_beginRemoveRowsSent = false;
@@ -170,10 +152,6 @@ private:
bool m_includeWarnings; bool m_includeWarnings;
bool m_includeErrors; bool m_includeErrors;
QList<Core::Id> m_categoryIds; QList<Core::Id> m_categoryIds;
mutable QList<int> m_mapping;
TaskModel *m_sourceModel;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -417,7 +417,6 @@ void TaskWindow::loadSettings()
if (value.isValid()) { if (value.isValid()) {
bool includeWarnings = value.toBool(); bool includeWarnings = value.toBool();
d->m_filter->setFilterIncludesWarnings(includeWarnings); d->m_filter->setFilterIncludesWarnings(includeWarnings);
d->m_filter->setFilterIncludesUnknowns(includeWarnings);
d->m_filterWarningsButton->setDown(d->m_filter->filterIncludesWarnings()); d->m_filterWarningsButton->setDown(d->m_filter->filterIncludesWarnings());
} }
} }
@@ -536,7 +535,6 @@ void TaskWindow::actionTriggered()
void TaskWindow::setShowWarnings(bool show) void TaskWindow::setShowWarnings(bool show)
{ {
d->m_filter->setFilterIncludesWarnings(show); d->m_filter->setFilterIncludesWarnings(show);
d->m_filter->setFilterIncludesUnknowns(show); // "Unknowns" are often associated with warnings
} }
void TaskWindow::updateCategoriesMenu() void TaskWindow::updateCategoriesMenu()