Module accesskit_bridge
Expand description
AccessKit bridge: turns the toolkit’s per-widget semantic model
(crate::a11y::AccessNode, from
Widget::accessibility) into an
accesskit::TreeUpdate, published over AT-SPI2.
build_tree_update_with_resources is a pure function of (root_widget, node_map, focused, resources): a depth-first walk that emits one node per widget returning
Some(AccessNode), and collapses presentational widgets (None) by
reparenting their accessible children onto the nearest ancestor —
mirroring ARIA role="presentation". Being pure and keyed off the same
node_map the App maintains makes it unit-testable without a live
AT-SPI bus.
§Id spaces
Five disjoint ranges share the u64, so an id says what it addresses
without a lookup and resolve_node_id can invert it:
| space | derivation | range |
|---|---|---|
| root | ROOT_ID (0) | zero |
| widget | taffy::NodeId + 1 (0 reserved for the root) | below 1 << 48 |
| widget, never laid out | (1 << 48) + walk_index | bit 48 |
| text run | (1 << 62) | owner id | bit 62 |
| synthetic | the owner’s id chained with each AccessNode::key | bit 63 |
A taffy::NodeId indexes a live arena, so it is bounded by the number of
nodes in memory and cannot reach 1 << 48, let alone the two marker bits;
that is what keeps the widget range clear of the other three.
§Canvas widgets: focus and actions on synthetic nodes
A widget that paints its own contents — a timeline of clips, a table of
rows — has no child widgets to expose, so it synthesises its accessible
structure through AccessNode::children. Two things make those nodes
first-class rather than merely readable:
- Focus. The widget marks one descendant
focused; while that widget holds the keyboard,build_tree_updateaims the tree’sfocusat that node instead of the widget. This is ARIA’saria-activedescendant: the widget owns the keyboard, the marked node is where the user is. Keying it off the App’s real focus is deliberate — an unfocused widget cannot steal the reader’s cursor by setting a flag.active_descendant_pathnames the same node for the keyboard path, so Enter activates what the reader just announced. - Actions.
resolve_node_idinverts a synthetic id back to(owning widget, key path)so an incomingActionRequestreaches the clip the user was on, viaWidget::perform_action. Inversion re-walks the live tree rather than caching an index, so a stale id from a previous frame resolves toNoneinstead of to the wrong clip.
Enums§
- A11y
Target - Where an
accesskit::NodeIdpoints in the live widget tree. Produced byresolve_node_id, which is how an incomingActionRequestis turned back into something the app can act on.
Constants§
- RESOURCE_
ID_ BASE - Resource and action nodes occupy reserved ranges above synthetic widget nodes and below text-run nodes. The ids are stable hashes of their public addresses, so assistive technology can retain identity across frames.
- ROOT_ID
- Reserved so no widget can collide with it: widget ids are offset by +1
from their taffy node id, and structural fallback ids are large offsets
— see
widget_node_id. - SYNTHETIC_
ID_ BIT - Bit that marks an
accesskit::NodeIdas belonging to a synthetic node — one with no backingWidget. Widget ids aretaffy_id + 1or(1 << 48) + indexand text-run ids sit at bit 62, so nothing else in the tree can reach bit 63 and the space cannot collide.
Functions§
- accesskit_
action_ to_ ours - Inverse of
map_action.Nonefor actions we don’t model (scroll-by-direction, custom actions, …). - active_
descendant_ path - The key path to the active descendant a widget nominates — the first
node in its synthetic subtree marked
focused— orNonewhen it nominates none. - build_
tree_ update - build_
tree_ update_ with_ resources - Build the widget accessibility tree and append the exact same live
resource/action surface used by the palette, shell and collaboration API.
Immediate operations expose
Click; prompted operations exposeSetValuewith the supplied value becoming their argument. - is_
synthetic_ id - Whether
idaddresses a synthetic node rather than a widget, a text run or the root. - map_
action ToggleCheckhas no dedicated verb — toggling is aClickat the platform level.SetValue/SetSelectionpayloads travel inActionDataon the round trip (accesskit_action_to_ours).- map_
role - E.g.
Role::Spinnerbecomes a busyAkRole::ProgressIndicatorandRole::ToggleButtonbecomesAkRole::Buttoncarryingtoggledstate. - resolve_
node_ id - Invert an
accesskit::NodeIdback to the thing it addresses, by re-deriving ids over the current widget tree. - resource_
action_ node_ id - resource_
node_ id - widget_
ak_ id - The AccessKit id of the widget laid out at
taffy_id, orNonewhen that taffy node holds no live widget.