summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Faure <[email protected]>2024-08-12 18:54:10 +0200
committerDavid Faure <[email protected]>2024-08-28 15:07:20 +0200
commit8e63cf282cb248ddd7fbc908766091659b3a0b22 (patch)
tree163f8e4bf949ad7a5506473363f9fa9ed9e8073b
parent7f01b62969d1734832ead0547904902ae0f1b5dd (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.cpp62
-rw-r--r--src/widgets/itemviews/qtableview.h4
-rw-r--r--src/widgets/itemviews/qtablewidget.cpp6
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;