summaryrefslogtreecommitdiffstats
path: root/tests/auto/widgets/itemviews
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/widgets/itemviews')
-rw-r--r--tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp178
1 files changed, 159 insertions, 19 deletions
diff --git a/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp b/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp
index 238f5f3ffe..161f7f648d 100644
--- a/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp
+++ b/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp
@@ -167,6 +167,7 @@ private slots:
void taskQTBUG_51086_skippingIndexesInSelectedIndexes();
void taskQTBUG_47694_indexOutOfBoundBatchLayout();
void itemAlignment();
+ void internalDragDropMove_data();
void internalDragDropMove();
};
@@ -2526,46 +2527,185 @@ void tst_QListView::itemAlignment()
QVERIFY(w.visualRect(item1->index()).width() < w.visualRect(item2->index()).width());
}
+void tst_QListView::internalDragDropMove_data()
+{
+ QTest::addColumn<QListView::ViewMode>("viewMode");
+ QTest::addColumn<QAbstractItemView::DragDropMode>("dragDropMode");
+ QTest::addColumn<Qt::DropActions>("supportedDropActions");
+ QTest::addColumn<Qt::DropAction>("defaultDropAction");
+ QTest::addColumn<Qt::ItemFlags>("itemFlags");
+ QTest::addColumn<bool>("modelMoves");
+ QTest::addColumn<QStringList>("expectedData");
+
+ const Qt::ItemFlags defaultFlags = Qt::ItemIsSelectable
+ | Qt::ItemIsEnabled
+ | Qt::ItemIsEditable
+ | Qt::ItemIsDragEnabled;
+
+ const QStringList reordered = QStringList{"0", "2", "3", "4", "5", "6", "7", "8", "9", "1"};
+ const QStringList replaced = QStringList{"0", "2", "3", "4", "1", "6", "7", "8", "9"};
+
+ for (auto viewMode : { QListView::IconMode, QListView::ListMode }) {
+ for (auto modelMoves : { true, false } ) {
+ QByteArray rowName = viewMode == QListView::IconMode ? "icon" : "list" ;
+ rowName += modelMoves ? ", model moves" : ", model doesn't move";
+ QTest::newRow((rowName + ", copy&move").constData())
+ << viewMode
+ << QAbstractItemView::InternalMove
+ << (Qt::CopyAction|Qt::MoveAction)
+ << Qt::MoveAction
+ << defaultFlags
+ << modelMoves
+ << reordered;
+
+ QTest::newRow((rowName + ", only move").constData())
+ << viewMode
+ << QAbstractItemView::InternalMove
+ << (Qt::IgnoreAction|Qt::MoveAction)
+ << Qt::MoveAction
+ << defaultFlags
+ << modelMoves
+ << reordered;
+
+ QTest::newRow((rowName + ", replace item").constData())
+ << viewMode
+ << QAbstractItemView::InternalMove
+ << (Qt::IgnoreAction|Qt::MoveAction)
+ << Qt::MoveAction
+ << (defaultFlags | Qt::ItemIsDropEnabled)
+ << modelMoves
+ << replaced;
+ }
+ }
+}
+
+/*
+ Test moving of items items via drag'n'drop.
+
+ This should reorder items when an item is dropped in between two items,
+ or - if items can be dropped on - replace the content of the drop target.
+
+ Test QListView in both icon and list view modes.
+
+ See QTBUG-67440, QTBUG-83084, QTBUG-87057
+*/
void tst_QListView::internalDragDropMove()
{
const QString platform(QGuiApplication::platformName().toLower());
if (platform != QLatin1String("xcb"))
QSKIP("Need a window system with proper DnD support via injected mouse events");
- // on an internal move, the item was deleted which should not happen
- // see QTBUG-67440
+ QFETCH(QListView::ViewMode, viewMode);
+ QFETCH(QAbstractItemView::DragDropMode, dragDropMode);
+ QFETCH(Qt::DropActions, supportedDropActions);
+ QFETCH(Qt::DropAction, defaultDropAction);
+ QFETCH(Qt::ItemFlags, itemFlags);
+ QFETCH(bool, modelMoves);
+ QFETCH(QStringList, expectedData);
+
+ class ItemModel : public QStringListModel
+ {
+ public:
+ ItemModel()
+ {
+ QStringList list;
+ for (int i = 0; i < 10; ++i) {
+ list << QString::number(i);
+ }
+ setStringList(list);
+ }
+
+ Qt::DropActions supportedDropActions() const override { return m_supportedDropActions; }
+ Qt::ItemFlags flags(const QModelIndex &index) const override
+ {
+ if (!index.isValid())
+ return QStringListModel::flags(index);
+ return m_itemFlags;
+ }
+ bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count,
+ const QModelIndex &destinationParent, int destinationChild) override
+ {
+ if (!m_modelMoves) // many models don't implement moveRows
+ return false;
+ return QStringListModel::moveRows(sourceParent, sourceRow, count,
+ destinationParent, destinationChild);
+ }
+ bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &values) override
+ {
+ return QStringListModel::setData(index, values.value(Qt::DisplayRole), Qt::DisplayRole);
+ }
+ QVariant data(const QModelIndex &index, int role) const override
+ {
+ if (role == Qt::DecorationRole)
+ return QColor(Qt::GlobalColor(index.row() + 1));
+ return QStringListModel::data(index, role);
+ }
+ QMap<int, QVariant> itemData(const QModelIndex &index) const override
+ {
+ auto item = QStringListModel::itemData(index);
+ item[Qt::DecorationRole] = data(index, Qt::DecorationRole);
+ return item;
+ }
+
+ Qt::DropActions m_supportedDropActions;
+ Qt::ItemFlags m_itemFlags;
+ bool m_modelMoves;
+ };
+
+ ItemModel data;
+ data.m_supportedDropActions = supportedDropActions;
+ data.m_itemFlags = itemFlags;
+ data.m_modelMoves = modelMoves;
- QStandardItemModel data(0, 1);
- QPixmap pixmap(32, 32);
- for (int i = 0; i < 10; ++i) {
- pixmap.fill(Qt::GlobalColor(i + 1));
- data.appendRow(new QStandardItem(QIcon(pixmap), QString::number(i)));
- }
QItemSelectionModel selections(&data);
PublicListView list;
list.setWindowTitle(QTest::currentTestFunction());
- list.setViewMode(QListView::IconMode);
- list.setDefaultDropAction(Qt::MoveAction);
+ list.setViewMode(viewMode);
+ list.setDragDropMode(dragDropMode);
+ list.setDefaultDropAction(defaultDropAction);
list.setModel(&data);
list.setSelectionModel(&selections);
- list.resize(300, 300);
+ int itemHeight = list.sizeHintForIndex(data.index(1, 0)).height();
+ list.resize(300, 15 * itemHeight);
list.show();
selections.select(data.index(1, 0), QItemSelectionModel::Select);
+ auto getSelectedTexts = [&]() -> QStringList {
+ QStringList selectedTexts;
+ for (auto index : selections.selectedIndexes())
+ selectedTexts << data.itemData(index).value(Qt::DisplayRole).toString();
+ return selectedTexts;
+ };
QVERIFY(QTest::qWaitForWindowExposed(&list));
-
// execute as soon as the eventloop is running again
// which is the case inside list.startDrag()
- QTimer::singleShot(0, [&list]()
+ QTimer::singleShot(0, [&]()
{
- const QPoint pos = list.rect().center();
- QMouseEvent mouseMove(QEvent::MouseMove, pos, list.mapToGlobal(pos), Qt::NoButton, {}, {});
+ QPoint droppos;
+ // take into account subtle differences between icon and list mode in QListView's drop placement
+ if (itemFlags & Qt::ItemIsDropEnabled)
+ droppos = list.rectForIndex(data.index(5, 0)).center();
+ else if (viewMode == QListView::IconMode)
+ droppos = list.rectForIndex(data.index(9, 0)).bottomRight() + QPoint(30, 30);
+ else
+ droppos = list.rectForIndex(data.index(9, 0)).bottomRight();
+
+ QMouseEvent mouseMove(QEvent::MouseMove, droppos, list.mapToGlobal(droppos), Qt::NoButton, {}, {});
QCoreApplication::sendEvent(&list, &mouseMove);
- QMouseEvent mouseRelease(QEvent::MouseButtonRelease, pos, list.mapToGlobal(pos), Qt::LeftButton, {}, {});
+ QMouseEvent mouseRelease(QEvent::MouseButtonRelease, droppos, list.mapToGlobal(droppos), Qt::LeftButton, {}, {});
QCoreApplication::sendEvent(&list, &mouseRelease);
});
- const int expectedCount = data.rowCount();
- list.startDrag(Qt::MoveAction|Qt::CopyAction);
- QCOMPARE(expectedCount, data.rowCount());
+
+ const QStringList expectedSelected = getSelectedTexts();
+
+ list.startDrag(Qt::MoveAction);
+
+ QCOMPARE(data.stringList(), expectedData);
+
+ // if the model doesn't implement moveRows, or if items are replaced, then selection is lost
+ if (modelMoves && !(itemFlags & Qt::ItemIsDropEnabled)) {
+ const QStringList actualSelected = getSelectedTexts();
+ QCOMPARE(actualSelected, expectedSelected);
+ }
}