diff --git a/app/playlists.py b/app/playlists.py index 5ca7ffa..bd93e04 100644 --- a/app/playlists.py +++ b/app/playlists.py @@ -189,8 +189,7 @@ class PlaylistTab(QTableWidget): self.resizeRowsToContents) # Drag and drop setup - # TODO: fix drag and drop for qt6 - self.setAcceptDrops(False) + self.setAcceptDrops(True) self.viewport().setAcceptDrops(True) self.setDragDropOverwriteMode(False) self.setDropIndicatorShown(True) @@ -232,41 +231,37 @@ class PlaylistTab(QTableWidget): if not event.source() == self: return # We don't accept external drops - drop_row: int = self._drop_on(event) - - rows: List = sorted(set(item.row() for item in self.selectedItems())) - rows_to_move = [ - [QTableWidgetItem( - self.item(row_index, column_index) # type: ignore - ) for - column_index in range(self.columnCount())] - for row_index in rows - ] - for row_index in reversed(rows): - self.removeRow(row_index) - if row_index < drop_row: - drop_row -= 1 - - for row_index, data in enumerate(rows_to_move): - row_index += drop_row - self.insertRow(row_index) - for column_index, column_data in enumerate(data): - self.setItem(row_index, column_index, column_data) + row_set = set([mi.row() for mi in self.selectedIndexes()]) + targetRow = self.indexAt(event.position().toPoint()).row() + row_set.discard(targetRow) + rows = list(sorted(row_set)) + if not rows: + return + if targetRow == -1: + targetRow = self.rowCount() + for _ in range(len(rows)): + self.insertRow(targetRow) + rowMapping = dict() # Src row to target row. + for idx, row in enumerate(rows): + if row < targetRow: + rowMapping[row] = targetRow + idx + else: + rowMapping[row + len(rows)] = targetRow + idx + colCount = self.columnCount() + for srcRow, tgtRow in sorted(rowMapping.items()): + for col in range(0, colCount): + self.setItem(tgtRow, col, self.takeItem(srcRow, col)) + for row in reversed(sorted(rowMapping.keys())): + self.removeRow(row) event.accept() - # The above doesn't handle column spans, which we use in note - # rows. Check and fix: - for row in range(drop_row, drop_row + len(rows_to_move)): - if not self._get_row_track_id(row): - self.setSpan(row, HEADER_NOTES_COLUMN, 1, len(columns)) # Scroll to drop zone - self.scrollToItem(self.item(row, 1)) + self.scrollToItem(self.item(targetRow, 1), + QAbstractItemView.ScrollHint.PositionAtCenter) # Reset drag mode to allow row selection by dragging self.setDragEnabled(False) - super().dropEvent(event) - with Session() as session: self.save_playlist(session) self._update_start_end_times(session) @@ -291,8 +286,7 @@ class PlaylistTab(QTableWidget): """ if self.selectedItems(): - # TODO: fix drag and drop - self.setDragEnabled(False) + self.setDragEnabled(True) else: self.setDragEnabled(False) super().mouseReleaseEvent(event) @@ -1152,19 +1146,6 @@ class PlaylistTab(QTableWidget): self._update_start_end_times(session) - def _drop_on(self, event): - """ - https://stackoverflow.com/questions/26227885/drag-and-drop-rows-within-qtablewidget - """ - - position = event.position().toPoint() - index = self.indexAt(position) - if not index.isValid(): - return self.rowCount() - - return (index.row() + 1 if self._is_below(position, index) - else index.row()) - def _find_next_track_row(self, session: scoped_session, starting_row: Optional[int] = None) \ -> Optional[int]: @@ -1416,23 +1397,6 @@ class PlaylistTab(QTableWidget): info.setDefaultButton(QMessageBox.StandardButton.Cancel) info.exec() - def _is_below(self, pos, index): # review - """ - https://stackoverflow.com/questions/26227885/drag-and-drop-rows-within-qtablewidget - """ - - rect = self.visualRect(index) - margin = 2 - if pos.y() - rect.top() < margin: - return False - elif rect.bottom() - pos.y() < margin: - return True - return ( - rect.contains(pos, True) and not - (int(self.model().flags(index)) & Qt.ItemIsDropEnabled) - and pos.y() >= rect.center().y() # noqa W503 - ) - def _look_up_row(self, website: str) -> None: """ If there is a selected row and it is a track row,