Add a way to filter only rows or columns in QSortFilterProxyModel
If we want to filter away a column without changing the filtering for the rows calling invalidateFilter() is wasteful because it will call filterAcceptsRow() for all rows even though that is not needed. This commit add two functions, invalidateRowsFilter() and invalidateColumnsFilter() that work the same way as invalidateFilter() except that they will invoke respectively only filterAcceptsRow() and filterAcceptsColumn(). Change-Id: Ib4351cf08c229bd97bbbfee6da92397dca579a84 Reviewed-by: David Faure <david.faure@kdab.com>bb10
parent
963c47aece
commit
b5f6a85d27
|
|
@ -274,6 +274,12 @@ class QSortFilterProxyModelPrivate : public QAbstractProxyModelPrivate
|
|||
Q_DECLARE_PUBLIC(QSortFilterProxyModel)
|
||||
|
||||
public:
|
||||
enum class Direction {
|
||||
Rows = 1,
|
||||
Columns = 2,
|
||||
All = Rows | Columns
|
||||
};
|
||||
|
||||
struct Mapping {
|
||||
QVector<int> source_rows;
|
||||
QVector<int> source_columns;
|
||||
|
|
@ -413,7 +419,7 @@ public:
|
|||
void update_persistent_indexes(const QModelIndexPairList &source_indexes);
|
||||
|
||||
void filter_about_to_be_changed(const QModelIndex &source_parent = QModelIndex());
|
||||
void filter_changed(const QModelIndex &source_parent = QModelIndex());
|
||||
void filter_changed(Direction dir, const QModelIndex &source_parent = QModelIndex());
|
||||
QSet<int> handle_filter_changed(
|
||||
QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
|
||||
const QModelIndex &source_parent, Qt::Orientation orient);
|
||||
|
|
@ -431,6 +437,11 @@ public:
|
|||
|
||||
typedef QHash<QModelIndex, QSortFilterProxyModelPrivate::Mapping *> IndexMap;
|
||||
|
||||
static bool operator&(QSortFilterProxyModelPrivate::Direction a, QSortFilterProxyModelPrivate::Direction b)
|
||||
{
|
||||
return int(a) & int(b);
|
||||
}
|
||||
|
||||
void QSortFilterProxyModelPrivate::_q_sourceModelDestroyed()
|
||||
{
|
||||
QAbstractProxyModelPrivate::_q_sourceModelDestroyed();
|
||||
|
|
@ -1269,14 +1280,14 @@ void QSortFilterProxyModelPrivate::filter_about_to_be_changed(const QModelIndex
|
|||
Updates the proxy model (adds/removes rows) based on the
|
||||
new filter.
|
||||
*/
|
||||
void QSortFilterProxyModelPrivate::filter_changed(const QModelIndex &source_parent)
|
||||
void QSortFilterProxyModelPrivate::filter_changed(Direction dir, const QModelIndex &source_parent)
|
||||
{
|
||||
IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
|
||||
if (it == source_index_mapping.constEnd())
|
||||
return;
|
||||
Mapping *m = it.value();
|
||||
QSet<int> rows_removed = handle_filter_changed(m->proxy_rows, m->source_rows, source_parent, Qt::Vertical);
|
||||
QSet<int> columns_removed = handle_filter_changed(m->proxy_columns, m->source_columns, source_parent, Qt::Horizontal);
|
||||
const QSet<int> rows_removed = (dir & Direction::Rows) ? handle_filter_changed(m->proxy_rows, m->source_rows, source_parent, Qt::Vertical) : QSet<int>();
|
||||
const QSet<int> columns_removed = (dir & Direction::Columns) ? handle_filter_changed(m->proxy_columns, m->source_columns, source_parent, Qt::Horizontal) : QSet<int>();
|
||||
|
||||
// We need to iterate over a copy of m->mapped_children because otherwise it may be changed by other code, invalidating
|
||||
// the iterator it2.
|
||||
|
|
@ -1290,7 +1301,7 @@ void QSortFilterProxyModelPrivate::filter_changed(const QModelIndex &source_pare
|
|||
indexesToRemove.push_back(i);
|
||||
remove_from_mapping(source_child_index);
|
||||
} else {
|
||||
filter_changed(source_child_index);
|
||||
filter_changed(dir, source_child_index);
|
||||
}
|
||||
}
|
||||
QVector<int>::const_iterator removeIt = indexesToRemove.constEnd();
|
||||
|
|
@ -2605,7 +2616,7 @@ void QSortFilterProxyModel::setFilterRegExp(const QRegExp ®Exp)
|
|||
Q_D(QSortFilterProxyModel);
|
||||
d->filter_about_to_be_changed();
|
||||
d->filter_data.setRegExp(regExp);
|
||||
d->filter_changed();
|
||||
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
|
||||
}
|
||||
|
||||
#if QT_CONFIG(regularexpression)
|
||||
|
|
@ -2634,7 +2645,7 @@ void QSortFilterProxyModel::setFilterRegularExpression(const QRegularExpression
|
|||
Q_D(QSortFilterProxyModel);
|
||||
d->filter_about_to_be_changed();
|
||||
d->filter_data.setRegularExpression(regularExpression);
|
||||
d->filter_changed();
|
||||
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -2657,7 +2668,7 @@ void QSortFilterProxyModel::setFilterKeyColumn(int column)
|
|||
Q_D(QSortFilterProxyModel);
|
||||
d->filter_about_to_be_changed();
|
||||
d->filter_column = column;
|
||||
d->filter_changed();
|
||||
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
@ -2683,7 +2694,7 @@ void QSortFilterProxyModel::setFilterCaseSensitivity(Qt::CaseSensitivity cs)
|
|||
return;
|
||||
d->filter_about_to_be_changed();
|
||||
d->filter_data.setCaseSensitivity(cs);
|
||||
d->filter_changed();
|
||||
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
|
||||
emit filterCaseSensitivityChanged(cs);
|
||||
}
|
||||
|
||||
|
|
@ -2754,7 +2765,7 @@ void QSortFilterProxyModel::setFilterRegExp(const QString &pattern)
|
|||
QRegExp rx(pattern);
|
||||
rx.setCaseSensitivity(d->filter_data.caseSensitivity());
|
||||
d->filter_data.setRegExp(rx);
|
||||
d->filter_changed();
|
||||
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
|
||||
}
|
||||
|
||||
#if QT_CONFIG(regularexpression)
|
||||
|
|
@ -2775,7 +2786,7 @@ void QSortFilterProxyModel::setFilterRegularExpression(const QString &pattern)
|
|||
d->filter_about_to_be_changed();
|
||||
QRegularExpression rx(pattern);
|
||||
d->filter_data.setRegularExpression(rx);
|
||||
d->filter_changed();
|
||||
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -2791,7 +2802,7 @@ void QSortFilterProxyModel::setFilterWildcard(const QString &pattern)
|
|||
d->filter_about_to_be_changed();
|
||||
QRegExp rx(pattern, d->filter_data.caseSensitivity(), QRegExp::Wildcard);
|
||||
d->filter_data.setRegExp(rx);
|
||||
d->filter_changed();
|
||||
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
@ -2806,7 +2817,7 @@ void QSortFilterProxyModel::setFilterFixedString(const QString &pattern)
|
|||
d->filter_about_to_be_changed();
|
||||
QRegExp rx(pattern, d->filter_data.caseSensitivity(), QRegExp::FixedString);
|
||||
d->filter_data.setRegExp(rx);
|
||||
d->filter_changed();
|
||||
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
@ -2886,7 +2897,7 @@ void QSortFilterProxyModel::setFilterRole(int role)
|
|||
return;
|
||||
d->filter_about_to_be_changed();
|
||||
d->filter_role = role;
|
||||
d->filter_changed();
|
||||
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
|
||||
emit filterRoleChanged(role);
|
||||
}
|
||||
|
||||
|
|
@ -2913,7 +2924,7 @@ void QSortFilterProxyModel::setRecursiveFilteringEnabled(bool recursive)
|
|||
return;
|
||||
d->filter_about_to_be_changed();
|
||||
d->filter_recursive = recursive;
|
||||
d->filter_changed();
|
||||
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
|
||||
emit recursiveFilteringEnabledChanged(recursive);
|
||||
}
|
||||
|
||||
|
|
@ -2964,11 +2975,57 @@ void QSortFilterProxyModel::filterChanged()
|
|||
(e.g. filterAcceptsRow()), and your filter parameters have changed.
|
||||
|
||||
\sa invalidate()
|
||||
\sa invalidateColumnsFilter()
|
||||
\sa invalidateRowsFilter()
|
||||
*/
|
||||
void QSortFilterProxyModel::invalidateFilter()
|
||||
{
|
||||
Q_D(QSortFilterProxyModel);
|
||||
d->filter_changed();
|
||||
d->filter_changed(QSortFilterProxyModelPrivate::Direction::All);
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 6.0
|
||||
|
||||
Invalidates the current filtering for the columns.
|
||||
|
||||
This function should be called if you are implementing custom filtering
|
||||
(by filterAcceptsColumn()), and your filter parameters have changed.
|
||||
This differs from invalidateFilter() in that it will not invoke
|
||||
filterAcceptsRow(), but only filterAcceptsColumn(). You can use this
|
||||
instead of invalidateFilter() if you want to hide or show a column where
|
||||
the rows don't change.
|
||||
|
||||
\sa invalidate()
|
||||
\sa invalidateFilter()
|
||||
\sa invalidateRowsFilter()
|
||||
*/
|
||||
void QSortFilterProxyModel::invalidateColumnsFilter()
|
||||
{
|
||||
Q_D(QSortFilterProxyModel);
|
||||
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Columns);
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 6.0
|
||||
|
||||
Invalidates the current filtering for the rows.
|
||||
|
||||
This function should be called if you are implementing custom filtering
|
||||
(by filterAcceptsRow()), and your filter parameters have changed.
|
||||
This differs from invalidateFilter() in that it will not invoke
|
||||
filterAcceptsColumn(), but only filterAcceptsRow(). You can use this
|
||||
instead of invalidateFilter() if you want to hide or show a row where
|
||||
the columns don't change.
|
||||
|
||||
\sa invalidate()
|
||||
\sa invalidateFilter()
|
||||
\sa invalidateColumnsFilter()
|
||||
*/
|
||||
void QSortFilterProxyModel::invalidateRowsFilter()
|
||||
{
|
||||
Q_D(QSortFilterProxyModel);
|
||||
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
|||
|
|
@ -143,6 +143,8 @@ protected:
|
|||
QT_DEPRECATED_X("Use QSortFilterProxyModel::invalidateFilter") void filterChanged();
|
||||
#endif
|
||||
void invalidateFilter();
|
||||
void invalidateRowsFilter();
|
||||
void invalidateColumnsFilter();
|
||||
|
||||
public:
|
||||
using QObject::parent;
|
||||
|
|
|
|||
|
|
@ -4940,4 +4940,84 @@ void tst_QSortFilterProxyModel::filterAndInsertRow()
|
|||
}
|
||||
}
|
||||
|
||||
void tst_QSortFilterProxyModel::invalidateColumnsOrRowsFilter()
|
||||
{
|
||||
class FilterProxy : public QSortFilterProxyModel
|
||||
{
|
||||
public:
|
||||
FilterProxy()
|
||||
{}
|
||||
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override
|
||||
{
|
||||
rowFiltered++;
|
||||
|
||||
if (sourceModel()->data(sourceModel()->index(source_row, 0, source_parent)).toString() == QLatin1String("A1"))
|
||||
return !rejectA1;
|
||||
return true;
|
||||
}
|
||||
bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const override
|
||||
{
|
||||
Q_UNUSED(source_column)
|
||||
Q_UNUSED(source_parent)
|
||||
|
||||
columnFiltered++;
|
||||
return true;
|
||||
}
|
||||
|
||||
mutable int rowFiltered = 0;
|
||||
mutable int columnFiltered = 0;
|
||||
bool rejectA1 = false;
|
||||
|
||||
using QSortFilterProxyModel::invalidateFilter;
|
||||
using QSortFilterProxyModel::invalidateRowsFilter;
|
||||
using QSortFilterProxyModel::invalidateColumnsFilter;
|
||||
};
|
||||
QStandardItemModel model(10, 4);
|
||||
for (int i = 0; i < model.rowCount(); ++i) {
|
||||
for (int j = 0; j < model.columnCount(); ++j) {
|
||||
model.setItem(i, j, new QStandardItem(QString('A' + j) + QString::number(i + 1)));
|
||||
model.item(i, 0)->appendColumn({ new QStandardItem(QString("child col %0").arg(j)) });
|
||||
}
|
||||
}
|
||||
FilterProxy proxy;
|
||||
proxy.setSourceModel(&model);
|
||||
|
||||
QTreeView view;
|
||||
view.setModel(&proxy);
|
||||
view.expandAll();
|
||||
|
||||
QCOMPARE(proxy.rowFiltered, 20); //10 parents + 10 children
|
||||
QCOMPARE(proxy.columnFiltered, 44); // 4 parents + 4 * 10 children
|
||||
|
||||
proxy.rowFiltered = proxy.columnFiltered = 0;
|
||||
proxy.invalidateFilter();
|
||||
|
||||
QCOMPARE(proxy.rowFiltered, 20);
|
||||
QCOMPARE(proxy.columnFiltered, 44);
|
||||
|
||||
proxy.rowFiltered = proxy.columnFiltered = 0;
|
||||
proxy.invalidateRowsFilter();
|
||||
|
||||
QCOMPARE(proxy.rowFiltered, 20);
|
||||
QCOMPARE(proxy.columnFiltered, 0);
|
||||
|
||||
proxy.rowFiltered = proxy.columnFiltered = 0;
|
||||
proxy.invalidateColumnsFilter();
|
||||
|
||||
QCOMPARE(proxy.rowFiltered, 0);
|
||||
QCOMPARE(proxy.columnFiltered, 44);
|
||||
|
||||
QCOMPARE(proxy.rowCount(), 10);
|
||||
proxy.rejectA1 = true;
|
||||
proxy.rowFiltered = proxy.columnFiltered = 0;
|
||||
proxy.invalidateRowsFilter();
|
||||
QCOMPARE(proxy.rowCount(), 9);
|
||||
QCOMPARE(proxy.rowFiltered, 19); // it will not check the child row of A1
|
||||
|
||||
proxy.rowFiltered = proxy.columnFiltered = 0;
|
||||
proxy.setRecursiveFilteringEnabled(true); // this triggers invalidateRowsFilter()
|
||||
QCOMPARE(proxy.rowCount(), 10);
|
||||
QCOMPARE(proxy.rowFiltered, 20);
|
||||
}
|
||||
|
||||
#include "tst_qsortfilterproxymodel.moc"
|
||||
|
|
|
|||
|
|
@ -159,6 +159,8 @@ private slots:
|
|||
void removeIntervals_data();
|
||||
void removeIntervals();
|
||||
|
||||
void invalidateColumnsOrRowsFilter();
|
||||
|
||||
protected:
|
||||
void buildHierarchy(const QStringList &data, QAbstractItemModel *model);
|
||||
void checkHierarchy(const QStringList &data, const QAbstractItemModel *model);
|
||||
|
|
|
|||
Loading…
Reference in New Issue