summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/itemmodels
diff options
context:
space:
mode:
authorTim Angus <tim@ngus.net>2018-03-12 10:57:10 +0000
committerTim Angus <tim@ngus.net>2018-03-13 09:42:36 +0000
commit5c7f6ee51f2678dc540e59686c49517176603087 (patch)
tree538ae1f6a3949013be6007a838f8b13d20979cd5 /tests/auto/corelib/itemmodels
parent1c98cb2b04ba07cda92133811c4fbfbf4637b0df (diff)
QSFPM: Faster row removes
When the source model of a QSortFilterProxyModel has rows removed, a mapping is performed in order to figure out which corresponding rows in the sorted and filtered view of the model are affected. In doing so it constructs a vector of removal intervals which are subsequently used to emit rowsRemoved signals. In the case where many rows are removed (e.g. all of them), many removal intervals are identified that are often adjacent and could be simplified, which is what this patch does. i.e. instead of emitting 3 rowsRemoved for 0-3, 4-6, 7-12, now a single rowsRemoved is emitted for 0-12. Change-Id: Ia503091cc4928378c88257cd8b431582e9ff454e Reviewed-by: David Faure <david.faure@kdab.com>
Diffstat (limited to 'tests/auto/corelib/itemmodels')
-rw-r--r--tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp212
1 files changed, 173 insertions, 39 deletions
diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp
index 1acedf4271..8af6347f89 100644
--- a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp
+++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp
@@ -152,6 +152,10 @@ private slots:
void emitLayoutChangedOnlyIfSortingChanged();
void checkSetNewModel();
+
+ void removeIntervals_data();
+ void removeIntervals();
+
protected:
void buildHierarchy(const QStringList &data, QAbstractItemModel *model);
void checkHierarchy(const QStringList &data, const QAbstractItemModel *model);
@@ -4402,6 +4406,44 @@ void tst_QSortFilterProxyModel::emitLayoutChangedOnlyIfSortingChanged_data()
QTest::newRow("many_changes_no_layoutChanged") << -1 << Qt::DisplayRole << "7,5,4,3,2,1,0,8" << "75432108" << "0248" << 0;
}
+// Custom version of QStringListModel which supports emitting dataChanged for many rows at once
+class CustomStringListModel : public QAbstractListModel
+{
+public:
+ bool setData(const QModelIndex &index, const QVariant &value, int role) override
+ {
+ if (index.row() >= 0 && index.row() < lst.size()
+ && (role == Qt::EditRole || role == Qt::DisplayRole)) {
+ lst.replace(index.row(), value.toString());
+ emit dataChanged(index, index, { Qt::DisplayRole, Qt::EditRole });
+ return true;
+ }
+ return false;
+ }
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
+ {
+ if (role == Qt::DisplayRole || role == Qt::EditRole)
+ return lst.at(index.row());
+ return QVariant();
+ }
+ int rowCount(const QModelIndex & = QModelIndex()) const override { return lst.count(); }
+
+ void replaceData(const QStringList &newData)
+ {
+ lst = newData;
+ emit dataChanged(index(0, 0), index(rowCount() - 1, 0), { Qt::DisplayRole, Qt::EditRole });
+ }
+
+ void emitDecorationChangedSignal()
+ {
+ const QModelIndex idx = index(0, 0);
+ emit dataChanged(idx, idx, { Qt::DecorationRole });
+ }
+
+private:
+ QStringList lst;
+};
+
void tst_QSortFilterProxyModel::emitLayoutChangedOnlyIfSortingChanged()
{
QFETCH(int, changedRow);
@@ -4411,45 +4453,6 @@ void tst_QSortFilterProxyModel::emitLayoutChangedOnlyIfSortingChanged()
QFETCH(QString, expectedProxyRowTexts);
QFETCH(int, expectedLayoutChanged);
- // Custom version of QStringListModel which supports emitting dataChanged for many rows at once
- class CustomStringListModel : public QAbstractListModel
- {
- public:
- bool setData(const QModelIndex &index, const QVariant &value, int role) override
- {
- if (index.row() >= 0 && index.row() < lst.size()
- && (role == Qt::EditRole || role == Qt::DisplayRole)) {
- lst.replace(index.row(), value.toString());
- emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
- return true;
- }
- return false;
- }
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
- {
- if (role == Qt::DisplayRole || role == Qt::EditRole)
- return lst.at(index.row());
- return QVariant();
- }
- int rowCount(const QModelIndex & = QModelIndex()) const override
- {
- return lst.count();
- }
-
- void replaceData(const QStringList &newData)
- {
- lst = newData;
- emit dataChanged(index(0, 0), index(rowCount()-1, 0), {Qt::DisplayRole, Qt::EditRole});
- }
-
- void emitDecorationChangedSignal()
- {
- const QModelIndex idx = index(0, 0);
- emit dataChanged(idx, idx, {Qt::DecorationRole});
- }
- private:
- QStringList lst;
- };
CustomStringListModel model;
QStringList strings;
for (auto i = 8; i >= 1; --i)
@@ -4487,6 +4490,137 @@ void tst_QSortFilterProxyModel::emitLayoutChangedOnlyIfSortingChanged()
QCOMPARE(proxyLayoutChangedSpy.size(), expectedLayoutChanged);
}
+void tst_QSortFilterProxyModel::removeIntervals_data()
+{
+ QTest::addColumn<QStringList>("sourceItems");
+ QTest::addColumn<int>("sortOrder");
+ QTest::addColumn<QString>("filter");
+ QTest::addColumn<QStringList>("replacementSourceItems");
+ QTest::addColumn<IntPairList>("expectedRemovedProxyIntervals");
+ QTest::addColumn<QStringList>("expectedProxyItems");
+
+ QTest::newRow("filter all, sort ascending")
+ << (QStringList() << "a"
+ << "b"
+ << "c") // sourceItems
+ << static_cast<int>(Qt::AscendingOrder) // sortOrder
+ << "[^x]" // filter
+ << (QStringList() << "x"
+ << "x"
+ << "x") // replacementSourceItems
+ << (IntPairList() << IntPair(0, 2)) // expectedRemovedIntervals
+ << QStringList() // expectedProxyItems
+ ;
+
+ QTest::newRow("filter all, sort descending")
+ << (QStringList() << "a"
+ << "b"
+ << "c") // sourceItems
+ << static_cast<int>(Qt::DescendingOrder) // sortOrder
+ << "[^x]" // filter
+ << (QStringList() << "x"
+ << "x"
+ << "x") // replacementSourceItems
+ << (IntPairList() << IntPair(0, 2)) // expectedRemovedIntervals
+ << QStringList() // expectedProxyItems
+ ;
+
+ QTest::newRow("filter first and last, sort ascending")
+ << (QStringList() << "a"
+ << "b"
+ << "c") // sourceItems
+ << static_cast<int>(Qt::AscendingOrder) // sortOrder
+ << "[^x]" // filter
+ << (QStringList() << "x"
+ << "b"
+ << "x") // replacementSourceItems
+ << (IntPairList() << IntPair(2, 2) << IntPair(0, 0)) // expectedRemovedIntervals
+ << (QStringList() << "b") // expectedProxyItems
+ ;
+
+ QTest::newRow("filter first and last, sort descending")
+ << (QStringList() << "a"
+ << "b"
+ << "c") // sourceItems
+ << static_cast<int>(Qt::DescendingOrder) // sortOrder
+ << "[^x]" // filter
+ << (QStringList() << "x"
+ << "b"
+ << "x") // replacementSourceItems
+ << (IntPairList() << IntPair(2, 2) << IntPair(0, 0)) // expectedRemovedIntervals
+ << (QStringList() << "b") // expectedProxyItems
+ ;
+}
+
+void tst_QSortFilterProxyModel::removeIntervals()
+{
+ QFETCH(QStringList, sourceItems);
+ QFETCH(int, sortOrder);
+ QFETCH(QString, filter);
+ QFETCH(QStringList, replacementSourceItems);
+ QFETCH(IntPairList, expectedRemovedProxyIntervals);
+ QFETCH(QStringList, expectedProxyItems);
+
+ CustomStringListModel model;
+ QSortFilterProxyModel proxy;
+
+ model.replaceData(sourceItems);
+ proxy.setSourceModel(&model);
+
+ for (int i = 0; i < sourceItems.count(); ++i) {
+ QModelIndex sindex = model.index(i, 0, QModelIndex());
+ QModelIndex pindex = proxy.index(i, 0, QModelIndex());
+ QCOMPARE(proxy.data(pindex, Qt::DisplayRole), model.data(sindex, Qt::DisplayRole));
+ }
+
+ proxy.setDynamicSortFilter(true);
+
+ if (sortOrder != -1)
+ proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder));
+ if (!filter.isEmpty())
+ proxy.setFilterRegExp(QRegExp(filter));
+
+ (void)proxy.rowCount(QModelIndex()); // force mapping
+
+ QSignalSpy removeSpy(&proxy, &QSortFilterProxyModel::rowsRemoved);
+ QSignalSpy insertSpy(&proxy, &QSortFilterProxyModel::rowsInserted);
+ QSignalSpy aboutToRemoveSpy(&proxy, &QSortFilterProxyModel::rowsAboutToBeRemoved);
+ QSignalSpy aboutToInsertSpy(&proxy, &QSortFilterProxyModel::rowsAboutToBeInserted);
+
+ QVERIFY(removeSpy.isValid());
+ QVERIFY(insertSpy.isValid());
+ QVERIFY(aboutToRemoveSpy.isValid());
+ QVERIFY(aboutToInsertSpy.isValid());
+
+ model.replaceData(replacementSourceItems);
+
+ QCOMPARE(aboutToRemoveSpy.count(), expectedRemovedProxyIntervals.count());
+ for (int i = 0; i < aboutToRemoveSpy.count(); ++i) {
+ QList<QVariant> args = aboutToRemoveSpy.at(i);
+ QCOMPARE(args.at(1).type(), QVariant::Int);
+ QCOMPARE(args.at(2).type(), QVariant::Int);
+ QCOMPARE(args.at(1).toInt(), expectedRemovedProxyIntervals.at(i).first);
+ QCOMPARE(args.at(2).toInt(), expectedRemovedProxyIntervals.at(i).second);
+ }
+ QCOMPARE(removeSpy.count(), expectedRemovedProxyIntervals.count());
+ for (int i = 0; i < removeSpy.count(); ++i) {
+ QList<QVariant> args = removeSpy.at(i);
+ QCOMPARE(args.at(1).type(), QVariant::Int);
+ QCOMPARE(args.at(2).type(), QVariant::Int);
+ QCOMPARE(args.at(1).toInt(), expectedRemovedProxyIntervals.at(i).first);
+ QCOMPARE(args.at(2).toInt(), expectedRemovedProxyIntervals.at(i).second);
+ }
+
+ QCOMPARE(insertSpy.count(), 0);
+ QCOMPARE(aboutToInsertSpy.count(), 0);
+
+ QCOMPARE(proxy.rowCount(QModelIndex()), expectedProxyItems.count());
+ for (int i = 0; i < expectedProxyItems.count(); ++i) {
+ QModelIndex pindex = proxy.index(i, 0, QModelIndex());
+ QCOMPARE(proxy.data(pindex, Qt::DisplayRole).toString(), expectedProxyItems.at(i));
+ }
+}
+
void tst_QSortFilterProxyModel::dynamicFilterWithoutSort()
{
QStringListModel model;