Cut removes immediately; scrollbar click-to-jump
Ctrl-X now removes the track from the playlist right away (so it visibly leaves) and a paste re-inserts it at the target — replacing the earlier deferred-move design (dropped move_tracks_between_playlists and the MIME cut flag). A cut from the library is still just a copy. Separately, a left-click on any scrollbar trough now jumps to that spot instead of paging, via an app-wide ClickToJumpScrollStyle proxy. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -40,6 +40,7 @@ class PlaylistView(QWidget):
|
|||||||
self.table.tracks_dropped.connect(self._on_tracks_dropped)
|
self.table.tracks_dropped.connect(self._on_tracks_dropped)
|
||||||
self.table.files_dropped.connect(self._on_files_dropped)
|
self.table.files_dropped.connect(self._on_files_dropped)
|
||||||
self.table.remove_requested.connect(self._on_remove)
|
self.table.remove_requested.connect(self._on_remove)
|
||||||
|
self.table.cut_requested.connect(self._on_cut)
|
||||||
self.table.paste_requested.connect(self._on_paste)
|
self.table.paste_requested.connect(self._on_paste)
|
||||||
|
|
||||||
manager.playlist_content_changed.connect(self._on_content_changed)
|
manager.playlist_content_changed.connect(self._on_content_changed)
|
||||||
@ -133,6 +134,13 @@ class PlaylistView(QWidget):
|
|||||||
self._manager.remove_tracks_from_playlist(
|
self._manager.remove_tracks_from_playlist(
|
||||||
self._playlist.persistent_id, rows)
|
self._playlist.persistent_id, rows)
|
||||||
|
|
||||||
|
def _on_cut(self, rows):
|
||||||
|
# Cut removes immediately (so the track visibly leaves); the ids are
|
||||||
|
# already on the clipboard for a later paste to re-insert.
|
||||||
|
if self._playlist is not None:
|
||||||
|
self._manager.remove_tracks_from_playlist(
|
||||||
|
self._playlist.persistent_id, rows)
|
||||||
|
|
||||||
def _on_paste(self):
|
def _on_paste(self):
|
||||||
if self._playlist is None:
|
if self._playlist is None:
|
||||||
return
|
return
|
||||||
@ -140,24 +148,8 @@ class PlaylistView(QWidget):
|
|||||||
if not payload:
|
if not payload:
|
||||||
return
|
return
|
||||||
track_ids = payload.get("track_ids", [])
|
track_ids = payload.get("track_ids", [])
|
||||||
if not track_ids:
|
if track_ids:
|
||||||
return
|
# Paste above the selected track (None -> append).
|
||||||
pid = self._playlist.persistent_id
|
|
||||||
# Paste above the selected track (None -> append).
|
|
||||||
position = self.table.paste_anchor_row()
|
|
||||||
src_pid = payload.get("source_playlist", "")
|
|
||||||
|
|
||||||
# A cut from a real playlist is a *move* (no duplicate prompt); a copy
|
|
||||||
# — or a cut from the library, which has nothing to remove — just adds.
|
|
||||||
if payload.get("cut") and src_pid:
|
|
||||||
if src_pid == pid:
|
|
||||||
dest = position if position is not None else len(self._playlist.track_ids)
|
|
||||||
self._manager.move_tracks_in_playlist(
|
|
||||||
pid, payload.get("rows", []), dest)
|
|
||||||
else:
|
|
||||||
self._manager.move_tracks_between_playlists(
|
|
||||||
src_pid, payload.get("rows", []), pid, position)
|
|
||||||
QApplication.clipboard().clear() # a move pastes once
|
|
||||||
else:
|
|
||||||
add_tracks_with_dup_check(
|
add_tracks_with_dup_check(
|
||||||
self._manager, self, pid, track_ids, position)
|
self._manager, self, self._playlist.persistent_id,
|
||||||
|
track_ids, self.table.paste_anchor_row())
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import json
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from PyQt6.QtWidgets import (
|
from PyQt6.QtWidgets import (
|
||||||
QTableView, QAbstractItemView, QMenu, QApplication,
|
QTableView, QAbstractItemView, QMenu, QApplication, QProxyStyle, QStyle,
|
||||||
)
|
)
|
||||||
from PyQt6.QtCore import (
|
from PyQt6.QtCore import (
|
||||||
Qt, QAbstractTableModel, QModelIndex, QSortFilterProxyModel, pyqtSignal,
|
Qt, QAbstractTableModel, QModelIndex, QSortFilterProxyModel, pyqtSignal,
|
||||||
@ -28,6 +28,17 @@ SORT_ROLE = Qt.ItemDataRole.UserRole
|
|||||||
AUTOSCROLL_MARGIN = 28
|
AUTOSCROLL_MARGIN = 28
|
||||||
|
|
||||||
|
|
||||||
|
class ClickToJumpScrollStyle(QProxyStyle):
|
||||||
|
"""Makes a left-click on the scrollbar trough jump the thumb straight to
|
||||||
|
that spot (absolute position) instead of paging toward it a step at a time.
|
||||||
|
Wraps the default app style and only overrides the one hint."""
|
||||||
|
|
||||||
|
def styleHint(self, hint, option=None, widget=None, returnData=None):
|
||||||
|
if hint == QStyle.StyleHint.SH_ScrollBar_LeftClickAbsolutePosition:
|
||||||
|
return 1
|
||||||
|
return super().styleHint(hint, option, widget, returnData)
|
||||||
|
|
||||||
|
|
||||||
def autoscroll_direction(y: int, height: int, accepted: bool,
|
def autoscroll_direction(y: int, height: int, accepted: bool,
|
||||||
margin: int = AUTOSCROLL_MARGIN) -> int:
|
margin: int = AUTOSCROLL_MARGIN) -> int:
|
||||||
"""-1 to scroll up, +1 down, 0 to stop: based on how close the drag is to
|
"""-1 to scroll up, +1 down, 0 to stop: based on how close the drag is to
|
||||||
@ -153,10 +164,10 @@ def speaker_pixmap(playing: bool, color: QColor) -> QPixmap:
|
|||||||
|
|
||||||
|
|
||||||
def make_tracks_mime(track_ids: list[int], source_playlist: str = "",
|
def make_tracks_mime(track_ids: list[int], source_playlist: str = "",
|
||||||
rows: list[int] | None = None, cut: bool = False) -> QMimeData:
|
rows: list[int] | None = None) -> QMimeData:
|
||||||
mime = QMimeData()
|
mime = QMimeData()
|
||||||
payload = {"track_ids": track_ids, "source_playlist": source_playlist,
|
payload = {"track_ids": track_ids, "source_playlist": source_playlist,
|
||||||
"rows": rows or [], "cut": cut}
|
"rows": rows or []}
|
||||||
mime.setData(TRACKS_MIME, json.dumps(payload).encode())
|
mime.setData(TRACKS_MIME, json.dumps(payload).encode())
|
||||||
return mime
|
return mime
|
||||||
|
|
||||||
@ -321,6 +332,7 @@ class TrackTableView(QTableView):
|
|||||||
tracks_dropped = pyqtSignal(list, object) # track ids, insert row or None
|
tracks_dropped = pyqtSignal(list, object) # track ids, insert row or None
|
||||||
files_dropped = pyqtSignal(list, object) # paths, insert row or None
|
files_dropped = pyqtSignal(list, object) # paths, insert row or None
|
||||||
remove_requested = pyqtSignal(list) # manual rows
|
remove_requested = pyqtSignal(list) # manual rows
|
||||||
|
cut_requested = pyqtSignal(list) # source rows to remove now
|
||||||
paste_requested = pyqtSignal()
|
paste_requested = pyqtSignal()
|
||||||
info_requested = pyqtSignal(list) # selected track ids
|
info_requested = pyqtSignal(list) # selected track ids
|
||||||
tracks_changed = pyqtSignal() # displayed track set changed
|
tracks_changed = pyqtSignal() # displayed track set changed
|
||||||
@ -487,20 +499,24 @@ class TrackTableView(QTableView):
|
|||||||
# ---- copy / paste ----
|
# ---- copy / paste ----
|
||||||
|
|
||||||
def copy_selection(self):
|
def copy_selection(self):
|
||||||
self._put_on_clipboard(cut=False)
|
|
||||||
|
|
||||||
def cut_selection(self):
|
|
||||||
"""Mark the selection for a move: a later paste removes them from this
|
|
||||||
source (a real playlist) and inserts them at the paste target."""
|
|
||||||
self._put_on_clipboard(cut=True)
|
|
||||||
|
|
||||||
def _put_on_clipboard(self, cut: bool):
|
|
||||||
ids = self.selected_track_ids()
|
ids = self.selected_track_ids()
|
||||||
if not ids:
|
if not ids:
|
||||||
return
|
return
|
||||||
mime = make_tracks_mime(ids, self._source_playlist_id,
|
QApplication.clipboard().setMimeData(make_tracks_mime(
|
||||||
self.selected_source_rows(), cut=cut)
|
ids, self._source_playlist_id, self.selected_source_rows()))
|
||||||
QApplication.clipboard().setMimeData(mime)
|
|
||||||
|
def cut_selection(self):
|
||||||
|
"""Copy the selection, then — in a playlist — remove it immediately so
|
||||||
|
it visibly leaves; a paste re-inserts it at the target. From the
|
||||||
|
library there's nothing to remove, so this is just a copy."""
|
||||||
|
ids = self.selected_track_ids()
|
||||||
|
if not ids:
|
||||||
|
return
|
||||||
|
rows = self.selected_source_rows()
|
||||||
|
QApplication.clipboard().setMimeData(
|
||||||
|
make_tracks_mime(ids, self._source_playlist_id, rows))
|
||||||
|
if self._playlist_mode and self._source_playlist_id:
|
||||||
|
self.cut_requested.emit(rows)
|
||||||
|
|
||||||
def paste_anchor_row(self):
|
def paste_anchor_row(self):
|
||||||
"""Source-model row to paste *above* — the first selected track — or
|
"""Source-model row to paste *above* — the first selected track — or
|
||||||
|
|||||||
@ -234,39 +234,6 @@ class LibraryManager(QObject):
|
|||||||
self._set_track_ids(pid, after)
|
self._set_track_ids(pid, after)
|
||||||
self._push_track_ids("Reorder Playlist", pid, before, after)
|
self._push_track_ids("Reorder Playlist", pid, before, after)
|
||||||
|
|
||||||
def move_tracks_between_playlists(self, src_pid: str, src_rows: list[int],
|
|
||||||
dest_pid: str, dest_position: int | None = None):
|
|
||||||
"""Cut tracks out of one playlist and insert them into another as a
|
|
||||||
single undoable move (Ctrl-X / Ctrl-V across playlists). For the same
|
|
||||||
playlist, use move_tracks_in_playlist instead."""
|
|
||||||
src = self.library.playlists.get(src_pid)
|
|
||||||
dest = self.library.playlists.get(dest_pid)
|
|
||||||
if (not src or not dest or src_pid == dest_pid
|
|
||||||
or dest.playlist_type == PlaylistType.FOLDER):
|
|
||||||
return
|
|
||||||
src_before = list(src.track_ids)
|
|
||||||
dest_before = list(dest.track_ids)
|
|
||||||
rows = sorted(set(r for r in src_rows if 0 <= r < len(src_before)))
|
|
||||||
if not rows:
|
|
||||||
return
|
|
||||||
moving = [src_before[r] for r in rows]
|
|
||||||
src_after = [tid for i, tid in enumerate(src_before) if i not in rows]
|
|
||||||
dest_after = list(dest_before)
|
|
||||||
if dest_position is None or dest_position >= len(dest_after):
|
|
||||||
dest_after.extend(moving)
|
|
||||||
else:
|
|
||||||
dest_after[dest_position:dest_position] = moving
|
|
||||||
|
|
||||||
def apply(src_ids, dest_ids):
|
|
||||||
self._set_track_ids(src_pid, src_ids)
|
|
||||||
self._set_track_ids(dest_pid, dest_ids)
|
|
||||||
|
|
||||||
apply(src_after, dest_after)
|
|
||||||
self.undo_stack.push(Command(
|
|
||||||
"Move to Playlist",
|
|
||||||
undo=lambda: apply(src_before, dest_before),
|
|
||||||
redo=lambda: apply(src_after, dest_after)))
|
|
||||||
|
|
||||||
def _set_track_ids(self, pid: str, ids: list[int]):
|
def _set_track_ids(self, pid: str, ids: list[int]):
|
||||||
playlist = self.library.playlists.get(pid)
|
playlist = self.library.playlists.get(pid)
|
||||||
if playlist is None:
|
if playlist is None:
|
||||||
|
|||||||
@ -103,6 +103,7 @@ def run_gui(data_dir: Path, files: list[Path], qt_args: list[str] | None = None)
|
|||||||
from lintunes.preferences import Preferences
|
from lintunes.preferences import Preferences
|
||||||
from lintunes.lastfm import LastFm
|
from lintunes.lastfm import LastFm
|
||||||
from lintunes.gui.main_window import MainWindow
|
from lintunes.gui.main_window import MainWindow
|
||||||
|
from lintunes.gui.track_table import ClickToJumpScrollStyle
|
||||||
from lintunes.mpris import setup_mpris
|
from lintunes.mpris import setup_mpris
|
||||||
|
|
||||||
resolve_conflicts(data_dir)
|
resolve_conflicts(data_dir)
|
||||||
@ -111,6 +112,10 @@ def run_gui(data_dir: Path, files: list[Path], qt_args: list[str] | None = None)
|
|||||||
app = QApplication([sys.argv[0]] + (qt_args or []))
|
app = QApplication([sys.argv[0]] + (qt_args or []))
|
||||||
app.setApplicationName("LinTunes")
|
app.setApplicationName("LinTunes")
|
||||||
app.setDesktopFileName("lintunes")
|
app.setDesktopFileName("lintunes")
|
||||||
|
# Click anywhere on a scrollbar trough to jump there, not page a step at a
|
||||||
|
# time. Set app-wide (before apply_theme's stylesheet wraps the style) so
|
||||||
|
# the hint is honored everywhere, including the track list and sidebar.
|
||||||
|
app.setStyle(ClickToJumpScrollStyle())
|
||||||
|
|
||||||
prefs = Preferences(data_dir)
|
prefs = Preferences(data_dir)
|
||||||
theme.apply_theme(app, prefs)
|
theme.apply_theme(app, prefs)
|
||||||
|
|||||||
@ -1,17 +1,18 @@
|
|||||||
"""Round 12: cut (Ctrl-X) + paste-above-selection.
|
"""Round 12: cut (Ctrl-X), paste-above-selection, scrollbar click-to-jump.
|
||||||
|
|
||||||
- The clipboard MIME carries a `cut` flag; a cut from a real playlist is a
|
- Cut copies the selection to the clipboard and, in a playlist, removes it
|
||||||
*move* on paste, a copy (or a cut from the library) just adds.
|
*immediately* (so it visibly leaves); a paste re-inserts it at the target.
|
||||||
|
A cut from the library has nothing to remove, so it's just a copy.
|
||||||
- Paste inserts *above the currently selected track*, not at the end.
|
- Paste inserts *above the currently selected track*, not at the end.
|
||||||
- A cross-playlist move is one undo step (move_tracks_between_playlists).
|
- Clicking the scrollbar trough jumps there instead of paging a step.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from PyQt6.QtWidgets import QApplication
|
from PyQt6.QtWidgets import QApplication, QStyle
|
||||||
|
|
||||||
from lintunes.models import Library, Track
|
from lintunes.models import Library, Track
|
||||||
from lintunes.library_manager import LibraryManager
|
from lintunes.library_manager import LibraryManager
|
||||||
from lintunes.gui.track_table import (
|
from lintunes.gui.track_table import (
|
||||||
TrackTableView, make_tracks_mime, parse_tracks_mime)
|
TrackTableView, make_tracks_mime, parse_tracks_mime, ClickToJumpScrollStyle)
|
||||||
from lintunes.gui.playlist_view import PlaylistView
|
from lintunes.gui.playlist_view import PlaylistView
|
||||||
|
|
||||||
|
|
||||||
@ -28,22 +29,10 @@ def _select_source_row(table, source_row):
|
|||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# MIME + table helpers
|
# clipboard helpers + cut signalling
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
|
|
||||||
class TestMimeAndHelpers:
|
class TestCutSelection:
|
||||||
def test_cut_flag_round_trips(self):
|
|
||||||
payload = parse_tracks_mime(
|
|
||||||
make_tracks_mime([1, 2], "PID", [0, 1], cut=True))
|
|
||||||
assert payload["cut"] is True
|
|
||||||
assert payload["track_ids"] == [1, 2]
|
|
||||||
assert payload["source_playlist"] == "PID"
|
|
||||||
assert payload["rows"] == [0, 1]
|
|
||||||
|
|
||||||
def test_copy_defaults_to_not_cut(self):
|
|
||||||
payload = parse_tracks_mime(make_tracks_mime([1]))
|
|
||||||
assert payload["cut"] is False
|
|
||||||
|
|
||||||
def test_paste_anchor_row(self, qapp, tmp_path):
|
def test_paste_anchor_row(self, qapp, tmp_path):
|
||||||
manager = _manager(tmp_path)
|
manager = _manager(tmp_path)
|
||||||
table = TrackTableView(playlist_mode=True)
|
table = TrackTableView(playlist_mode=True)
|
||||||
@ -52,51 +41,51 @@ class TestMimeAndHelpers:
|
|||||||
_select_source_row(table, 1)
|
_select_source_row(table, 1)
|
||||||
assert table.paste_anchor_row() == 1
|
assert table.paste_anchor_row() == 1
|
||||||
|
|
||||||
def test_cut_selection_marks_clipboard(self, qapp, tmp_path):
|
def test_cut_in_playlist_copies_and_requests_removal(self, qapp, tmp_path):
|
||||||
manager = _manager(tmp_path)
|
manager = _manager(tmp_path)
|
||||||
table = TrackTableView(playlist_mode=True)
|
table = TrackTableView(playlist_mode=True)
|
||||||
table.set_source_playlist("PID")
|
table.set_source_playlist("PID")
|
||||||
table.set_tracks([manager.library.tracks[i] for i in (1, 2, 3)])
|
table.set_tracks([manager.library.tracks[i] for i in (1, 2, 3)])
|
||||||
|
removed = []
|
||||||
|
table.cut_requested.connect(removed.append)
|
||||||
|
|
||||||
_select_source_row(table, 0)
|
_select_source_row(table, 0)
|
||||||
table.cut_selection()
|
table.cut_selection()
|
||||||
|
|
||||||
|
assert removed == [[0]] # asked the view to remove row 0 immediately
|
||||||
|
payload = parse_tracks_mime(QApplication.clipboard().mimeData())
|
||||||
|
assert payload["track_ids"] == [1]
|
||||||
|
|
||||||
|
def test_cut_in_library_is_just_a_copy(self, qapp, tmp_path):
|
||||||
|
manager = _manager(tmp_path)
|
||||||
|
table = TrackTableView(playlist_mode=False) # library
|
||||||
|
table.set_tracks([manager.library.tracks[i] for i in (1, 2, 3)])
|
||||||
|
removed = []
|
||||||
|
table.cut_requested.connect(removed.append)
|
||||||
|
|
||||||
|
_select_source_row(table, 0)
|
||||||
|
table.cut_selection()
|
||||||
|
|
||||||
|
assert removed == [] # nothing to remove from the library
|
||||||
payload = parse_tracks_mime(QApplication.clipboard().mimeData())
|
payload = parse_tracks_mime(QApplication.clipboard().mimeData())
|
||||||
assert payload["cut"] is True
|
|
||||||
assert payload["track_ids"] == [1]
|
assert payload["track_ids"] == [1]
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# manager: single-undo cross-playlist move
|
# scrollbar click-to-jump
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
|
|
||||||
class TestMoveBetweenPlaylists:
|
class TestScrollbarJump:
|
||||||
def test_move_and_single_undo(self, tmp_path):
|
def test_style_hint_enables_absolute_position(self, qapp):
|
||||||
manager = _manager(tmp_path)
|
"""Applied app-wide in main.run_gui via app.setStyle(); here we just
|
||||||
a = manager.create_playlist("A")
|
confirm the proxy flips the left-click-jump hint on."""
|
||||||
b = manager.create_playlist("B")
|
style = ClickToJumpScrollStyle()
|
||||||
manager.add_tracks_to_playlist(a.persistent_id, [1, 2, 3])
|
assert style.styleHint(
|
||||||
manager.add_tracks_to_playlist(b.persistent_id, [4])
|
QStyle.StyleHint.SH_ScrollBar_LeftClickAbsolutePosition) == 1
|
||||||
|
|
||||||
manager.move_tracks_between_playlists(
|
|
||||||
a.persistent_id, [0, 1], b.persistent_id, 0)
|
|
||||||
|
|
||||||
assert a.track_ids == [3]
|
|
||||||
assert b.track_ids == [1, 2, 4]
|
|
||||||
|
|
||||||
manager.undo_stack.undo() # one step reverts both playlists
|
|
||||||
assert a.track_ids == [1, 2, 3]
|
|
||||||
assert b.track_ids == [4]
|
|
||||||
|
|
||||||
def test_same_playlist_is_a_noop(self, tmp_path):
|
|
||||||
manager = _manager(tmp_path)
|
|
||||||
a = manager.create_playlist("A")
|
|
||||||
manager.add_tracks_to_playlist(a.persistent_id, [1, 2, 3])
|
|
||||||
manager.move_tracks_between_playlists(
|
|
||||||
a.persistent_id, [0], a.persistent_id, 2)
|
|
||||||
assert a.track_ids == [1, 2, 3]
|
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# PlaylistView paste integration
|
# PlaylistView integration
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
|
|
||||||
class TestPasteIntegration:
|
class TestPasteIntegration:
|
||||||
@ -113,24 +102,23 @@ class TestPasteIntegration:
|
|||||||
|
|
||||||
assert b.track_ids == [1, 2, 4]
|
assert b.track_ids == [1, 2, 4]
|
||||||
|
|
||||||
def test_cut_paste_moves_across_playlists(self, qapp, tmp_path):
|
def test_cut_removes_now_then_paste_reinserts_elsewhere(self, qapp, tmp_path):
|
||||||
manager = _manager(tmp_path)
|
manager = _manager(tmp_path)
|
||||||
a = manager.create_playlist("A")
|
a = manager.create_playlist("A")
|
||||||
b = manager.create_playlist("B")
|
b = manager.create_playlist("B")
|
||||||
manager.add_tracks_to_playlist(a.persistent_id, [1, 2, 3])
|
manager.add_tracks_to_playlist(a.persistent_id, [1, 2, 3])
|
||||||
manager.add_tracks_to_playlist(b.persistent_id, [4])
|
manager.add_tracks_to_playlist(b.persistent_id, [4])
|
||||||
view = PlaylistView(manager)
|
view = PlaylistView(manager)
|
||||||
view.show_playlist(b)
|
|
||||||
|
|
||||||
QApplication.clipboard().setMimeData(
|
view.show_playlist(a)
|
||||||
make_tracks_mime([1, 2], a.persistent_id, [0, 1], cut=True))
|
_select_source_row(view.table, 0) # track 1
|
||||||
|
view.table.cut_selection()
|
||||||
|
assert a.track_ids == [2, 3] # gone immediately
|
||||||
|
|
||||||
|
view.show_playlist(b)
|
||||||
_select_source_row(view.table, 0) # above track 4
|
_select_source_row(view.table, 0) # above track 4
|
||||||
view._on_paste()
|
view._on_paste()
|
||||||
|
assert b.track_ids == [1, 4]
|
||||||
assert b.track_ids == [1, 2, 4]
|
|
||||||
assert a.track_ids == [3]
|
|
||||||
# The move consumes the clipboard so it can't be pasted again.
|
|
||||||
assert parse_tracks_mime(QApplication.clipboard().mimeData()) is None
|
|
||||||
|
|
||||||
def test_cut_paste_same_playlist_reorders(self, qapp, tmp_path):
|
def test_cut_paste_same_playlist_reorders(self, qapp, tmp_path):
|
||||||
manager = _manager(tmp_path)
|
manager = _manager(tmp_path)
|
||||||
@ -139,9 +127,10 @@ class TestPasteIntegration:
|
|||||||
view = PlaylistView(manager)
|
view = PlaylistView(manager)
|
||||||
view.show_playlist(a)
|
view.show_playlist(a)
|
||||||
|
|
||||||
QApplication.clipboard().setMimeData(
|
_select_source_row(view.table, 0) # track 1
|
||||||
make_tracks_mime([1], a.persistent_id, [0], cut=True))
|
view.table.cut_selection()
|
||||||
_select_source_row(view.table, 2) # above track 3
|
assert a.track_ids == [2, 3, 4]
|
||||||
view._on_paste()
|
|
||||||
|
|
||||||
|
_select_source_row(view.table, 1) # above track 3
|
||||||
|
view._on_paste()
|
||||||
assert a.track_ids == [2, 1, 3, 4]
|
assert a.track_ids == [2, 1, 3, 4]
|
||||||
|
|||||||
Reference in New Issue
Block a user