Skip to main content

smudge_core/
semantics.rs

1//! The accessibility projection: the roles and names widgets report and the
2//! tree the [`App`](crate::app::App) exposes to an accessibility backend.
3//!
4//! A widget opts into the projection by returning [`Semantics`] from
5//! [`Widget::semantics`](crate::widget::Widget::semantics), and an element can
6//! override the role or set the label with the
7//! [`role`](crate::element::Element::role) and
8//! [`accessible_label`](crate::element::Element::accessible_label) builders.
9//! An accessibility backend reads the projected tree through the app and the
10//! changes since the last frame; this module defines the vocabulary the two
11//! sides exchange. It stays backend-neutral: mapping it onto a specific
12//! accessibility protocol is a backend's job.
13
14use crate::geometry::Rect;
15use crate::tree::WidgetId;
16
17/// The kind of control a projected node represents.
18///
19/// The set covers the widgets this release projects. A widget without a role
20/// here is left out of the projection, and its children attach to the nearest
21/// projected ancestor.
22#[derive(Copy, Clone, Debug, PartialEq, Eq)]
23#[non_exhaustive]
24pub enum Role {
25    /// The window the tree fills. The backend adds this as the root.
26    Window,
27    /// A plain container that groups its children.
28    Group,
29    /// A control a press activates.
30    Button,
31    /// Static text.
32    Label,
33    /// A single-line editable text field.
34    TextInput,
35    /// A multi-line editable text field.
36    MultilineTextInput,
37    /// A viewport that clips and scrolls its content.
38    ScrollView,
39    /// A container whose children are list items.
40    List,
41    /// One entry of a list.
42    ListItem,
43}
44
45/// The accessibility description a widget reports about itself.
46///
47/// Return one from [`Widget::semantics`](crate::widget::Widget::semantics) to
48/// place the widget in the projection.
49#[derive(Clone, Debug, PartialEq, Eq)]
50pub struct Semantics {
51    /// The kind of control the widget is.
52    pub role: Role,
53    /// The accessible name: what a screen reader announces first.
54    pub label: Option<String>,
55    /// The current value: for a text field its content, for a label its text.
56    pub value: Option<String>,
57}
58
59impl Semantics {
60    /// Creates a description with the role and no name or value.
61    pub fn new(role: Role) -> Semantics {
62        Semantics {
63            role,
64            label: None,
65            value: None,
66        }
67    }
68}
69
70/// One node of the projected tree, in the form a backend reads.
71///
72/// `id`, `parent` and `children` name projected nodes only, so the backend can
73/// build a tree whose nodes all carry a role. `children` is the list the
74/// projection reports: the children of an unprojected widget attach to the
75/// nearest projected ancestor.
76#[derive(Clone, Debug, PartialEq)]
77pub struct SemanticsNode {
78    /// The id of the widget this node projects.
79    pub id: WidgetId,
80    /// The id of the nearest projected ancestor, or `None` for the root.
81    pub parent: Option<WidgetId>,
82    /// The ids of the projected children, in paint order.
83    pub children: Vec<WidgetId>,
84    /// The kind of control the node represents.
85    pub role: Role,
86    /// The accessible name.
87    pub label: Option<String>,
88    /// The current value.
89    pub value: Option<String>,
90    /// The node's absolute rectangle in logical pixels, as the last layout
91    /// pass left it.
92    pub bounds: Rect,
93    /// Whether the node can take the keyboard focus.
94    pub focusable: bool,
95    /// Whether the node is disabled.
96    pub disabled: bool,
97    /// Whether the node holds the keyboard focus.
98    pub focused: bool,
99    /// Whether a press on the node runs a handler, so the backend can offer
100    /// the press action.
101    pub pressable: bool,
102}
103
104/// The projected nodes that changed since the last frame.
105///
106/// The backend rebuilds the nodes the changed ids project; `reset` says the
107/// projection must be rebuilt from scratch instead, which happens when too
108/// many changes queued in one frame. The ids name widgets that are alive,
109/// visible and projected at the moment this value is taken, and each appears
110/// once; a widget that a frame removes or hides drops out on its own, and the
111/// change to its parent's child list is what takes it off the tree.
112#[derive(Clone, Debug, Default, PartialEq, Eq)]
113pub struct A11yChanges {
114    /// The widgets whose projected nodes must be rebuilt, children included.
115    pub changed: Vec<WidgetId>,
116    /// Whether the list of projected nodes with no projected ancestor may
117    /// have changed, which the backend's own root holds as its children.
118    pub roots_changed: bool,
119    /// Whether the whole projection must be rebuilt from scratch.
120    pub reset: bool,
121}