forked from qt-creator/qt-creator
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:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user