From c699daeceb4448c4545a67ffdba27bcb3b994114 Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Sat, 7 Apr 2018 08:59:06 +0200 Subject: [PATCH] QListView: do not delete item on internal move MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When an internal DnD occoures in IconMode, the item is moved to it's new place but the logic in filterStartDrag() has no clue about the fact that an internal move happend. Therefore the item gets deleted. Fix it by changing the event's drop action to Qt::CopyAction to avoid the deletion as it is done within QListWidget DnD code. [ChangeLog][QtWidgets][QListView] Do not delete item on internal move. Task-number: QTBUG-67440 Change-Id: I873d3c9fa76e107e108d9af0dcf8cecd1e18a18f Reviewed-by: Thorbjørn Lund Martsum --- src/widgets/itemviews/qlistview.cpp | 4 ++ .../itemviews/qlistview/tst_qlistview.cpp | 49 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/widgets/itemviews/qlistview.cpp b/src/widgets/itemviews/qlistview.cpp index 7fbbf39309..e5769940d4 100644 --- a/src/widgets/itemviews/qlistview.cpp +++ b/src/widgets/itemviews/qlistview.cpp @@ -2815,6 +2815,8 @@ bool QIconModeViewBase::filterStartDrag(Qt::DropActions supportedActions) drag->setHotSpot(dd->pressedPosition - rect.topLeft()); Qt::DropAction action = drag->exec(supportedActions, dd->defaultDropAction); draggedItems.clear(); + // for internal moves the action was set to Qt::CopyAction in + // filterDropEvent() to avoid the deletion here if (action == Qt::MoveAction) dd->clearOrRemove(); } @@ -2851,6 +2853,8 @@ bool QIconModeViewBase::filterDropEvent(QDropEvent *e) dd->stopAutoScroll(); draggedItems.clear(); dd->emitIndexesMoved(indexes); + // do not delete item on internal move, see filterStartDrag() + e->setDropAction(Qt::CopyAction); e->accept(); // we have handled the event // if the size has not grown, we need to check if it has shrinked if (contentsSize != contents) { diff --git a/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp b/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp index b60e19c474..5227db64ec 100644 --- a/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp +++ b/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp @@ -150,6 +150,7 @@ private slots: void taskQTBUG_51086_skippingIndexesInSelectedIndexes(); void taskQTBUG_47694_indexOutOfBoundBatchLayout(); void itemAlignment(); + void internalDragDropMove(); }; // Testing get/set functions @@ -2577,5 +2578,53 @@ void tst_QListView::itemAlignment() QVERIFY(w.visualRect(item1->index()).width() < w.visualRect(item2->index()).width()); } +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 + class QListViewWithPublicStartDrag : public QListView + { + public: + using QListView::startDrag; + }; + + 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); + QListViewWithPublicStartDrag list; + list.setWindowTitle(QTest::currentTestFunction()); + list.setViewMode(QListView::IconMode); + list.setDefaultDropAction(Qt::MoveAction); + list.setModel(&data); + list.setSelectionModel(&selections); + list.resize(300, 300); + list.show(); + selections.select(data.index(1, 0), QItemSelectionModel::Select); + QVERIFY(QTest::qWaitForWindowExposed(&list)); + + // execute as soon as the eventloop is running again + // which is the case inside list.startDrag() + QTimer::singleShot(0, [&list]() + { + const QPoint pos = list.rect().center(); + QMouseEvent mouseMove(QEvent::MouseMove, pos, list.mapToGlobal(pos), Qt::NoButton, 0, 0); + QApplication::sendEvent(&list, &mouseMove); + QMouseEvent mouseRelease(QEvent::MouseButtonRelease, pos, list.mapToGlobal(pos), Qt::LeftButton, 0, 0); + QApplication::sendEvent(&list, &mouseRelease); + }); + const int expectedCount = data.rowCount(); + list.startDrag(Qt::MoveAction|Qt::CopyAction); + QCOMPARE(expectedCount, data.rowCount()); +} + + QTEST_MAIN(tst_QListView) #include "tst_qlistview.moc"