"""Editor undo/redo commands for node tree operations.
Provides self-contained command objects for every undoable editor action.
Each command stores all data needed to execute, undo, and redo without
external state. Use with ``UndoStack.push()`` to record operations.
"""
from typing import Any
from simvx.core import BatchCommand, CallableCommand, Node, PropertyCommand
[docs]
class AddNodeCommand(CallableCommand):
"""Add a child node to the tree. Undo removes it."""
def __init__(self, parent: Node, child: Node, index: int = -1) -> None:
self._parent = parent
self._child = child
self._index = index
def do() -> None:
self._parent.add_child(self._child)
if self._index >= 0:
lst = self._parent.children._list
lst.remove(self._child)
lst.insert(self._index, self._child)
self._parent.children._dirty = True
def undo() -> None:
self._parent.remove_child(self._child)
super().__init__(do, undo, f"Add {child.name}")
[docs]
class RemoveNodeCommand(CallableCommand):
"""Remove a child from the tree. Undo re-adds it at original index."""
def __init__(self, parent: Node, child: Node) -> None:
self._parent = parent
self._child = child
self._index = list(parent.children).index(child)
def do() -> None:
self._parent.remove_child(self._child)
def undo() -> None:
self._parent.add_child(self._child)
lst = self._parent.children._list
lst.remove(self._child)
lst.insert(self._index, self._child)
self._parent.children._dirty = True
super().__init__(do, undo, f"Remove {child.name}")
[docs]
class ReparentCommand(CallableCommand):
"""Move a node from one parent to another. Undo restores original parent."""
def __init__(self, node: Node, new_parent: Node, old_parent: Node, old_index: int) -> None:
self._node = node
self._new_parent = new_parent
self._old_parent = old_parent
self._old_index = old_index
def do() -> None:
self._old_parent.remove_child(self._node)
self._new_parent.add_child(self._node)
def undo() -> None:
self._new_parent.remove_child(self._node)
self._old_parent.add_child(self._node)
lst = self._old_parent.children._list
lst.remove(self._node)
lst.insert(self._old_index, self._node)
self._old_parent.children._dirty = True
super().__init__(do, undo, f"Reparent {node.name}")
[docs]
class ReorderCommand(CallableCommand):
"""Change a child's index within its parent. Undo restores original index."""
def __init__(self, parent: Node, child: Node, new_index: int, old_index: int) -> None:
self._parent = parent
self._child = child
self._new_index = new_index
self._old_index = old_index
def _move(idx: int) -> None:
lst = self._parent.children._list
lst.remove(self._child)
lst.insert(idx, self._child)
self._parent.children._dirty = True
super().__init__(lambda: _move(self._new_index), lambda: _move(self._old_index), f"Reorder {child.name}")
[docs]
class RenameCommand(CallableCommand):
"""Rename a node. Undo restores the old name."""
def __init__(self, node: Node, new_name: str, old_name: str) -> None:
self._node = node
self._new_name = new_name
self._old_name = old_name
super().__init__(
lambda: setattr(self._node, "name", self._new_name),
lambda: setattr(self._node, "name", self._old_name),
f"Rename {old_name} \u2192 {new_name}",
)