Implement QListModel::moveRows

Implemented the virtual method moveRows to allow row movement.

[ChangeLog][QtWidgets][QListWidget] Implemented moveRows in model

Task-number: QTBUG-69807
Change-Id: I212b560b8778306a0315d9d5e4710efcc7dbbe44
Reviewed-by: David Faure <david.faure@kdab.com>
This commit is contained in:
Luca Beldi 2018-09-04 20:09:23 +01:00
parent 551e11c286
commit fcdb459c06
4 changed files with 141 additions and 8 deletions

View File

@ -293,6 +293,30 @@ bool QListModel::removeRows(int row, int count, const QModelIndex &parent)
return true;
}
/*!
\since 5.13
\reimp
*/
bool QListModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild)
{
if (sourceRow < 0
|| sourceRow + count - 1 >= rowCount(sourceParent)
|| destinationChild <= 0
|| destinationChild > rowCount(destinationParent)
|| sourceRow == destinationChild - 1
|| count <= 0) {
return false;
}
if (!beginMoveRows(QModelIndex(), sourceRow, sourceRow + count - 1, QModelIndex(), destinationChild))
return false;
destinationChild--;
const int fromRow = destinationChild < sourceRow ? (sourceRow + count - 1) : sourceRow;
while (count--)
items.move(fromRow, destinationChild);
endMoveRows();
return true;
}
Qt::ItemFlags QListModel::flags(const QModelIndex &index) const
{
if (!index.isValid() || index.row() >= items.count() || index.model() != this)

View File

@ -108,6 +108,7 @@ public:
bool insertRows(int row, int count = 1, const QModelIndex &parent = QModelIndex()) override;
bool removeRows(int row, int count = 1, const QModelIndex &parent = QModelIndex()) override;
bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;

View File

@ -83,8 +83,8 @@ private slots:
void supportedDragDropActions();
void moveRows();
void moveRows_data();
void moveRows();
void moveRowsInvalid_data();
void moveRowsInvalid();
};
@ -115,11 +115,11 @@ void tst_QStringListModel::moveRowsInvalid_data()
tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this);
QTest::addRow("source_row_equal_rowCount") << tempModel << QModelIndex() << tempModel->rowCount() << 1 << QModelIndex() << 1;
tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this);
QTest::addRow("destination_row_greather_rowCount") << tempModel << QModelIndex() << 0 << 1 << QModelIndex() << tempModel->rowCount() + 1;
QTest::addRow("destination_row_greater_rowCount") << tempModel << QModelIndex() << 0 << 1 << QModelIndex() << tempModel->rowCount() + 1;
tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this);
QTest::addRow("move_row_within_source_range") << tempModel << QModelIndex() << 0 << 3 << QModelIndex() << 2;
tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this);
QTest::addRow("destrination_row_before_0") << tempModel << QModelIndex() << 1 << 1 << QModelIndex() << 0;
QTest::addRow("destination_row_before_0") << tempModel << QModelIndex() << 1 << 1 << QModelIndex() << 0;
}
void tst_QStringListModel::moveRowsInvalid()
@ -154,8 +154,8 @@ void tst_QStringListModel::moveRows_data()
QTest::newRow("1_Item_from_bottom_to_middle") << 5 << 1 << 3 << QStringList{"A", "B", "F", "C", "D", "E"};
QTest::newRow("1_Item_from_bottom to_top") << 5 << 1 << 1 << QStringList{"F", "A", "B", "C", "D", "E"};
QTest::newRow("1_Item_from_middle_to_bottom") << 2 << 1 << 6 << QStringList{"A", "B", "D", "E", "F", "C"};
QTest::newRow("1_Item_from_middle_to_middle before") << 2 << 1 << 1 << QStringList{"C", "A", "B", "D", "E", "F"};
QTest::newRow("1_Item_from_middle_to_middle after") << 2 << 1 << 4 << QStringList{"A", "B", "D", "C", "E", "F"};
QTest::newRow("1_Item_from_middle_to_middle_before") << 2 << 1 << 1 << QStringList{"C", "A", "B", "D", "E", "F"};
QTest::newRow("1_Item_from_middle_to_middle_after") << 2 << 1 << 4 << QStringList{"A", "B", "D", "C", "E", "F"};
QTest::newRow("2_Items_from_top_to_middle") << 0 << 2 << 3 << QStringList{"C", "A", "B", "D", "E", "F"};
QTest::newRow("2_Items_from_top_to_bottom") << 0 << 2 << 6 << QStringList{"C", "D", "E", "F", "A", "B"};
@ -163,8 +163,8 @@ void tst_QStringListModel::moveRows_data()
QTest::newRow("2_Items_from_bottom_to_middle") << 4 << 2 << 3 << QStringList{"A", "B", "E", "F", "C", "D"};
QTest::newRow("2_Items_from_bottom_to_top") << 4 << 2 << 1 << QStringList{"E", "F", "A", "B", "C", "D"};
QTest::newRow("2_Items_from_middle_to_bottom") << 2 << 2 << 6 << QStringList{"A", "B", "E", "F", "C", "D"};
QTest::newRow("2_Items_from_middle_to_middle before") << 3 << 2 << 2 << QStringList{"A", "D", "E", "B", "C", "F"};
QTest::newRow("2_Items_from_middle_to_middle after") << 1 << 2 << 5 << QStringList{"A", "D", "E", "B", "C", "F"};
QTest::newRow("2_Items_from_middle_to_middle_before") << 3 << 2 << 2 << QStringList{"A", "D", "E", "B", "C", "F"};
QTest::newRow("2_Items_from_middle_to_middle_after") << 1 << 2 << 5 << QStringList{"A", "D", "E", "B", "C", "F"};
}
void tst_QStringListModel::moveRows()
@ -180,7 +180,7 @@ void tst_QStringListModel::moveRows()
QCOMPARE(baseModel.stringList(), expected);
QCOMPARE(rowMovedSpy.size(), 1);
QCOMPARE(rowAboutMovedSpy.size(), 1);
for (const QList<QVariant> &signalArgs : {rowMovedSpy.takeFirst(), rowAboutMovedSpy.takeFirst()}){
for (const QList<QVariant> &signalArgs : {rowMovedSpy.first(), rowAboutMovedSpy.first()}){
QVERIFY(!signalArgs.at(0).value<QModelIndex>().isValid());
QCOMPARE(signalArgs.at(1).toInt(), startRow);
QCOMPARE(signalArgs.at(2).toInt(), startRow + count - 1);

View File

@ -31,6 +31,7 @@
#include <QtGui/QtGui>
#include <QtWidgets/QtWidgets>
#include <qlist.h>
#include <QSignalSpy>
#include <qlistwidget.h>
#include <private/qlistwidget_p.h>
@ -120,6 +121,11 @@ private slots:
void clearItemData();
#endif
void moveRows_data();
void moveRows();
void moveRowsInvalid_data();
void moveRowsInvalid();
protected slots:
void rowsAboutToBeInserted(const QModelIndex &parent, int first, int last)
{ modelChanged(RowsAboutToBeInserted, parent, first, last); }
@ -151,6 +157,108 @@ private:
};
void tst_QListWidget::moveRowsInvalid_data()
{
QTest::addColumn<QListWidget*>("baseWidget");
QTest::addColumn<QModelIndex>("startParent");
QTest::addColumn<int>("startRow");
QTest::addColumn<int>("count");
QTest::addColumn<QModelIndex>("destinationParent");
QTest::addColumn<int>("destination");
const auto createWidget = []() -> QListWidget* {
QListWidget* result = new QListWidget;
result->addItems(QStringList{"A", "B", "C", "D", "E", "F"});
return result;
};
QTest::addRow("destination_equal_source") << createWidget() << QModelIndex() << 0 << 1 << QModelIndex() << 1;
QTest::addRow("count_equal_0") << createWidget() << QModelIndex() << 0 << 0 << QModelIndex() << 2;
QListWidget* tempWidget = createWidget();
QTest::addRow("move_child") << tempWidget << tempWidget->model()->index(0, 0) << 0 << 1 << QModelIndex() << 2;
tempWidget = createWidget();
QTest::addRow("move_to_child") << tempWidget << QModelIndex() << 0 << 1 << tempWidget->model()->index(0, 0) << 2;
QTest::addRow("negative_count") << createWidget() << QModelIndex() << 0 << -1 << QModelIndex() << 2;
QTest::addRow("negative_source_row") << createWidget() << QModelIndex() << -1 << 1 << QModelIndex() << 2;
QTest::addRow("negative_destination_row") << createWidget() << QModelIndex() << 0 << 1 << QModelIndex() << -1;
QTest::addRow("source_row_equal_rowCount") << createWidget() << QModelIndex() << 6 << 1 << QModelIndex() << 1;
QTest::addRow("destination_row_greater_rowCount") << createWidget() << QModelIndex() << 0 << 1 << QModelIndex() << 6 + 1;
QTest::addRow("move_row_within_source_range") << createWidget() << QModelIndex() << 0 << 3 << QModelIndex() << 2;
QTest::addRow("destination_row_before_0") << createWidget() << QModelIndex() << 1 << 1 << QModelIndex() << 0;
}
void tst_QListWidget::moveRowsInvalid()
{
QFETCH(QListWidget* const, baseWidget);
QFETCH(const QModelIndex, startParent);
QFETCH(const int, startRow);
QFETCH(const int, count);
QFETCH(const QModelIndex, destinationParent);
QFETCH(const int, destination);
QAbstractItemModel *baseModel = baseWidget->model();
QSignalSpy rowMovedSpy(baseModel, &QAbstractItemModel::rowsMoved);
QSignalSpy rowAboutMovedSpy(baseModel, &QAbstractItemModel::rowsAboutToBeMoved);
QVERIFY(rowMovedSpy.isValid());
QVERIFY(rowAboutMovedSpy.isValid());
QVERIFY(!baseModel->moveRows(startParent, startRow, count, destinationParent, destination));
QCOMPARE(rowMovedSpy.size(), 0);
QCOMPARE(rowAboutMovedSpy.size(), 0);
delete baseWidget;
}
void tst_QListWidget::moveRows_data()
{
QTest::addColumn<int>("startRow");
QTest::addColumn<int>("count");
QTest::addColumn<int>("destination");
QTest::addColumn<QStringList>("expected");
QTest::newRow("1_Item_from_top_to_middle") << 0 << 1 << 3 << QStringList{"B", "C", "A", "D", "E", "F"};
QTest::newRow("1_Item_from_top_to_bottom") << 0 << 1 << 6 << QStringList{"B", "C", "D", "E", "F", "A"};
QTest::newRow("1_Item_from_middle_to_top") << 2 << 1 << 1 << QStringList{"C", "A", "B", "D", "E", "F"};
QTest::newRow("1_Item_from_bottom_to_middle") << 5 << 1 << 3 << QStringList{"A", "B", "F", "C", "D", "E"};
QTest::newRow("1_Item_from_bottom to_top") << 5 << 1 << 1 << QStringList{"F", "A", "B", "C", "D", "E"};
QTest::newRow("1_Item_from_middle_to_bottom") << 2 << 1 << 6 << QStringList{"A", "B", "D", "E", "F", "C"};
QTest::newRow("1_Item_from_middle_to_middle_before") << 2 << 1 << 1 << QStringList{"C", "A", "B", "D", "E", "F"};
QTest::newRow("1_Item_from_middle_to_middle_after") << 2 << 1 << 4 << QStringList{"A", "B", "D", "C", "E", "F"};
QTest::newRow("2_Items_from_top_to_middle") << 0 << 2 << 3 << QStringList{"C", "A", "B", "D", "E", "F"};
QTest::newRow("2_Items_from_top_to_bottom") << 0 << 2 << 6 << QStringList{"C", "D", "E", "F", "A", "B"};
QTest::newRow("2_Items_from_middle_to_top") << 2 << 2 << 1 << QStringList{"C", "D", "A", "B", "E", "F"};
QTest::newRow("2_Items_from_bottom_to_middle") << 4 << 2 << 3 << QStringList{"A", "B", "E", "F", "C", "D"};
QTest::newRow("2_Items_from_bottom_to_top") << 4 << 2 << 1 << QStringList{"E", "F", "A", "B", "C", "D"};
QTest::newRow("2_Items_from_middle_to_bottom") << 2 << 2 << 6 << QStringList{"A", "B", "E", "F", "C", "D"};
QTest::newRow("2_Items_from_middle_to_middle_before") << 3 << 2 << 2 << QStringList{"A", "D", "E", "B", "C", "F"};
QTest::newRow("2_Items_from_middle_to_middle_after") << 1 << 2 << 5 << QStringList{"A", "D", "E", "B", "C", "F"};
}
void tst_QListWidget::moveRows()
{
QFETCH(const int, startRow);
QFETCH(const int, count);
QFETCH(const int, destination);
QFETCH(const QStringList, expected);
QListWidget baseWidget;
baseWidget.addItems(QStringList{"A", "B", "C", "D", "E", "F"});
QAbstractItemModel *baseModel = baseWidget.model();
QSignalSpy rowMovedSpy(baseModel, &QAbstractItemModel::rowsMoved);
QSignalSpy rowAboutMovedSpy(baseModel, &QAbstractItemModel::rowsAboutToBeMoved);
QVERIFY(baseModel->moveRows(QModelIndex(), startRow, count, QModelIndex(), destination));
QCOMPARE(baseModel->rowCount(), expected.size());
for (int i = 0; i < expected.size(); ++i)
QCOMPARE(baseModel->index(i, 0).data().toString(), expected.at(i));
QCOMPARE(rowMovedSpy.size(), 1);
QCOMPARE(rowAboutMovedSpy.size(), 1);
for (const QList<QVariant> &signalArgs : {rowMovedSpy.first(), rowAboutMovedSpy.first()}){
QVERIFY(!signalArgs.at(0).value<QModelIndex>().isValid());
QCOMPARE(signalArgs.at(1).toInt(), startRow);
QCOMPARE(signalArgs.at(2).toInt(), startRow + count - 1);
QVERIFY(!signalArgs.at(3).value<QModelIndex>().isValid());
QCOMPARE(signalArgs.at(4).toInt(), destination);
}
}
typedef QList<int> IntList;
tst_QListWidget::tst_QListWidget(): testWidget(0), rcParent(8), rcFirst(8,0), rcLast(8,0)