diff options
author | David Faure <[email protected]> | 2024-08-12 18:54:10 +0200 |
---|---|---|
committer | David Faure <[email protected]> | 2024-08-28 15:07:20 +0200 |
commit | 8e63cf282cb248ddd7fbc908766091659b3a0b22 (patch) | |
tree | 163f8e4bf949ad7a5506473363f9fa9ed9e8073b | |
parent | 7f01b62969d1734832ead0547904902ae0f1b5dd (diff) |
QTableView: implement moving rows by drag-n-drop
This is only available when calling setDragDropOverwriteMode(false)
so this makes no difference in most applications (see implementation
of QAbstractItemView::position).
QTableWidget::dropEvent() was assuming we were dropping onto items
without checking that this was the case, this is fixed here,
and QTableView::dropEvent() is added to handle moving rows,
with very similar code to QListView::dropEvent().
Task-number: QTBUG-13873
Task-number: QTBUG-101475
Pick-to: 6.8
Change-Id: I96e75da2c0abf6f568edba461735905dee5cb84f
Reviewed-by: Richard Moe Gustavsen <[email protected]>
-rw-r--r-- | src/widgets/itemviews/qtableview.cpp | 62 | ||||
-rw-r--r-- | src/widgets/itemviews/qtableview.h | 4 | ||||
-rw-r--r-- | src/widgets/itemviews/qtablewidget.cpp | 6 |
3 files changed, 69 insertions, 3 deletions
diff --git a/src/widgets/itemviews/qtableview.cpp b/src/widgets/itemviews/qtableview.cpp index db62f13355d..2d26ed74c0b 100644 --- a/src/widgets/itemviews/qtableview.cpp +++ b/src/widgets/itemviews/qtableview.cpp @@ -3108,6 +3108,68 @@ void QTableView::timerEvent(QTimerEvent *event) QAbstractItemView::timerEvent(event); } +#if QT_CONFIG(draganddrop) +/*! \reimp */ +void QTableView::dropEvent(QDropEvent *event) +{ + Q_D(QTableView); + if (event->source() == this && (event->dropAction() == Qt::MoveAction || + dragDropMode() == QAbstractItemView::InternalMove)) { + QModelIndex topIndex; + int col = -1; + int row = -1; + // check whether a subclass has already accepted the event, ie. moved the data + if (!event->isAccepted() && d->dropOn(event, &row, &col, &topIndex) && !topIndex.isValid() && col != -1) { + // Drop between items (reordering) - can only happen with setDragDropOverwriteMode(false) + const QModelIndexList indexes = selectedIndexes(); + QList<QPersistentModelIndex> persIndexes; + persIndexes.reserve(indexes.size()); + + bool topIndexDropped = false; + for (const auto &index : indexes) { + // Reorder entire rows + QPersistentModelIndex firstColIndex = index.siblingAtColumn(0); + if (!persIndexes.contains(firstColIndex)) + persIndexes.append(firstColIndex); + if (index.row() == topIndex.row()) { + topIndexDropped = true; + break; + } + } + if (!topIndexDropped) { + std::sort(persIndexes.begin(), persIndexes.end()); // The dropped items will remain in the same visual order. + + QPersistentModelIndex dropRow = model()->index(row, col, topIndex); + + int r = row == -1 ? model()->rowCount() : (dropRow.row() >= 0 ? dropRow.row() : row); + bool dataMoved = false; + for (int i = 0; i < persIndexes.size(); ++i) { + const QPersistentModelIndex &pIndex = persIndexes.at(i); + // only generate a move when not same row or behind itself + if (r != pIndex.row() && r != pIndex.row() + 1) { + // try to move (preserves selection) + dataMoved |= model()->moveRow(QModelIndex(), pIndex.row(), QModelIndex(), r); + if (!dataMoved) // can't move - abort and let QAbstractItemView handle this + break; + } else { + // move onto itself is blocked, don't delete anything + dataMoved = true; + } + r = pIndex.row() + 1; // Dropped items are inserted contiguously and in the right order. + } + if (dataMoved) + event->accept(); + } + } + } + + if (!event->isAccepted()) { + // moveRows not implemented, fall back to default + QAbstractItemView::dropEvent(event); + } +} +#endif + /*! This slot is called to change the index of the given \a row in the table view. The old index is specified by \a oldIndex, and the new diff --git a/src/widgets/itemviews/qtableview.h b/src/widgets/itemviews/qtableview.h index eff0ea3502e..dc5af4f4287 100644 --- a/src/widgets/itemviews/qtableview.h +++ b/src/widgets/itemviews/qtableview.h @@ -114,6 +114,10 @@ protected: void timerEvent(QTimerEvent *event) override; +#if QT_CONFIG(draganddrop) + void dropEvent(QDropEvent *event) override; +#endif + int horizontalOffset() const override; int verticalOffset() const override; QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override; diff --git a/src/widgets/itemviews/qtablewidget.cpp b/src/widgets/itemviews/qtablewidget.cpp index 55ebe8f0cb8..3b82679c50c 100644 --- a/src/widgets/itemviews/qtablewidget.cpp +++ b/src/widgets/itemviews/qtablewidget.cpp @@ -2686,7 +2686,8 @@ void QTableWidget::dropEvent(QDropEvent *event) { int col = -1; int row = -1; // check whether a subclass has already accepted the event, ie. moved the data - if (!event->isAccepted() && d->dropOn(event, &row, &col, &topIndex)) { + if (!event->isAccepted() && d->dropOn(event, &row, &col, &topIndex) && row == -1 && col == -1) { + // Drop onto item const QModelIndexList indexes = selectedIndexes(); int top = INT_MAX; int left = INT_MAX; @@ -2694,7 +2695,6 @@ void QTableWidget::dropEvent(QDropEvent *event) { top = qMin(index.row(), top); left = qMin(index.column(), left); } - QList<QTableWidgetItem *> taken; const int indexesCount = indexes.size(); taken.reserve(indexesCount); @@ -2709,7 +2709,7 @@ void QTableWidget::dropEvent(QDropEvent *event) { event->accept(); } - // either we or a subclass accepted the move event, so assume that the data was + // either we or a subclass accepted the drop event, so assume that the data was // moved and that QAbstractItemView shouldn't remove the source when QDrag::exec returns if (event->isAccepted()) d->dropEventMoved = true; |