"""Round 12: cut (Ctrl-X), paste-above-selection, scrollbar click-to-jump. - Cut copies the selection to the clipboard and, in a playlist, removes it *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. - Clicking the scrollbar trough jumps there instead of paging a step. """ from PyQt6.QtWidgets import QApplication, QStyle from lintunes.models import Library, Track from lintunes.library_manager import LibraryManager from lintunes.gui.track_table import ( TrackTableView, make_tracks_mime, parse_tracks_mime, ClickToJumpScrollStyle) from lintunes.gui.playlist_view import PlaylistView def _manager(tmp_path, n=4): library = Library(tracks={i: Track(track_id=i, name=f"T{i}") for i in range(1, n + 1)}) return LibraryManager(library, tmp_path) def _select_source_row(table, source_row): """Select the visual row backing a given source-model row (sort-proof).""" proxy_index = table.proxy.mapFromSource(table.model_.index(source_row, 0)) table.selectRow(proxy_index.row()) # -------------------------------------------------------------------------- # clipboard helpers + cut signalling # -------------------------------------------------------------------------- class TestCutSelection: def test_paste_anchor_row(self, qapp, tmp_path): manager = _manager(tmp_path) table = TrackTableView(playlist_mode=True) table.set_tracks([manager.library.tracks[i] for i in (1, 2, 3)]) assert table.paste_anchor_row() is None # nothing selected _select_source_row(table, 1) assert table.paste_anchor_row() == 1 def test_cut_in_playlist_copies_and_requests_removal(self, qapp, tmp_path): manager = _manager(tmp_path) table = TrackTableView(playlist_mode=True) table.set_source_playlist("PID") 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 == [[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()) assert payload["track_ids"] == [1] # -------------------------------------------------------------------------- # scrollbar click-to-jump # -------------------------------------------------------------------------- class TestScrollbarJump: def test_style_hint_enables_absolute_position(self, qapp): """Applied app-wide in main.run_gui via app.setStyle(); here we just confirm the proxy flips the left-click-jump hint on.""" style = ClickToJumpScrollStyle() assert style.styleHint( QStyle.StyleHint.SH_ScrollBar_LeftClickAbsolutePosition) == 1 # -------------------------------------------------------------------------- # PlaylistView integration # -------------------------------------------------------------------------- class TestPasteIntegration: def test_copy_paste_inserts_above_selection(self, qapp, tmp_path): manager = _manager(tmp_path) b = manager.create_playlist("B") manager.add_tracks_to_playlist(b.persistent_id, [4]) view = PlaylistView(manager) view.show_playlist(b) QApplication.clipboard().setMimeData(make_tracks_mime([1, 2])) _select_source_row(view.table, 0) # above track 4 view._on_paste() assert b.track_ids == [1, 2, 4] def test_cut_removes_now_then_paste_reinserts_elsewhere(self, qapp, tmp_path): manager = _manager(tmp_path) a = manager.create_playlist("A") b = manager.create_playlist("B") manager.add_tracks_to_playlist(a.persistent_id, [1, 2, 3]) manager.add_tracks_to_playlist(b.persistent_id, [4]) view = PlaylistView(manager) view.show_playlist(a) _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 view._on_paste() assert b.track_ids == [1, 4] def test_cut_paste_same_playlist_reorders(self, qapp, tmp_path): manager = _manager(tmp_path) a = manager.create_playlist("A") manager.add_tracks_to_playlist(a.persistent_id, [1, 2, 3, 4]) view = PlaylistView(manager) view.show_playlist(a) _select_source_row(view.table, 0) # track 1 view.table.cut_selection() assert a.track_ids == [2, 3, 4] _select_source_row(view.table, 1) # above track 3 view._on_paste() assert a.track_ids == [2, 1, 3, 4]