Snapshot of the existing codebase before working through the TASKS.md backlog. Real library data (data/) and the iTunes import fixture (itunes-test-library/) are gitignored. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
72 lines
1.9 KiB
Python
72 lines
1.9 KiB
Python
"""A tiny closure-based undo/redo stack.
|
|
|
|
Each Command stores a human label plus two zero-arg callables — one to undo the
|
|
change and one to redo it. The change is assumed to have already been applied by
|
|
the caller, so push() only records it. The stack keeps a bounded history (10
|
|
levels by default); pushing past the limit silently drops the oldest entry.
|
|
"""
|
|
import collections
|
|
|
|
from PyQt6.QtCore import QObject, pyqtSignal
|
|
|
|
|
|
class Command:
|
|
def __init__(self, label: str, undo, redo):
|
|
self.label = label
|
|
self._undo = undo
|
|
self._redo = redo
|
|
|
|
def undo(self):
|
|
self._undo()
|
|
|
|
def redo(self):
|
|
self._redo()
|
|
|
|
|
|
class UndoStack(QObject):
|
|
changed = pyqtSignal()
|
|
|
|
def __init__(self, maxlen: int = 10, parent=None):
|
|
super().__init__(parent)
|
|
self._undo: collections.deque[Command] = collections.deque(maxlen=maxlen)
|
|
self._redo: list[Command] = []
|
|
|
|
def push(self, command: Command):
|
|
"""Record an already-applied change; clears the redo branch."""
|
|
self._undo.append(command)
|
|
self._redo.clear()
|
|
self.changed.emit()
|
|
|
|
def undo(self):
|
|
if not self._undo:
|
|
return
|
|
command = self._undo.pop()
|
|
command.undo()
|
|
self._redo.append(command)
|
|
self.changed.emit()
|
|
|
|
def redo(self):
|
|
if not self._redo:
|
|
return
|
|
command = self._redo.pop()
|
|
command.redo()
|
|
self._undo.append(command)
|
|
self.changed.emit()
|
|
|
|
def can_undo(self) -> bool:
|
|
return bool(self._undo)
|
|
|
|
def can_redo(self) -> bool:
|
|
return bool(self._redo)
|
|
|
|
def undo_label(self) -> str:
|
|
return self._undo[-1].label if self._undo else ""
|
|
|
|
def redo_label(self) -> str:
|
|
return self._redo[-1].label if self._redo else ""
|
|
|
|
def clear(self):
|
|
self._undo.clear()
|
|
self._redo.clear()
|
|
self.changed.emit()
|