Skip to main content

Module access

Module access 

Source
Expand description

Accessibility as data: the semantic tree of a frame, derived from what nodes do and from the role and label props a view declares.

A view rarely builds anything here. It sets NodeSpec::role, NodeSpec::label, description, live and the value props; the core derives an AccessTree of AccessNodes (roles from behaviour, names from labels or text, plain boxes elided) that a driver reads with crate::Core::access_tree and hands to the platform through AccessKit. Requests from assistive technology come back as crate::InputEvent::Access carrying an AccessRequest and resolve inside the core: activating a button emits the same event a click would. Ui::announce queues an Announcement for a one-off message with no node behind it. Headless tests assert on the tree directly.

use kui_core::{AccessAction, Core, NodeSpec, Role, Size, TextStyle};

let mut core = Core::new();
let mut ui = core.frame(Size::new(400.0, 300.0), 1.0);
ui.window_title("Demo");
// An `on_click` node is a button; `label` names it when it has no text.
ui.leaf_keyed("save", NodeSpec::row().size(24.0, 24.0).on_click("save").label("Save"));
ui.text("Ready", TextStyle::new(14.0));
ui.finish();

let tree = core.access_tree();
assert_eq!(tree.root().map(|n| n.role), Some(Role::Window));
let button = tree.nodes.iter().find(|n| n.role == Role::Button).unwrap();
assert_eq!(button.name.as_deref(), Some("Save"));
assert!(button.supports(AccessAction::Click));

Text is the one place the tree goes below the node: an editor carries its laid-out lines as AccessRuns and its caret and selection as positions in them, which is what a screen reader needs to read by character, word and line. An app that draws its own text in an on_key sink gets the same by declaring role="multilineTextInput" on the sink, role="line" on each line, and caret / selectionAnchor byte offsets on the lines that hold them.

Structs§

AccessNode
One semantic node of a frame.
AccessRequest
A request from assistive technology, delivered as crate::InputEvent::Access.
AccessRun
One visual line (or a piece of one) of an editor’s text, with what a screen reader needs to read it by character and word and to place a caret: every character’s byte length, x position and width. A line that continues into another ends with its "\n", counted as a character of zero width. Runs longer than RUN_CHARS characters are split, so indices fit the platform’s byte-sized ones.
AccessTree
The semantic nodes of a finished frame, in tree order (a parent always precedes its descendants; the root comes first).
Announcement
One thing to say once, with no node behind it: “Saved”, “3 results”. Queued by Core::announce and drained by Core::take_announcements, the way window commands, audio commands and warnings are: an announcement is an event on a timeline, and the frame’s tree has no place to keep one.
ScrollState
A scroll view’s offsets and range (logical px).
TextPos
A position in an editor’s text: a run and a character index into it (character == char count is the end of the run).

Enums§

AccessAction
What assistive technology can ask of a node. Each node advertises the subset it supports (AccessNode::actions), and a request for one arrives as crate::InputEvent::Access.
Live
How urgently a reader should read a change it was not asked to read: ARIA’s aria-live, AccessKit’s Live. Declared on the node holding the text (live prop) and, for a one-off with no node behind it, the politeness of an Announcement.
Orientation
How a container arranges its items, for the platform to announce (AXOrientation, UIA’s Orientation). Derived from the container’s dir and never declared: the layout is what arranges the items, so a row that says it is a column would be a fact with two owners. It is an announcement and not a gate — the arrows move both ways whatever this says — so a container whose visual arrangement does not match its dir costs a less precise announcement rather than a dead keyboard.
Role
What a node is to assistive technology. Most of these a view declares (role prop; schema::ROLES is that list, and the wire order); the ones the core derives from a node’s content and behaviour instead are schema::DERIVED_ONLY, which says what derives each. Every variant is on one list or the other — schema’s every_role_is_declarable_or_derived fails when a new one is on neither.

Constants§

RUN_CHARS
Longest run, in characters (the platform indexes them in a byte).

Type Aliases§

Label
The label type on NodeSpec: shared, so cloning a spec is a refcount bump and a Rust view can keep one Arc<str> across frames.