Undo¶
Unless stated otherwise, “undo” means “undo/redo”
- Granularity is about how much should undone at one time.
- A chunk is conceptual change from one state to another
- Interaction can be divided into undoable chunks
- Undo reverses one chunk
- Two general approaches of implementing Undo:
- Forward Undo
- Start from base document, then maintain of list of changes to compute current document
- Undo by removing last change from list when computing current document
- Reverse Undo
- Apply change to update document, but also save "reverse" change
- Undo by applying reverse change to document
- Forward Undo
- A change record defines a single transformation to the "document" (i.e. the state of the Model)
- Standard undo/redo behavior: once you do something new after undoing, redo history is lost, like you clear the redo history
- Forward Undo
- Stores: Only “do” commands (how to go forward).
- Undo: Remove the last command and recompute the state from the base by replaying the remaining commands.
- Destructive commands are not special — you just skip them when replaying.
- Downsides: Can be slow for long histories (need to replay from base).
- Reverse Undo
- Stores: Both “do” and “undo” actions for each command.
- Undo: Call the last command’s undo() to directly restore the previous state.
- Problem: For destructive commands, undo() might need data that no longer exists — so you must store a Memento (snapshot) of the destroyed data.
- Downsides: Snapshots can take a lot of memory, so undo stack size is often limited.