fix: keep separators when removing their anchor file; add Copy name

- _remove_paths re-anchors a separator to the next surviving file (or a
  trailing separator) instead of dropping it when its anchor is removed;
  used by both Remove and Delete-from-disk. Removal now persists tabs.
- Add "Copy name" / "Copy N names" to the playlist context menu (basenames
  to clipboard, newline-joined for multi-select).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-06 15:55:42 +02:00
parent 1d49ce7cee
commit 13c4d3f7f6
+35 -15
View File
@@ -3228,6 +3228,32 @@ class PlaylistWidget(QListWidget):
self._rebuild()
self.separators_changed.emit()
def _remove_paths(self, paths: list[str]) -> None:
"""Remove *paths* from the list, re-anchoring separators so they survive."""
removing = set(paths)
# A separator anchored to a removed file moves to the next surviving
# file (or becomes a trailing separator) instead of vanishing.
for anchor in [p for p in paths if p in self._separators_before]:
target = self._SEP_END
seen = False
for p in self._paths:
if p == anchor:
seen = True
continue
if seen and p not in removing:
target = p
break
self._separators_before.discard(anchor)
self._separators_before.add(target)
for path in paths:
if path in self._path_set:
self._paths.remove(path)
self._path_set.discard(path)
self._done_set.discard(path)
self._done_counts.pop(path, None)
self._rebuild()
self.separators_changed.emit()
def _is_visible(self, path: str) -> bool:
if os.path.basename(path) in self._hidden_basenames:
return self._show_hidden
@@ -3441,13 +3467,14 @@ class PlaylistWidget(QListWidget):
menu = QMenu(self)
# Check if any selected files are hidden.
hidden_sel = [p for p in sel if os.path.basename(p) in self._hidden_basenames]
act_remove = act_hide = act_unhide = act_delete = None
act_remove = act_hide = act_unhide = act_delete = act_copy = None
act_sep_above = act_sep_below = None
act_disable_all = act_enable_all = None
disable_acts: dict = {}
enable_acts: dict = {}
if len(sel) == 1:
name = os.path.basename(sel[0])
act_copy = menu.addAction("Copy name")
act_remove = menu.addAction(f"Remove: {name}")
if hidden_sel:
act_unhide = menu.addAction(f"Unhide: {name}")
@@ -3487,6 +3514,7 @@ class PlaylistWidget(QListWidget):
"Remove separator below" if below_present else "Add separator below")
act_delete = menu.addAction(f"Delete from disk: {name}")
else:
act_copy = menu.addAction(f"Copy {len(sel)} names")
act_remove = menu.addAction(f"Remove {len(sel)} files")
if hidden_sel:
act_unhide = menu.addAction(f"Unhide {len(hidden_sel)} file(s)")
@@ -3498,14 +3526,11 @@ class PlaylistWidget(QListWidget):
chosen = menu.exec(event.globalPos())
if chosen is None:
return
if chosen == act_remove:
for path in sel:
if path in self._path_set:
self._paths.remove(path)
self._path_set.discard(path)
self._done_set.discard(path)
self._done_counts.pop(path, None)
self._rebuild()
if chosen == act_copy:
names = "\n".join(os.path.basename(p) for p in sel)
QApplication.clipboard().setText(names)
elif chosen == act_remove:
self._remove_paths(sel)
elif chosen == act_delete:
from PyQt6.QtWidgets import QMessageBox
names = "\n".join(os.path.basename(p) for p in sel)
@@ -3521,12 +3546,7 @@ class PlaylistWidget(QListWidget):
os.remove(path)
except OSError:
pass
if path in self._path_set:
self._paths.remove(path)
self._path_set.discard(path)
self._done_set.discard(path)
self._done_counts.pop(path, None)
self._rebuild()
self._remove_paths(sel)
elif chosen == act_hide:
self.hide_requested.emit(sel)
elif chosen == act_unhide: