Skip to main content

Commands, Undo/Redo

Undo uses the command pattern (command.rs). Each command captures, when applied, the state it needs to invert itself, so the history holds small deltas rather than project snapshots and is unbounded.

pub trait Command: Debug + Send + Any {
    fn apply(&mut self, project: &mut Project);
    fn undo(&self, project: &mut Project);
    fn label(&self) -> CommandLabel;
}

Send because an editor serving MCP also runs commands from another thread. CommandLabel names the step; the app translates it.

Flow of an edit

ch2-commands.png

Two entry points

LayerRole
vv_core::editEditing operations (split, delete, ripple delete, insert, move, trim, slip…) on explicit targets. Selection and playhead belong to the caller. Each operation is at most one undo step.
Command structsSingle changes (SetClipValue, UpsertKeyframe, SetMixerParam…) applied directly by panels and tools, and the building blocks edit composes.
pool.rs commandsMedia, timelines and folders (AddEntities, RenameTimeline, DeleteFolder…). New entities get their ids when the command is built, so a redo restores the same ids.

History

MethodEffect
do_commandApplies, pushes on the undo stack, clears redo.
undo / redo / go_to(n)Moves between stacks; go_to jumps to any step of the visible history.
begin_group / end_group[_as]Wraps everything in between into one CompositeCommand; groups nest.
joinAppends to an earlier step if nothing else touched the history since (background imports).
generationChanges on every effective modification; Session compares it to know if the project is unsaved.

A CompositeCommand applies its children in order and undoes them in reverse.