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
Change-Id: I96e75da2c0abf6f568edba461735905dee5cb84f
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
(cherry picked from commit 8e63cf282cb248ddd7fbc908766091659b3a0b22)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
bb10
David Faure 2024-08-12 18:54:10 +02:00 committed by Qt Cherry-pick Bot
parent 679ae93507
commit a9e464119a
3 changed files with 69 additions and 3 deletions

View File

@ -3115,6 +3115,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

View File

@ -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;

View File

@ -2722,7 +2722,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;
@ -2730,7 +2731,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);
@ -2745,7 +2745,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;