Skip to main content

cranpose_ui/layout/
semantics_details.rs

1use cranpose_core::collections::rare::{RareProperties, rare, set_rare, update_rare};
2use cranpose_foundation::{
3    CanvasSemanticsNode, CollectionInfo, LiveRegionMode, ProgressBarRangeInfo, ScrollAxisRange,
4    SemanticsConfiguration, SemanticsCustomAction, SemanticsDismiss, SemanticsExpand,
5    SemanticsLongClick, SemanticsMagicTap, SemanticsScrollBy, SemanticsScrollToIndex,
6    SemanticsSetProgress, SemanticsSetSelection, SemanticsSetText, text::TextRange,
7};
8
9use super::SemanticsNode;
10
11/// What a semantics node reports beyond its place, its name, its value and
12/// its click: the properties few nodes set. A node holds them only when it
13/// sets one, so a node without any stays small.
14#[derive(Clone, Debug, PartialEq)]
15pub struct SemanticsDetails {
16    /// What the control says about itself after its name.
17    pub state_description: Option<String>,
18    /// What activating the control does, as a verb phrase a reader reads out.
19    pub on_click_label: Option<String>,
20    /// What this control does when a screen reader asks for its long press.
21    pub on_long_click: Option<SemanticsLongClick>,
22    /// What the long press does, as a verb phrase a reader reads out.
23    pub on_long_click_label: Option<String>,
24    /// What this control does on VoiceOver's magic tap.
25    pub on_magic_tap: Option<SemanticsMagicTap>,
26    /// What the magic tap does, as a verb phrase a reader reads out.
27    pub on_magic_tap_label: Option<String>,
28    /// The short names a person says to Voice Control to reach this control.
29    pub input_labels: Vec<String>,
30    /// The language of this control's text, as a BCP 47 tag.
31    pub language: Option<String>,
32    /// The actions a screen reader lists for the control beyond its click.
33    pub custom_actions: Vec<SemanticsCustomAction>,
34    /// Controls this node drew rather than laid out; their bounds are relative
35    /// to this node's own top-left. See [`CanvasSemanticsNode`].
36    pub canvas_children: Vec<CanvasSemanticsNode>,
37    /// Whether this node is a field a person types into.
38    pub editable_text: bool,
39    /// Whether an editable field accepts line breaks, independent of its current text.
40    pub multiline: bool,
41    /// Whether this subtree makes content outside it unavailable to assistive technology.
42    pub is_modal: bool,
43    /// Whether the selectable controls under this node form one group.
44    pub selectable_group: bool,
45    /// The title of the screen or pane this node is the root of.
46    pub pane_title: Option<String>,
47    /// Why the control's content is wrong, when it is.
48    pub error: Option<String>,
49    /// Whether this field holds a secret, so its text stays unspoken.
50    pub password: bool,
51    /// Where the caret of an editable field sits, or which stretch of its
52    /// text is picked.
53    pub text_selection: Option<TextRange>,
54    /// How urgently a screen reader reads this node when its text changes.
55    /// Compose's `SemanticsProperties.LiveRegion`.
56    pub live_region: Option<LiveRegionMode>,
57    /// The value this control holds inside a range. Compose's
58    /// `ProgressBarRangeInfo`.
59    pub progress: Option<ProgressBarRangeInfo>,
60    /// What this control does when a screen reader moves its value.
61    pub set_progress: Option<SemanticsSetProgress>,
62    /// What this field does when a screen reader hands it text.
63    pub set_text: Option<SemanticsSetText>,
64    /// What this field does when a screen reader moves its caret or picks a
65    /// stretch of its text.
66    pub set_selection: Option<SemanticsSetSelection>,
67    /// What this control does when a screen reader asks it to open.
68    pub expand: Option<SemanticsExpand>,
69    /// What this control does when a screen reader asks it to close.
70    pub collapse: Option<SemanticsExpand>,
71    /// What this control does when a screen reader asks to send it away.
72    pub dismiss: Option<SemanticsDismiss>,
73    /// How far this container scrolled up and down, when it scrolls.
74    pub vertical_scroll: Option<ScrollAxisRange>,
75    /// How far this container scrolled left and right, when it scrolls.
76    pub horizontal_scroll: Option<ScrollAxisRange>,
77    /// What this container does when a screen reader pages it.
78    pub scroll_by: Option<SemanticsScrollBy>,
79    /// What this list does when a screen reader asks for the row at an index.
80    pub scroll_to_index: Option<SemanticsScrollToIndex>,
81    /// How many rows and columns this list holds, when it is a list.
82    pub collection: Option<CollectionInfo>,
83    /// The name tests find this node by; never published to screen readers.
84    pub test_tag: Option<String>,
85}
86
87impl SemanticsDetails {
88    /// Details with nothing set: what a node that holds none reports.
89    pub const NONE: Self = Self {
90        state_description: None,
91        on_click_label: None,
92        on_long_click: None,
93        on_long_click_label: None,
94        on_magic_tap: None,
95        on_magic_tap_label: None,
96        input_labels: Vec::new(),
97        language: None,
98        custom_actions: Vec::new(),
99        canvas_children: Vec::new(),
100        editable_text: false,
101        multiline: false,
102        is_modal: false,
103        selectable_group: false,
104        pane_title: None,
105        error: None,
106        password: false,
107        text_selection: None,
108        live_region: None,
109        progress: None,
110        set_progress: None,
111        set_text: None,
112        set_selection: None,
113        expand: None,
114        collapse: None,
115        dismiss: None,
116        vertical_scroll: None,
117        horizontal_scroll: None,
118        scroll_by: None,
119        scroll_to_index: None,
120        collection: None,
121        test_tag: None,
122    };
123
124    pub(super) fn from_configuration(
125        config: SemanticsConfiguration,
126        on_click_label: Option<String>,
127        is_modal: bool,
128    ) -> Self {
129        Self {
130            state_description: config.state_description,
131            on_click_label,
132            on_long_click: config.on_long_click,
133            on_long_click_label: config.on_long_click_label,
134            on_magic_tap: config.on_magic_tap,
135            on_magic_tap_label: config.on_magic_tap_label,
136            input_labels: config.input_labels,
137            language: config.language,
138            custom_actions: config.custom_actions,
139            canvas_children: config.canvas_children,
140            editable_text: config.is_editable_text,
141            multiline: config.multiline,
142            is_modal,
143            selectable_group: config.selectable_group,
144            pane_title: config.pane_title,
145            error: config.error,
146            password: config.password,
147            text_selection: config.text_selection,
148            live_region: config.live_region,
149            progress: config.progress,
150            set_progress: config.set_progress,
151            set_text: config.set_text,
152            set_selection: config.set_selection,
153            expand: config.expand,
154            collapse: config.collapse,
155            dismiss: config.dismiss,
156            vertical_scroll: config.vertical_scroll,
157            horizontal_scroll: config.horizontal_scroll,
158            scroll_by: config.scroll_by,
159            scroll_to_index: config.scroll_to_index,
160            collection: config.collection,
161            test_tag: config.test_tag,
162        }
163    }
164}
165
166impl Default for SemanticsDetails {
167    fn default() -> Self {
168        Self::NONE
169    }
170}
171
172impl RareProperties for SemanticsDetails {
173    const EMPTY: &'static Self = &Self::NONE;
174}
175
176impl SemanticsNode {
177    /// The properties few nodes set, with nothing set for a node that holds
178    /// none.
179    pub fn details(&self) -> &SemanticsDetails {
180        rare(&self.details)
181    }
182
183    /// Replaces the node's details. A node holds them only when something is
184    /// set, and keeps the space it already held for them.
185    pub fn set_details(&mut self, details: SemanticsDetails) {
186        set_rare(&mut self.details, details);
187    }
188
189    /// Changes some of the node's details in place, through
190    /// [`SemanticsNode::set_details`].
191    pub fn update_details(&mut self, update: impl FnOnce(&mut SemanticsDetails)) {
192        update_rare(&mut self.details, update);
193    }
194}