Source code for simvx.editor.commands

"""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}", )
[docs] class TransformCommand(BatchCommand): """Batch property changes from a gizmo operation. Undo restores old transform. Parameters ---------- node: The node whose transform properties changed. old_values: Mapping of property name to value *before* the change. new_values: Mapping of property name to value *after* the change. """ def __init__(self, node: Node, old_values: dict[str, Any], new_values: dict[str, Any]) -> None: cmds = [ PropertyCommand(node, prop, old_values[prop], new_values[prop], f"Set {node.name}.{prop}") for prop in new_values if prop in old_values ] super().__init__(cmds, f"Transform {node.name}")