A11yGroupId
Accessibility policies for SceneView.
Two layers cooperate. The visual-default path emits AT nodes
for every visible heavyweight widget and every visible lightweight
item with role + screen-projected bounds, gated by an
A11yOffScreenMode policy that decides which off-viewport items
are still announced. The logical-structural API (groups,
parents, relations, auto-graft, custom focus callbacks) layers
over the top — see docs/teksilo-scene-a11y.md
for the full picture.
Defaults are chosen so a quick prototype is accessible out of the
box: heavyweight widgets emit normally, lightweight items get
synthetic nodes, Tab cycles in reading order. Apps shape the
reading experience by declaring A11yGroups, reparenting nodes,
and installing a focus-order callback.
Builder methods at a glance
as_u64
API reference
📖 Full rustdoc API for this module
pub struct A11yGroupId
Opaque identifier for a logical AT group declared via
Scene::add_a11y_group. Stable
across the lifetime of the process; safe to hash, compare, store.
#![allow(unused)] fn main() { pub struct A11yGroupId(pub(crate) u64); }
Methods
pub fn as_u64(self) -> u64
Raw numeric value. Used by the AT walker to derive a synthetic
NodeId via synthetic_node_id(scene_view_id, id.as_u64(), SyntheticKind::SceneGroup).
pub enum A11yNode
Address of a node in the parallel logical AT tree. Lets apps uniformly target scene entries, virtual groups, and ad-hoc widgets when declaring relationships, parents, or rotor categories.
#![allow(unused)] fn main() { pub enum A11yNode { /* variants */ } }
Variants
Item— Any entry in the scene — lightweightSceneItemor heavyweightWidgetadded viaScene::add_widget. The walker discriminates by entry kind: lightweight items get a syntheticSyntheticKind::SceneItemAT node; heavyweight items get auto-grafted via the framework redirect hook, landing the real widget'sNodeIdunder the declared parent.Group— A virtualA11yGroupdeclared viaScene::add_a11y_group.Widget— A real interactive widget addressed by its arenaWidgetId. Use this to relocate widgets that aren'tScene::add_widget-managed — typically a descendant of a heavyweight scene item that should logically belong elsewhere (a globalComboBoxnested visually inside a Scene card but logically under a top-level "Tools" group). For widgets you added viaScene::add_widget, preferA11yNode::Item(item_id)— the walker handles the heavyweight-item auto-graft for you.
pub enum A11yRelation
AT relationship kind, applied via
Scene::add_a11y_relation.
Maps to AccessKit's relationship arrays.
#![allow(unused)] fn main() { pub enum A11yRelation { /* variants */ } }
Variants
Controls—fromcontrolsto(e.g. a button that opens a menu).DescribedBy—fromis described byto(cross-item annotation).LabelledBy—fromis labelled byto(cross-item label).FlowTo— Logical flow direction — many node-graph editors use this so VoiceOver / NVDA "next item" follows data-flow order rather than reading order.
pub struct A11yCategory
App-defined category tag for AT rotor / quick-nav navigation.
Surfaced to AT clients that support categorized navigation
(VoiceOver rotor on macOS, NVDA quick-nav). Apps coin their own
tag values like "node", "connector", "comment".
#![allow(unused)] fn main() { pub struct A11yCategory(pub std::borrow::Cow<'static, str>); }
Methods
pub fn new(name: impl Into<std::borrow::Cow<'static, str>>) -> Self
Create a new category tag from a string or &'static str.
Accepts "node", "connector", String, or any Cow<'static, str>.
pub struct A11yGroupBuilder
Builder for an A11yGroup. Returned by
A11yGroup::builder; consumed by
Scene::add_a11y_group.
let act_one = scene.add_a11y_group(
A11yGroup::builder()
.label("Act 1")
.role(accesskit::Role::Group)
);
scene.set_a11y_parent(A11yNode::Item(scene_card), Some(A11yNode::Group(act_one)));
#![allow(unused)] fn main() { pub struct A11yGroupBuilder { /* fields */ } }
Methods
pub fn label(mut self, label: impl Into<LocalizedString>) -> Self
Human-readable label for the group, announced when AT clients
land on the group node. Accepts anything convertible into
LocalizedString.
pub fn role(mut self, role: accesskit::Role) -> Self
Override the AccessKit role. Default: Role::Group. Apps
commonly use Role::Region for landmark-style groups.
pub struct A11yGroup
A logical AT group. Pure structure — no visual counterpart, no hit-test, no paint. Declares AT-shape that diverges from visual scene layout (Acts containing Scene cards, Subgraphs containing Nodes, Layers containing Components).
#![allow(unused)] fn main() { pub struct A11yGroup { /* fields */ } }
Methods
pub fn builder() -> A11yGroupBuilder
A fresh builder for a logical group. Default role is
Role::Group; override with A11yGroupBuilder::role.
pub fn id(&self) -> A11yGroupId
The group's id. Stable for the lifetime of the process.
pub fn label(&self) -> Option<String>
The label set on the builder, if any.
pub fn role(&self) -> accesskit::Role
The role set on the builder. Default Role::Group.
pub enum A11yMode
AT-emission strategy for SceneView. Decides whether items /
widgets that have not been placed in the app-declared logical
tree appear in the AT tree by default, or are suppressed.
Pick Cooperative when the visual scene layout is a sensible
AT structure for your app (charts, dashboards, simple maps).
Pick StrictlyParallel when AT shape diverges meaningfully
from visual layout — story corkboards (Acts → Scene cards),
node-graph editors (Subgraphs → Nodes → Ports), CAD canvases
(Layers → Components). Apps in this category typically declare
every AT edge anyway, so the default visual-emission becomes
noise.
#![allow(unused)] fn main() { pub enum A11yMode { /* variants */ } }
Variants
Cooperative— Default. Visual is the AT structure unless overridden. Items inside the off-screen-mode policy emit as direct AT children ofSceneView(or their declared logical parent ifset_a11y_parentplaced them). Heavyweight widgets emit through the arena walker as natural descendants ofSceneView. The logical-tree machinery layers on top.StrictlyParallel— AT structure is purely declared. Items are emitted only if the app placed them in the logical tree viaScene::set_a11y_parent. Heavyweight widgets still emit (they own focus / interaction state the AT layer can't suppress) but their parent in the AT tree is the declared logical parent if any, elseSceneViewitself. Use this when your app's AT shape is fundamentally different from its visual layout — declaring every node once is cheaper than overriding the visual default for every node.
pub enum A11yBoundsSpace
Coordinate space the AT walker reports SceneItem bounds in.
The framework convention is screen-projected bounds — the rectangle a sighted user would see on the physical monitor, after pan/zoom/rotation has been applied. Screen readers consume this for spatial nav (Apple's "explore by touch", touch-screen navi- gation, magnifier follow-focus). 99% of apps want this default.
Scene bounds are the raw scene-coord rectangle stored on the item, with no view-transform applied. Use this only for the rare AT clients that reason about scene topology rather than viewport position — typically when a SceneView's contents have a logical, fixed coordinate system that the user thinks in (a CAD canvas where "the bracket is at (240, 180)" means a fixed physical machine position regardless of zoom level).
Picking the wrong one makes "go to the next item" navigation
either a) ignore the user's current pan (Screen mode in a
scene-coord-aware app) or b) report bounds that drift under
pan/zoom (Scene mode in a viewport-aware app). Default is
Screen — change only when you've confirmed your AT users
genuinely want the alternative.
#![allow(unused)] fn main() { pub enum A11yBoundsSpace { /* variants */ } }
Variants
Screen— Screen-projected bounds —view_transform * bounds_in_scene. The framework default; matches the convention used by every other widget in the framework.Scene— Raw scene-coordinate bounds, with no view-transform applied. Apps with a logical fixed coordinate system (CAD canvases, blueprint editors) may want this so AT users can reason about "where in the design" an item sits, independent of the current pan/zoom.
pub enum A11yOffScreenMode
Off-screen visibility policy for the AT walker. Decides which scene items get emitted as synthetic AT nodes per AT-rebuild.
ViewportPlusN { n: 1 } is the default: an item appears in the
AT tree if its bounds_in_scene intersects viewport ∪ (1× viewport-grown-rect). That keeps the tree close to "what the
user can interact with right now" while letting screen-reader
users discover items just outside the visible region by jumping
to the next/prev — at which point SceneView::ensure_visible
pans the view to bring the focused item into view.
#![allow(unused)] fn main() { pub enum A11yOffScreenMode { /* variants */ } }
Variants
AllItems— Emit every item in the scene as a synthetic AT node. Heaviest mode — appropriate for small scenes (< ~500 items) where AT users want a complete table of contents.ViewportPlusN— Emit items inside the viewport plus ann × viewport-grown margin around it.n = 0collapses to "viewport only" with the same allocation pattern asViewportOnly.n = 1is the default — gives screen-reader users a one-screen "lookahead" to navigate withoutensure_visibleround-tripping through pan animation.ViewportOnly— Strict: only items intersecting the current viewport. Pairs with apps that have very large scenes where listing off-screen content would overwhelm AT clients.
Methods
pub fn at_visible_region(&self, visible_scene_region: Rect) -> Option<Rect>
Compute the scene-coord rectangle a given mode considers
"AT-visible" given the current visible scene region. Used by
SceneView::accessibility as the spatial-index query rect.
AllItems returns None so the caller knows to bypass the
query and emit every item.