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
  • 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.