Function record_dispatched_action
pub fn record_dispatched_action<S>(
state: &mut State,
root: *mut dyn Widget,
node_map: &HashMap<NodeId, *mut dyn Widget, S>,
author: &UserId,
) -> boolwhere
S: BuildHasher,Expand description
Turn the actions a widget dispatched during the last event into exactly one undo boundary. Returns whether an entry was recorded.
The counterpart of shortcut for the other direction: the single place
“an edit became an undo boundary” is written down, so a widget behaves
identically under the live host (App) and under the
headless FrameHarness. Both call this once per tick.
It matters that there is only one of these, because there are two separate ways to get it wrong and both are silent:
-
Not draining at all.
take_committed_actionis rising-edge, so a host that never reads it records nothing — an app whose edits all travel throughtilo_actionsthen has an empty undo history no matter how much the user changed. The driver did exactly this, which made undo unobservable to every headless test of such an app. -
Draining anonymously. Undo is author-scoped: an entry with no author is skipped by the local user’s own Ctrl+Z, which then reverts an older entry instead. So the boundary must be attributed, and to the same identity
State::undo_asis asked about. -
Draining only the root. This is the third host→widget channel of its kind, and it was the only one that read a single widget: no container in the toolkit forwards
take_committed_action, so an app that put its editor inside aRow, aColumn, or aNavShelllost every boundary with no error anywhere. LikeUndoStatusandUndoRequest, this sweepsnode_map— every laid-out widget registers itself there — so a widget is reached at any depth.
Every bracket found is drained, even when the actions turned out to change nothing: the rising edge must be cleared, or a later unrelated edit would be bracketed from a stale point and undone along with it.
One tick is one boundary. When several widgets dispatched in the same tick, the entry spans from the earliest of their brackets — the one the others descend from — so a single Ctrl+Z reverts the tick rather than peeling it apart in an order the user never performed.
§Safety
root and the node_map pointers must point at widgets that are alive for
the call — the invariant every node_map sweep in
App and FrameHarness already
relies on. Exactly one &mut is live at a time, so a widget that is both
the root and registered in the map is safe (and yields its bracket once,
because reading clears it).