DISCLAIMER: All this is WIP! Those are mostly quick notes from working in this area, arranged in an attempt of proper documentation layout/organization.
Concepts & Definitions
Undo is organized as an 'undo stack' storing a list of 'undo steps'. The whole stack represents the history of what is undoable.
Typically, only data is stored and managed in undo, UI changes are not.
Undo steps could be either:
- Relative: A step can only be loaded if the previous (or next) one has already be loaded. In other words, the stack needs to be processed sequentially through all steps from the current (active) one until the target one is reached.
- Absolute: A step contains the whole data and can be directly loaded as its own from any point of the history.
Currently, Blender undo stack is fully relative. Some parts of it could also be handled as absolute if needed in the future (at least the global undo ones effectively store the whole data).
Further more, we have two kinds of steps:
- Stateful: The undo step stores the state of some data, and can be loaded regardless of the direction (undo or redo).
- Differential: The undo step only stores the difference to the previous step. Further more, they cannot be loaded, but either applied (redo) or unapplied (undo). One key consequence of this is that with such steps, when undoing, you actually need to process (unapply) the n + 1 step, not the target n step.
Overview of Implementation
There is a single stack that gathers steps of different types (like global undo, edit modes undo, sculpt or paint undo, etc.).
The current state in Blender matches the active undo step, this information is stored in the stack as well.
The undo system in Blender is currently fully relative, which means to reach a given undo step, you also need to load all those in-between the target and the current active one.
Steps have a type, with a set of callbacks that actually implement the undo storage and loading.
Some skipped steps may be hidden from the user (in the history e.g.), those are used when an intermediate state storage is needed, that does not actually match a state in the history from the user point of view. 'Skipped' steps should never become 'active', they should always only be intermediary steps.
The main part of step creation ('undo push') is controlled by the operator management system (from the Window manager code).
Undo system is implemented in three layers:
Top level Editor Code
This code (mostly located in
ed_undo.c file) is essentially operators to provide various undo-related operations (basic one-step undo/redo, exposing the undo stack as an history, handle operators parameters editing as an undo/re-execution process, etc.). It also contains some high-level API to push undo steps.
BKE Undo System
undo_system.c there is the main undo stack management code, including creation of steps, and undoing/redoing.
This code handles the processing of intermediate steps, proper handling of 'skipped' ones, and deals with the specificities of the two kinds of steps (stateful vs. differential).
Specific Step Type Implementation
Actual undo types implementations happens by defining and registering an
They are scattered in several sub-modules of the
ED area (like
memfile_undo.c, etc.). Many of those use in turn low-level code from
BKE or other areas (e.g.
memfile undo uses
BKE_memfile_ functions from
blender_undo.c, which in turns uses read/write .blend file code from