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.

Structural add / remove / reparent leaves live in ``simvx.core.undo`` so
non-editor tools (IDE plugins, asset importers, scripted demos) can
record undoable scene-tree mutations without re-implementing them. The
``AddNodeCommand`` / ``RemoveNodeCommand`` aliases below keep the editor
naming convention.
"""

from typing import Any

from simvx.core import (
    AddChildCommand,
    BatchCommand,
    CallableCommand,
    Node,
    PropertyCommand,
    RemoveChildCommand,
    ReparentCommand,
)

__all__ = [
    "AddNodeCommand",
    "RemoveNodeCommand",
    "ReparentCommand",
    "ReorderCommand",
    "RenameCommand",
    "TransformCommand",
]


# Editor-facing names map onto the generic core commands. ``AddNodeCommand``
# and ``RemoveNodeCommand`` are the editor's vocabulary: keeping the alias
# avoids a churn pass through every consumer for no value.
AddNodeCommand = AddChildCommand
RemoveNodeCommand = RemoveChildCommand
# ReparentCommand is re-exported under its original 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}")