diff options
author | Volker Hilsheimer <volker.hilsheimer@qt.io> | 2020-09-29 23:39:53 +0200 |
---|---|---|
committer | Volker Hilsheimer <volker.hilsheimer@qt.io> | 2020-10-16 13:53:03 +0200 |
commit | 0f1008a5936c903ca9448193df7df6117e2c617b (patch) | |
tree | 0dbed87480f28495ea87323c7b0a4f820fbf9fdb /src/widgets | |
parent | faf7fd577f30a6be4cc2b77bb6ab8b5baf3e4fa3 (diff) |
QAbstractItemView: don't lose items if model only allows MoveAction
If a model only allows MoveAction, then calls in the view/widget subclasses'
dropEvent implementation to set the event's drop action to CopyAction
will fail. QAbstractItemView will then remove the item when QDrag::exec
returns.
Instead of abusing the event actions for this, store explicitly that the
dropEvent implementation already moved the item. If the flag is set,
don't remove the item.
In QListView, which uses moveRow to move items in the dropEvent handler,
handle the case that the model might not implement moveRows. In that
case, or when dropping an item onto another item (to overwrite data),
fall back to the default implementation of QAbstractItemView. Sadly, it
is impossible to know whether a model doesn't implement moveRows, or
whether the move failed for other reasons, so this requires a bit of
extra special case handling. QListView in IconMode is particularly odd
in that it moves the item in the view, but not in the model.
This follows up on fd894fd68edf3d67975cda8eb9dda43646887b0d and fixes
additional issues discovered during debugging. Extend the existing unit
test; since drag'n'drop runs a modal, native event loop on most systems,
it still only runs on the Xcb platform.
Change-Id: I6c5377e2b097c8080001afe904d6d3e4aed33df4
Pick-to: 5.15
Fixes: QTBUG-87057
Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
Diffstat (limited to 'src/widgets')
-rw-r--r-- | src/widgets/itemviews/qabstractitemview.cpp | 5 | ||||
-rw-r--r-- | src/widgets/itemviews/qabstractitemview_p.h | 1 | ||||
-rw-r--r-- | src/widgets/itemviews/qlistview.cpp | 33 | ||||
-rw-r--r-- | src/widgets/itemviews/qtablewidget.cpp | 2 | ||||
-rw-r--r-- | src/widgets/itemviews/qtreewidget.cpp | 2 |
5 files changed, 28 insertions, 15 deletions
diff --git a/src/widgets/itemviews/qabstractitemview.cpp b/src/widgets/itemviews/qabstractitemview.cpp index 198c26bd24..c037267f45 100644 --- a/src/widgets/itemviews/qabstractitemview.cpp +++ b/src/widgets/itemviews/qabstractitemview.cpp @@ -100,6 +100,7 @@ QAbstractItemViewPrivate::QAbstractItemViewPrivate() dragEnabled(false), dragDropMode(QAbstractItemView::NoDragDrop), overwrite(false), + dropEventMoved(false), dropIndicatorPosition(QAbstractItemView::OnItem), defaultDropAction(Qt::IgnoreAction), #endif @@ -3699,8 +3700,10 @@ void QAbstractItemView::startDrag(Qt::DropActions supportedActions) defaultDropAction = d->defaultDropAction; else if (supportedActions & Qt::CopyAction && dragDropMode() != QAbstractItemView::InternalMove) defaultDropAction = Qt::CopyAction; - if (drag->exec(supportedActions, defaultDropAction) == Qt::MoveAction) + d->dropEventMoved = false; + if (drag->exec(supportedActions, defaultDropAction) == Qt::MoveAction && !d->dropEventMoved) d->clearOrRemove(); + d->dropEventMoved = false; // Reset the drop indicator d->dropIndicatorRect = QRect(); d->dropIndicatorPosition = OnItem; diff --git a/src/widgets/itemviews/qabstractitemview_p.h b/src/widgets/itemviews/qabstractitemview_p.h index 3996257273..83012a650d 100644 --- a/src/widgets/itemviews/qabstractitemview_p.h +++ b/src/widgets/itemviews/qabstractitemview_p.h @@ -392,6 +392,7 @@ public: bool dragEnabled; QAbstractItemView::DragDropMode dragDropMode; bool overwrite; + bool dropEventMoved; QAbstractItemView::DropIndicatorPosition dropIndicatorPosition; Qt::DropAction defaultDropAction; #endif diff --git a/src/widgets/itemviews/qlistview.cpp b/src/widgets/itemviews/qlistview.cpp index 721b9aa0d3..5e400e5c8f 100644 --- a/src/widgets/itemviews/qlistview.cpp +++ b/src/widgets/itemviews/qlistview.cpp @@ -934,7 +934,7 @@ void QListView::dropEvent(QDropEvent *event) } } - if (!topIndexDropped) { + if (!topIndexDropped && !topIndex.isValid()) { std::sort(persIndexes.begin(), persIndexes.end()); // The dropped items will remain in the same visual order. QPersistentModelIndex dropRow = model()->index(row, col, topIndex); @@ -942,19 +942,29 @@ void QListView::dropEvent(QDropEvent *event) int r = row == -1 ? model()->rowCount() : (dropRow.row() >= 0 ? dropRow.row() : row); for (int i = 0; i < persIndexes.count(); ++i) { const QPersistentModelIndex &pIndex = persIndexes.at(i); - model()->moveRow(QModelIndex(), pIndex.row(), QModelIndex(), r); + if (r != pIndex.row()) { + // try to move (preserves selection) + d->dropEventMoved |= model()->moveRow(QModelIndex(), pIndex.row(), QModelIndex(), r); + if (!d->dropEventMoved) // can't move - abort and let QAbstractItemView handle this + break; + } else { + // move onto itself is blocked, don't delete anything + d->dropEventMoved = true; + } r = pIndex.row() + 1; // Dropped items are inserted contiguously and in the right order. } - - event->accept(); - // Don't want QAbstractItemView to delete it because it was "moved" we already did it - event->setDropAction(Qt::CopyAction); + if (d->dropEventMoved) + event->accept(); // data moved, nothing to be done in QAbstractItemView::dropEvent } } } - if (!d->commonListView->filterDropEvent(event)) + if (!d->commonListView->filterDropEvent(event) || !d->dropEventMoved) { + // icon view didn't move the data, and moveRows not implemented, so fall back to default + if (!d->dropEventMoved) + event->ignore(); QAbstractItemView::dropEvent(event); + } } /*! @@ -2887,12 +2897,13 @@ bool QIconModeViewBase::filterStartDrag(Qt::DropActions supportedActions) drag->setMimeData(dd->model->mimeData(indexes)); drag->setPixmap(pixmap); drag->setHotSpot(dd->pressedPosition - rect.topLeft()); + dd->dropEventMoved = false; 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) + // delete item, unless it has already been moved internally (see filterDropEvent) + if (action == Qt::MoveAction && !dd->dropEventMoved) dd->clearOrRemove(); + dd->dropEventMoved = false; } return true; } @@ -2927,8 +2938,6 @@ 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/src/widgets/itemviews/qtablewidget.cpp b/src/widgets/itemviews/qtablewidget.cpp index d09b07b674..f01d392545 100644 --- a/src/widgets/itemviews/qtablewidget.cpp +++ b/src/widgets/itemviews/qtablewidget.cpp @@ -2702,7 +2702,7 @@ void QTableWidget::dropEvent(QDropEvent *event) { event->accept(); // Don't want QAbstractItemView to delete it because it was "moved" we already did it - event->setDropAction(Qt::CopyAction); + d->dropEventMoved = true; } } diff --git a/src/widgets/itemviews/qtreewidget.cpp b/src/widgets/itemviews/qtreewidget.cpp index f5434100b9..a5d6e5a722 100644 --- a/src/widgets/itemviews/qtreewidget.cpp +++ b/src/widgets/itemviews/qtreewidget.cpp @@ -3392,7 +3392,7 @@ void QTreeWidget::dropEvent(QDropEvent *event) { event->accept(); // Don't want QAbstractItemView to delete it because it was "moved" we already did it - event->setDropAction(Qt::CopyAction); + d->dropEventMoved = true; } } |