zng_app/widget/info/
access.rs

1//! Accessibility metadata types.
2
3use std::num::NonZeroU32;
4
5use parking_lot::Mutex;
6use unic_langid::LanguageIdentifier;
7use zng_layout::unit::{Factor, PxSize, PxTransform};
8use zng_state_map::{StateId, static_id};
9use zng_txt::Txt;
10use zng_unique_id::IdMap;
11use zng_var::{IntoVar, Var};
12pub use zng_view_api::access::{
13    AccessCmdName, AccessRole, AutoComplete, CurrentKind, Invalid, LiveIndicator, Orientation, Popup, SortDirection,
14};
15use zng_view_api::access::{AccessNodeId, AccessState};
16
17use crate::widget::WidgetId;
18
19use super::{WidgetInfo, WidgetInfoBuilder, WidgetInfoTree, iter::TreeIterator};
20
21impl WidgetInfoBuilder {
22    /// Accessibility metadata builder.
23    ///
24    /// Only available if accessibility info is required for the window.
25    pub fn access(&mut self) -> Option<WidgetAccessInfoBuilder<'_>> {
26        if self.access_enabled.is_enabled() {
27            Some(WidgetAccessInfoBuilder { builder: self })
28        } else {
29            None
30        }
31    }
32}
33
34/// Accessibility metadata.
35pub struct WidgetAccessInfoBuilder<'a> {
36    pub(super) builder: &'a mut WidgetInfoBuilder,
37}
38impl WidgetAccessInfoBuilder<'_> {
39    fn with_access(&mut self, f: impl FnOnce(&mut AccessInfo)) {
40        self.builder.with_meta(move |mut m| f(m.entry(*ACCESS_INFO_ID).or_default()))
41    }
42
43    /// Set the accessibility role of the widget.
44    pub fn set_role(&mut self, role: AccessRole) {
45        self.with_access(|a| a.role = Some(role))
46    }
47
48    /// Add a supported access command.
49    pub fn push_command(&mut self, cmd: AccessCmdName) {
50        self.with_access(|a| a.commands.push(cmd))
51    }
52
53    /// Set how input text triggers display of one or more predictions of the user's intended
54    /// value for a [`ComboBox`], [`SearchBox`], or [`TextInput`].
55    ///
56    /// [`ComboBox`]: AccessRole::ComboBox
57    /// [`SearchBox`]: AccessRole::SearchBox
58    /// [`TextInput`]: AccessRole::TextInput
59    pub fn set_auto_complete(&mut self, mode: AutoComplete) {
60        self.with_access(|a| a.set_state(AccessState::AutoComplete(mode)))
61    }
62
63    /// If the widget is checked (`Some(true)`), unchecked (`Some(false)`), or if the checked status is indeterminate (`None`).
64    pub fn set_checked(&mut self, checked: Option<bool>) {
65        self.with_access(|a| a.set_state(AccessState::Checked(checked)))
66    }
67
68    /// Indicates that the widget represents the current item of a [kind](CurrentKind).
69    pub fn set_current(&mut self, kind: CurrentKind) {
70        self.with_access(|a| a.set_state(AccessState::Current(kind)))
71    }
72
73    /// Indicates that the widget is an error message for the `invalid_wgt`.
74    ///
75    /// The other widget must [`set_invalid`].
76    ///
77    /// [`set_invalid`]: fn@Self::set_invalid
78    pub fn set_error_message(&mut self, invalid_wgt: impl Into<WidgetId>) {
79        let invalid_wgt = invalid_wgt.into();
80        self.with_access(|a| a.set_state(AccessState::ErrorMessage(invalid_wgt.into())))
81    }
82
83    /// Identifies the currently active widget when focus is on a composite widget.
84    pub fn set_active_descendant(&mut self, descendant: impl Into<WidgetId>) {
85        let descendant = descendant.into();
86        self.with_access(|a| a.set_state(AccessState::ActiveDescendant(descendant.into())))
87    }
88
89    /// Indicate that the widget toggles the visibility of related widgets.
90    ///
91    /// Use [`push_controls`], or [`push_owns`] to indicate the widgets that change visibility based on
92    /// this value.
93    ///
94    /// [`push_controls`]: Self::push_controls
95    /// [`push_owns`]: Self::push_owns
96    pub fn set_expanded(&mut self, expanded: bool) {
97        self.with_access(|a| a.set_state(AccessState::Expanded(expanded)))
98    }
99
100    /// Indicates the availability and type of interactive popup widget.
101    pub fn set_popup(&mut self, popup: Popup) {
102        self.with_access(|a| a.set_state(AccessState::Popup(popup)))
103    }
104
105    /// Indicates that the widget's data is invalid with optional kinds of errors.
106    pub fn set_invalid(&mut self, error: Invalid) {
107        self.with_access(|a| a.set_state(AccessState::Invalid(error)));
108    }
109
110    /// Sets a custom name for the widget in accessibility info.
111    ///
112    /// Note that if this is not set the [`WidgetId::name`] of the widget is used.
113    pub fn set_label(&mut self, name: impl Into<Txt>) {
114        let name = name.into();
115        self.with_access(|a| a.set_state_source(AccessStateSource::Label(name)))
116    }
117
118    /// Sets the hierarchical level of the widget within a parent scope.
119    pub fn set_level(&mut self, hierarchical_level: NonZeroU32) {
120        self.with_access(|a| a.set_state(AccessState::Level(hierarchical_level)))
121    }
122
123    /// Indicates that the user may select more than one item from the current selectable descendants.
124    pub fn flag_multi_selectable(&mut self) {
125        self.with_access(|a| a.set_state(AccessState::MultiSelectable))
126    }
127
128    /// Indicates whether the widget's orientation is horizontal, vertical, or unknown/ambiguous.
129    pub fn set_orientation(&mut self, orientation: Orientation) {
130        self.with_access(|a| a.set_state(AccessState::Orientation(orientation)))
131    }
132
133    /// Short hint (a word or short phrase) intended to help the user with data entry when a form control has no value.
134    pub fn set_placeholder(&mut self, placeholder: impl Into<Txt>) {
135        let placeholder = placeholder.into();
136        self.with_access(|a| a.set_state_source(AccessStateSource::Placeholder(placeholder)))
137    }
138
139    /// Indicates that the widget is not editable, but is otherwise operable.
140    pub fn flag_read_only(&mut self) {
141        self.with_access(|a| a.set_state(AccessState::ReadOnly))
142    }
143
144    /// Indicates that user input is required on the widget before a form may be submitted.
145    pub fn flag_required(&mut self) {
146        self.with_access(|a| a.set_state(AccessState::Required))
147    }
148
149    /// Indicates that the widget is selected.
150    pub fn flag_selected(&mut self) {
151        self.with_access(|a| a.set_state(AccessState::Selected))
152    }
153
154    /// Sets the sort direction for the table or grid items.
155    pub fn set_sort(&mut self, direction: SortDirection) {
156        self.with_access(|a| a.set_state(AccessState::Sort(direction)))
157    }
158
159    /// Set the maximum value (inclusive).
160    pub fn set_value_max(&mut self, max: f64) {
161        self.with_access(|a| a.set_state(AccessState::ValueMax(max)))
162    }
163
164    /// Set the minimum value (inclusive).
165    pub fn set_value_min(&mut self, min: f64) {
166        self.with_access(|a| a.set_state(AccessState::ValueMin(min)))
167    }
168
169    /// Set the current value.
170    pub fn set_value(&mut self, value: f64) {
171        self.with_access(|a| a.set_state(AccessState::Value(value)))
172    }
173
174    /// Set a text that is a readable version of the current value.
175    pub fn set_value_text(&mut self, value: impl Into<Txt>) {
176        let value = value.into();
177        self.with_access(|a| a.set_state_source(AccessStateSource::ValueText(value)))
178    }
179
180    /// Indicate that the widget can change, how the change can be announced, if `atomic`
181    /// the entire widget must be re-read, if `busy` the screen reader must wait until the change completes.
182    pub fn set_live(&mut self, indicator: LiveIndicator, atomic: bool, busy: bool) {
183        self.with_access(|a| a.set_state(AccessState::Live { indicator, atomic, busy }))
184    }
185
186    /// Sets the total number of columns in a [`Table`], [`Grid`], or [`TreeGrid`] when not all columns are present in tree.
187    ///
188    /// The value `0` indicates that not all columns are in the widget and the application cannot determinate the exact number.
189    ///
190    /// [`Table`]: AccessRole::Table
191    /// [`Grid`]: AccessRole::Grid
192    /// [`TreeGrid`]: AccessRole::TreeGrid
193    pub fn set_col_count(&mut self, count: usize) {
194        self.with_access(|a| a.set_state(AccessState::ColCount(count)))
195    }
196
197    /// Sets the widget's column index in the parent table or grid.
198    pub fn set_col_index(&mut self, index: usize) {
199        self.with_access(|a| a.set_state(AccessState::ColIndex(index)))
200    }
201
202    /// Sets the number of columns spanned by the widget in the parent table or grid.
203    pub fn set_col_span(&mut self, span: usize) {
204        self.with_access(|a| a.set_state(AccessState::ColSpan(span)))
205    }
206
207    /// Sets the total number of rows in a [`Table`], [`Grid`], or [`TreeGrid`] when not all rows are present in tree.
208    ///
209    /// The value `0` indicates that not all rows are in the widget and the application cannot determinate the exact number.
210    ///
211    /// [`Table`]: AccessRole::Table
212    /// [`Grid`]: AccessRole::Grid
213    /// [`TreeGrid`]: AccessRole::TreeGrid
214    pub fn set_row_count(&mut self, count: usize) {
215        self.with_access(|a| a.set_state(AccessState::RowCount(count)))
216    }
217
218    /// Sets the widget's row index in the parent table or grid.
219    pub fn set_row_index(&mut self, index: usize) {
220        self.with_access(|a| a.set_state(AccessState::RowIndex(index)))
221    }
222
223    /// Sets the number of rows spanned by the widget in the parent table or grid.
224    pub fn set_row_span(&mut self, span: usize) {
225        self.with_access(|a| a.set_state(AccessState::RowSpan(span)))
226    }
227
228    /// Sets the number of items in the current set of list items or tree items when not all items in the set are present in the tree.
229    pub fn set_item_count(&mut self, count: usize) {
230        self.with_access(|a| a.set_state(AccessState::ItemCount(count)))
231    }
232
233    /// Sets the widget's number or position in the current set of list items or tree items when not all items are present in the tree.
234    pub fn set_item_index(&mut self, index: usize) {
235        self.with_access(|a| a.set_state(AccessState::ItemIndex(index)))
236    }
237
238    /// Sets if the widget is modal when displayed.
239    pub fn flag_modal(&mut self) {
240        self.with_access(|a| a.set_state(AccessState::Modal))
241    }
242
243    /// Defines the language used by screen-readers to read text in this widget and descendants.
244    pub fn set_lang(&mut self, lang: LanguageIdentifier) {
245        self.with_access(|a| a.set_state(AccessState::Lang(lang)))
246    }
247
248    /// Sets the amount scrolled horizontally if allowed.
249    ///
250    /// The `normal_x` value can be a read-only variable, the variable can be updated without needing to rebuild
251    /// info for every pixel scrolled, if the view-process requires access info the value is updated every render
252    /// together with the widget bounds updates.
253    ///
254    /// The value must be normalized in the 0..=1 range, 0 is showing the content leftmost edge, 1 is showing
255    /// the content the rightmost edge.
256    pub fn set_scroll_horizontal(&mut self, normal_x: impl IntoVar<Factor>) {
257        let normal_x = normal_x.into_var();
258        self.with_access(|a| a.set_state_source(AccessStateSource::ScrollHorizontal(normal_x)))
259    }
260
261    /// Sets the amount scrolled vertically if allowed.
262    ///
263    /// The `normal_y` value can be a read-only variable, the variable can be updated without needing to rebuild
264    /// info for every pixel scrolled, if the view-process requires access info the value is updated every render
265    /// together with the widget bounds updates.
266    ///
267    /// The value must be normalized in the 0..=1 range, 0 is showing the content topmost edge, 1 is showing
268    /// the content the bottommost edge.
269    pub fn set_scroll_vertical(&mut self, normal_y: impl IntoVar<Factor>) {
270        let normal_y = normal_y.into_var();
271        self.with_access(|a| a.set_state_source(AccessStateSource::ScrollVertical(normal_y)))
272    }
273
274    /// Push a widget whose contents or presence are controlled by this widget.
275    pub fn push_controls(&mut self, controlled_id: impl Into<WidgetId>) {
276        let controlled_id = controlled_id.into();
277        self.with_access(|a| {
278            for state in &mut a.state {
279                if let AccessState::Controls(c) = state {
280                    c.push(controlled_id.into());
281                    return;
282                }
283            }
284            a.state.push(AccessState::Controls(vec![controlled_id.into()]))
285        })
286    }
287
288    /// Push a widget that describes this widget.
289    pub fn push_described_by(&mut self, descriptor_id: impl Into<WidgetId>) {
290        let descriptor_id = descriptor_id.into();
291        self.with_access(|a| {
292            for state in &mut a.state {
293                if let AccessState::DescribedBy(c) = state {
294                    c.push(descriptor_id.into());
295                    return;
296                }
297            }
298            a.state.push(AccessState::DescribedBy(vec![descriptor_id.into()]))
299        })
300    }
301
302    /// Set a widget that is described-by this widget.
303    ///
304    /// When access info for the view-process is build this is converted to a described-by entry. Note
305    /// that only updated widgets are send to the view-process, so if this relation is dynamic you must
306    /// request info rebuild for the previous and new `target_id` to ensure they update correctly.
307    pub fn set_describes(&mut self, target_id: impl Into<WidgetId>) {
308        let target_id = target_id.into();
309        self.with_access(|a| {
310            for state in &mut a.inverse_state {
311                if let InverseAccessState::Describes(t) = state {
312                    *t = target_id;
313                    return;
314                }
315            }
316            a.inverse_state.push(InverseAccessState::Describes(target_id));
317        })
318    }
319
320    /// Push a widget that provide additional information related to this widget.
321    pub fn push_details(&mut self, detail_id: impl Into<WidgetId>) {
322        let detail_id = detail_id.into();
323        self.with_access(|a| {
324            for state in &mut a.state {
325                if let AccessState::Details(c) = state {
326                    c.push(detail_id.into());
327                    return;
328                }
329            }
330            a.state.push(AccessState::Details(vec![detail_id.into()]))
331        })
332    }
333
334    /// Push a widget that provide additional information related to this widget.
335    pub fn push_labelled_by(&mut self, label_id: impl Into<WidgetId>) {
336        let label_id = label_id.into();
337        self.with_access(|a| {
338            for state in &mut a.state {
339                if let AccessState::LabelledBy(c) = state {
340                    c.push(label_id.into());
341                    return;
342                }
343            }
344            a.state.push(AccessState::LabelledBy(vec![label_id.into()]))
345        })
346    }
347
348    /// Set a widget that is labelled-by this widget.
349    ///
350    /// When access info for the view-process is build this is converted to a labelled-by entry. Note
351    /// that only updated widgets are send to the view-process, so if this relation is dynamic you must
352    /// request info rebuild for the previous and new `target_id` to ensure they update correctly.
353    pub fn set_labels(&mut self, target_id: impl Into<WidgetId>) {
354        let target_id = target_id.into();
355        self.with_access(|a| {
356            for state in &mut a.inverse_state {
357                if let InverseAccessState::Labels(t) = state {
358                    *t = target_id;
359                    return;
360                }
361            }
362            a.inverse_state.push(InverseAccessState::Labels(target_id));
363        })
364    }
365
366    /// Push a widget that is a *child* of this widget, but is not already a child in the info tree.
367    pub fn push_owns(&mut self, owned_id: impl Into<WidgetId>) {
368        let owned_id = owned_id.into();
369        self.with_access(|a| {
370            for state in &mut a.state {
371                if let AccessState::Owns(c) = state {
372                    c.push(owned_id.into());
373                    return;
374                }
375            }
376            a.state.push(AccessState::Owns(vec![owned_id.into()]))
377        })
378    }
379
380    /// Push an option for next widget read that is not the next logical widget already.
381    pub fn push_flows_to(&mut self, next_id: impl Into<WidgetId>) {
382        let next_id = next_id.into();
383        self.with_access(|a| {
384            for state in &mut a.state {
385                if let AccessState::FlowTo(c) = state {
386                    c.push(next_id.into());
387                    return;
388                }
389            }
390            a.state.push(AccessState::FlowTo(vec![next_id.into()]))
391        })
392    }
393
394    /// Uses the accessible children as [`labelled_by`].
395    ///
396    /// [`labelled_by`]: WidgetAccessInfo::labelled_by
397    pub fn flag_labelled_by_child(&mut self) {
398        self.with_access(|a| a.set_state(AccessState::LabelledByChild))
399    }
400
401    /// Exclude the widget and descendants from the view-process and screen readers.
402    ///
403    /// Note that the accessibility info for the widget and descendants is still collected and
404    /// available in the app-process.
405    pub fn flag_inaccessible(&mut self) {
406        self.builder.flag_meta(*INACCESSIBLE_ID);
407    }
408
409    /// Register a `handler` that is called every time view-process access info is build from the current widget,
410    /// the handler can modify the view info.
411    pub fn on_access_build(&mut self, handler: impl Fn(AccessBuildArgs) + Send + Sync + 'static) {
412        let handler = Box::new(handler);
413        self.with_access(|a| a.build_handlers.push(handler));
414    }
415}
416
417impl WidgetInfoTree {
418    /// If this tree contains accessibility information.
419    ///
420    /// If accessibility is enabled for the window and will stay enabled for its lifetime.
421    pub fn access_enabled(&self) -> AccessEnabled {
422        self.0.access_enabled
423    }
424
425    /// Build an access tree from the info tree.
426    ///
427    /// If not [`access_enabled`] returns a placeholder tree with only the root node.
428    ///
429    /// [`access_enabled`]: Self::access_enabled
430    pub fn to_access_tree(&self) -> zng_view_api::access::AccessTree {
431        let mut builder = zng_view_api::access::AccessTreeBuilder::default();
432        if self.0.access_enabled.is_enabled() {
433            // no panic cause root role is always set by the builder.
434            let inverse = self.collect_inverse_state();
435            self.root().access().unwrap().to_access_info(&inverse, &mut builder);
436        } else {
437            builder.push(zng_view_api::access::AccessNode::new(
438                self.root().id().into(),
439                Some(AccessRole::Application),
440            ));
441        }
442        builder.build()
443    }
444
445    /// Build partial or full access trees for updated widgets.
446    ///
447    /// Returns `None` if not [`access_enabled`] or no access info has changed. The [`focused`] value is always set
448    /// to the root ID, it must be changed to the correct focused widget.
449    ///
450    /// This is usually called by window implementers just after the next frame after info rebuild. Note that these
451    /// updates will also include [`to_access_updates_bounds`].
452    ///
453    /// [`access_enabled`]: Self::access_enabled
454    /// [`focused`]: zng_view_api::access::AccessTreeUpdate::focused
455    /// [`to_access_updates_bounds`]: Self::to_access_updates_bounds
456    pub fn to_access_updates(&self, prev_tree: &Self) -> Option<zng_view_api::access::AccessTreeUpdate> {
457        let is_enabled = self.access_enabled().is_enabled();
458        let root_id = self.root().id().into();
459        if is_enabled && !prev_tree.access_enabled().is_enabled() {
460            // first update after access enabled
461            return Some(zng_view_api::access::AccessTreeUpdate::new(
462                vec![self.to_access_tree()],
463                Some(root_id),
464                root_id,
465            ));
466        }
467
468        if is_enabled {
469            let inverse = self.collect_inverse_state();
470            let mut updates = vec![];
471            self.root().access().unwrap().to_access_updates(prev_tree, &inverse, &mut updates);
472            if !updates.is_empty() {
473                return Some(zng_view_api::access::AccessTreeUpdate::new(updates, None, root_id));
474            }
475        }
476
477        None
478    }
479
480    /// Build partial access trees for widgets that changed transform, size or visibility.
481    ///
482    /// Returns `None` if not [`access_enabled`] or no transform/visibility changed. The [`focused`] value is always set
483    /// to the root ID, it must be changed to the correct focused widget.
484    ///
485    /// This is usually called by window implementers after each frame that is not [`to_access_updates`].
486    ///
487    /// [`access_enabled`]: Self::access_enabled
488    /// [`focused`]: zng_view_api::access::AccessTreeUpdate::focused
489    /// [`to_access_updates`]: Self::to_access_updates
490    pub fn to_access_updates_bounds(&self) -> Option<zng_view_api::access::AccessTreeUpdate> {
491        let is_enabled = self.access_enabled().is_enabled();
492        let root_id = self.root().id().into();
493
494        if is_enabled && {
495            let frame = self.0.frame.read();
496            frame.stats.bounds_updated_frame == frame.stats.last_frame || frame.stats.vis_updated_frame == frame.stats.last_frame
497        } {
498            let inverse = self.collect_inverse_state();
499            let mut updates = vec![];
500            self.root().access().unwrap().to_access_updates_bounds(&inverse, &mut updates);
501            if !updates.is_empty() {
502                return Some(zng_view_api::access::AccessTreeUpdate::new(updates, None, root_id));
503            }
504        }
505
506        None
507    }
508
509    fn collect_inverse_state(&self) -> InverseAccess {
510        let mut state = InverseAccess::default();
511        for wgt in self.root().self_and_descendants() {
512            if let Some(a) = wgt.access() {
513                if let Some(t) = a.labels() {
514                    state.labelled_by.entry(t.id()).or_default().push(wgt.id());
515                }
516                if let Some(t) = a.describes() {
517                    state.described_by.entry(t.id()).or_default().push(wgt.id());
518                }
519            }
520        }
521        state
522    }
523}
524
525impl WidgetInfo {
526    /// Accessibility info, if the widget is accessible.
527    ///
528    /// The widget is accessible only if [`access_enabled`] and some accessibility metadata was set on the widget.
529    ///
530    /// [`access_enabled`]: crate::widget::info::WidgetInfoTree::access_enabled
531    pub fn access(&self) -> Option<WidgetAccessInfo> {
532        if self.tree.access_enabled().is_enabled() && self.meta().contains(*ACCESS_INFO_ID) {
533            Some(WidgetAccessInfo { info: self.clone() })
534        } else {
535            None
536        }
537    }
538
539    /// Descendant branches that have accessibility info.
540    ///
541    /// The iterator enters descendants only until it finds a node that has access info, these nodes are yielded.
542    pub fn access_children(&self) -> impl Iterator<Item = WidgetAccessInfo> {
543        self.descendants()
544            .tree_filter(|w| {
545                if w.access().is_some() {
546                    super::TreeFilter::SkipDescendants
547                } else {
548                    super::TreeFilter::Skip
549                }
550            })
551            .map(|w| w.access().unwrap())
552    }
553
554    fn access_children_ids(&self, is_prev: bool) -> Vec<zng_view_api::access::AccessNodeId> {
555        self.access_children()
556            .filter_map(|w| {
557                if w.is_local_accessible() {
558                    if is_prev && w.access().view_bounds.lock().is_none() {
559                        // was collapsed
560                        None
561                    } else {
562                        Some(w.info.id().into())
563                    }
564                } else {
565                    None
566                }
567            })
568            .collect()
569    }
570
571    /// First ancestor that is accessible.
572    pub fn access_parent(&self) -> Option<WidgetAccessInfo> {
573        self.ancestors().find_map(|w| w.access())
574    }
575}
576
577/// Accessibility info for a widget.
578pub struct WidgetAccessInfo {
579    info: WidgetInfo,
580}
581macro_rules! get_state {
582    ($self:ident.$Discriminant:ident) => {
583        get_state!($self, state, AccessState, $Discriminant)
584    };
585    ($self:ident.source.$Discriminant:ident) => {
586        get_state!($self, state_source, AccessStateSource, $Discriminant)
587    };
588    ($self:ident.inverse.$Discriminant:ident) => {
589        get_state!($self, inverse_state, InverseAccessState, $Discriminant)
590    };
591    ($self:ident, $state:ident, $State:ident, $Discriminant:ident) => {
592        $self
593            .access()
594            .$state
595            .iter()
596            .find_map(|a| if let $State::$Discriminant(value) = a { Some(value) } else { None })
597    };
598}
599macro_rules! has_state {
600    ($self:ident.$Discriminant:ident) => {
601        $self.access().state.iter().any(|a| matches!(a, AccessState::$Discriminant))
602    };
603}
604macro_rules! get_widgets {
605    ($self:ident.$Discriminant:ident) => {
606        $self
607            .access()
608            .state
609            .iter()
610            .find_map(|a| {
611                if let AccessState::$Discriminant(ids) = a {
612                    Some(ids.iter().filter_map(|id| {
613                        let id = WidgetId::from_raw(id.0);
614                        $self.info.tree.get(id)
615                    }))
616                } else {
617                    None
618                }
619            })
620            .into_iter()
621            .flatten()
622    };
623}
624impl WidgetAccessInfo {
625    /// Full widget info.
626    pub fn info(&self) -> &WidgetInfo {
627        &self.info
628    }
629
630    fn access(&self) -> &AccessInfo {
631        self.info.meta().req(*ACCESS_INFO_ID)
632    }
633
634    /// Accessibility role of the widget.
635    pub fn role(&self) -> Option<AccessRole> {
636        self.access().role
637    }
638
639    /// Accessibility commands supported by the widget.
640    pub fn commands(&self) -> &[AccessCmdName] {
641        &self.access().commands
642    }
643
644    /// How input text triggers display of one or more predictions of the user's intended value.
645    pub fn auto_complete(&self) -> Option<AutoComplete> {
646        get_state!(self.AutoComplete).copied()
647    }
648
649    /// If the widget is checked (`Some(true)`), unchecked (`Some(false)`), or if the checked status is indeterminate (`None`).
650    ///
651    /// Note that the value is wrapped in another `Option<_>` that indicates if it was set or not.
652    pub fn checked(&self) -> Option<Option<bool>> {
653        get_state!(self.Checked).copied()
654    }
655
656    /// Kind of current item the widget represents.
657    pub fn current(&self) -> Option<CurrentKind> {
658        get_state!(self.Current).copied()
659    }
660
661    /// Gets the invalid widget that this widget is an error message for.
662    pub fn error_message(&self) -> Option<WidgetInfo> {
663        let id = get_state!(self.ErrorMessage)?;
664        let id = WidgetId::from_raw(id.0);
665        self.info.tree.get(id)
666    }
667
668    /// Identifies the currently active widget when focus is on a composite widget.
669    pub fn active_descendant(&self) -> Option<WidgetInfo> {
670        let id = get_state!(self.ActiveDescendant)?;
671        let id = WidgetId::from_raw(id.0);
672        self.info.tree.get(id)
673    }
674
675    /// Gets visibility of related widgets.
676    pub fn expanded(&self) -> Option<bool> {
677        get_state!(self.Expanded).copied()
678    }
679
680    /// Indicates the availability and type of interactive popup widget.
681    pub fn has_popup(&self) -> Option<Popup> {
682        get_state!(self.Popup).copied()
683    }
684
685    /// If the widget data has errors.
686    pub fn invalid(&self) -> Invalid {
687        get_state!(self.Invalid).copied().unwrap_or_else(Invalid::empty)
688    }
689
690    /// Gets the accessibility name explicitly set on this widget.
691    pub fn label(&self) -> Option<Txt> {
692        get_state!(self.source.Label).cloned()
693    }
694
695    /// If the widget children must be used like [`labelled_by`].
696    ///
697    /// [`labelled_by`]: Self::labelled_by
698    pub fn labelled_by_child(&self) -> bool {
699        has_state!(self.LabelledByChild)
700    }
701
702    /// Gets the language of texts inside this widget and descendants.
703    ///
704    /// If not set it is the parents language.
705    pub fn lang(&self) -> Option<LanguageIdentifier> {
706        get_state!(self.Lang).cloned()
707    }
708    /// Normalized (0..1) horizontal scroll, 0 is showing the content leftmost edge, 1 is showing the content the rightmost edge.
709    ///
710    /// Also signals that the content is horizontally scrollable.
711    pub fn scroll_horizontal(&self) -> Option<Var<Factor>> {
712        get_state!(self.source.ScrollHorizontal).cloned()
713    }
714    /// Normalized (0..1) vertical scroll, 0 is showing the content topmost edge, 1 is showing the content the bottommost edge.
715    ///
716    /// Also signals that the content is vertically scrollable.
717    pub fn scroll_vertical(&self) -> Option<Var<Factor>> {
718        get_state!(self.source.ScrollVertical).cloned()
719    }
720
721    /// Indicates that the user may select more than one item from the current selectable descendants.
722    pub fn is_multi_selectable(&self) -> bool {
723        has_state!(self.MultiSelectable)
724    }
725
726    /// Indicates whether the widget's orientation is horizontal, vertical, or unknown/ambiguous.
727    pub fn orientation(&self) -> Option<Orientation> {
728        get_state!(self.Orientation).copied()
729    }
730
731    /// Short hint (a word or short phrase) intended to help the user with data entry when a form control has no value.
732    pub fn placeholder(&self) -> Option<Txt> {
733        get_state!(self.source.Placeholder).cloned()
734    }
735
736    /// Indicates that the widget is not editable, but is otherwise operable.
737    pub fn is_read_only(&self) -> bool {
738        has_state!(self.ReadOnly)
739    }
740
741    /// Indicates that user input is required on the widget before a form may be submitted.
742    pub fn is_required(&self) -> bool {
743        has_state!(self.Required)
744    }
745
746    /// Defines the hierarchical level of a widget within a structure.
747    pub fn level(&self) -> Option<NonZeroU32> {
748        get_state!(self.Level).copied()
749    }
750
751    /// Indicates that the widget is selected.
752    pub fn is_selected(&self) -> bool {
753        has_state!(self.Selected)
754    }
755
756    /// Indicates if items in a table or grid are sorted in ascending or descending order.
757    pub fn sort(&self) -> Option<SortDirection> {
758        get_state!(self.Sort).copied()
759    }
760
761    /// Maximum value (inclusive).
762    pub fn value_max(&self) -> Option<f64> {
763        get_state!(self.ValueMax).copied()
764    }
765
766    /// Minimum value (inclusive).
767    pub fn value_min(&self) -> Option<f64> {
768        get_state!(self.ValueMin).copied()
769    }
770
771    /// Current value.
772    pub fn value(&self) -> Option<f64> {
773        get_state!(self.Value).copied()
774    }
775
776    /// Current value in a readable format.
777    ///
778    /// Note that this returns `Some(_)` only when a value text was set, [`value`]
779    /// may or may not be set also.
780    ///
781    /// [`value`]: Self::value
782    pub fn value_text(&self) -> Option<Txt> {
783        get_state!(self.source.ValueText).cloned()
784    }
785
786    /// Gets the live indicator, atomic and busy.
787    ///
788    /// See [`AccessState::Live`] for more details.
789    ///
790    /// [`AccessState::Live`]: zng_view_api::access::AccessState::Live
791    pub fn live(&self) -> Option<(LiveIndicator, bool, bool)> {
792        self.access().state.iter().find_map(|s| {
793            if let AccessState::Live { indicator, atomic, busy } = s {
794                Some((*indicator, *atomic, *busy))
795            } else {
796                None
797            }
798        })
799    }
800
801    /// Defines the total number of columns in a [`Table`], [`Grid`], or [`TreeGrid`] when not all columns are present in tree.
802    ///
803    /// The value `0` indicates that not all columns are in the widget and the application cannot determinate the exact number.
804    ///
805    /// [`Table`]: AccessRole::Table
806    /// [`Grid`]: AccessRole::Grid
807    /// [`TreeGrid`]: AccessRole::TreeGrid
808    pub fn col_count(&self) -> Option<usize> {
809        get_state!(self.ColCount).copied()
810    }
811
812    /// Defines a widget's column index in the parent table or grid.
813    pub fn col_index(&self) -> Option<usize> {
814        get_state!(self.ColIndex).copied()
815    }
816
817    /// Defines the number of columns spanned by the widget in the parent table or grid.
818    pub fn col_span(&self) -> Option<usize> {
819        get_state!(self.ColSpan).copied()
820    }
821
822    /// Defines the total number of rows in a [`Table`], [`Grid`], or [`TreeGrid`] when not all rows are present in tree.
823    ///
824    /// The value `0` indicates that not all rows are in the widget and the application cannot determinate the exact number.
825    ///
826    /// [`Table`]: AccessRole::Table
827    /// [`Grid`]: AccessRole::Grid
828    /// [`TreeGrid`]: AccessRole::TreeGrid
829    pub fn row_count(&self) -> Option<usize> {
830        get_state!(self.RowCount).copied()
831    }
832
833    /// Defines a widget's column index in the parent table or grid.
834    pub fn row_index(&self) -> Option<usize> {
835        get_state!(self.RowIndex).copied()
836    }
837
838    /// Defines the number of columns spanned by the widget in the parent table or grid.
839    pub fn row_span(&self) -> Option<usize> {
840        get_state!(self.RowSpan).copied()
841    }
842
843    /// Defines the number of items in the current set of list items or tree items when not all items in the set are present in the tree.
844    pub fn item_count(&self) -> Option<usize> {
845        get_state!(self.ItemCount).copied()
846    }
847
848    /// Defines the widget's number or position in the current set of list items or tree items when not all items are present in the tree.
849    pub fn item_index(&self) -> Option<usize> {
850        get_state!(self.ItemIndex).copied()
851    }
852
853    /// Indicates whether the widget is modal when displayed.
854    pub fn modal(&self) -> bool {
855        has_state!(self.Modal)
856    }
857
858    /// Widget(s) whose contents or presence are controlled by this widget.
859    pub fn controls(&self) -> impl Iterator<Item = WidgetInfo> + '_ {
860        get_widgets!(self.Controls)
861    }
862
863    /// Identifies the widget(s) that describes this widget.
864    pub fn described_by(&self) -> impl Iterator<Item = WidgetInfo> + '_ {
865        get_widgets!(self.DescribedBy)
866    }
867
868    /// Identifies the widget that is described by this widget.
869    ///
870    /// Note that this is not a query for all widgets that have this one in their [`described_by`] list, it is only
871    /// set if it was set explicitly during info build.
872    ///
873    /// [`described_by`]: Self::described_by
874    pub fn describes(&self) -> Option<WidgetInfo> {
875        get_state!(self.inverse.Describes).copied().and_then(|id| self.info.tree().get(id))
876    }
877
878    /// Identifies the widget(s) that provide additional information related to this widget.
879    pub fn details(&self) -> impl Iterator<Item = WidgetInfo> + '_ {
880        get_widgets!(self.Details)
881    }
882
883    /// Identifies the widget(s) that labels the widget it is applied to.
884    pub fn labelled_by(&self) -> impl Iterator<Item = WidgetInfo> + '_ {
885        get_widgets!(self.LabelledBy)
886    }
887
888    /// Identifies the widget that is labelled by this widget.
889    ///
890    /// Note that this is not a query for all widgets that have this one in their [`labelled_by`] list, it is only
891    /// set if it was set explicitly during info build.
892    ///
893    /// [`labelled_by`]: Self::labelled_by
894    pub fn labels(&self) -> Option<WidgetInfo> {
895        get_state!(self.inverse.Labels).copied().and_then(|id| self.info.tree().get(id))
896    }
897
898    /// Extra widgets that are *child* to this widget, but are not descendants on the info tree.
899    pub fn owns(&self) -> impl Iterator<Item = WidgetInfo> + '_ {
900        get_widgets!(self.Owns)
901    }
902
903    /// Options for next widget to read.
904    pub fn flows_to(&self) -> impl Iterator<Item = WidgetInfo> + '_ {
905        get_widgets!(self.FlowTo)
906    }
907
908    /// If the widget and descendants is *visible* in the view-process and screen readers.
909    ///   
910    /// Note that the accessibility info for the widget and descendants is still
911    /// available in the app-process.
912    pub fn is_accessible(&self) -> bool {
913        for wgt in self.info.self_and_ancestors() {
914            if wgt.meta().contains(*INACCESSIBLE_ID) || !self.info.visibility().is_visible() {
915                return false;
916            }
917        }
918        true
919    }
920
921    fn is_local_accessible(&self) -> bool {
922        !self.info.meta().contains(*INACCESSIBLE_ID) && self.info.visibility().is_visible()
923    }
924
925    fn to_access_node_leaf(&self, inverse: &InverseAccess) -> zng_view_api::access::AccessNode {
926        let mut node = zng_view_api::access::AccessNode::new(self.info.id().into(), None);
927        let a = self.access();
928
929        let bounds_info = self.bounds_info();
930        node.transform = bounds_info.transform;
931        node.size = bounds_info.size;
932        *a.view_bounds.lock() = Some(bounds_info);
933
934        node.role = a.role;
935        node.state.clone_from(&a.state);
936        node.state.extend(a.state_source.iter().map(From::from));
937
938        if let Some(lb) = inverse.labelled_by.get(&self.info.id()) {
939            let mut done = false;
940            for state in node.state.iter_mut() {
941                if let AccessState::LabelledBy(l) = state {
942                    l.extend(lb.iter().map(|&id| AccessNodeId::from(id)));
943                    done = true;
944                    break;
945                }
946            }
947            if !done {
948                node.state.push(AccessState::LabelledBy(lb.iter().map(|&id| id.into()).collect()));
949            }
950        }
951        if let Some(ds) = inverse.described_by.get(&self.info.id()) {
952            let mut done = false;
953            for state in node.state.iter_mut() {
954                if let AccessState::DescribedBy(l) = state {
955                    l.extend(ds.iter().map(|&id| AccessNodeId::from(id)));
956                    done = true;
957                    break;
958                }
959            }
960            if !done {
961                node.state.push(AccessState::DescribedBy(ds.iter().map(|&id| id.into()).collect()));
962            }
963        }
964
965        node.commands.clone_from(&a.commands);
966
967        for handler in &a.build_handlers {
968            handler(AccessBuildArgs {
969                widget: self,
970                node: &mut node,
971            });
972        }
973
974        node
975    }
976
977    fn bounds_info(&self) -> ViewBoundsInfo {
978        let bounds = self.info.bounds_info();
979        let undo_parent_transform = self
980            .info
981            .access_parent()
982            .and_then(|w| w.info.inner_transform().inverse())
983            .unwrap_or_default();
984        let transform = bounds.inner_transform().then(&undo_parent_transform);
985        let size = bounds.inner_size();
986
987        let scroll_h = get_state!(self.source.ScrollHorizontal).map(|x| x.get());
988        let scroll_v = get_state!(self.source.ScrollVertical).map(|x| x.get());
989
990        ViewBoundsInfo {
991            transform,
992            size,
993            scroll_h,
994            scroll_v,
995        }
996    }
997
998    fn to_access_info(&self, inverse: &InverseAccess, builder: &mut zng_view_api::access::AccessTreeBuilder) -> bool {
999        if !self.is_local_accessible() {
1000            if self.info.parent().is_none() {
1001                // root node is required (but can be empty)
1002                builder.push(zng_view_api::access::AccessNode::new(self.info.id().into(), self.access().role));
1003            }
1004            *self.access().view_bounds.lock() = None;
1005            return false;
1006        }
1007
1008        let node = builder.push(self.to_access_node_leaf(inverse));
1009
1010        let mut children_len = 0;
1011        let len_before = builder.len();
1012        for child in self.info.access_children() {
1013            if child.to_access_info(inverse, builder) {
1014                children_len += 1;
1015            }
1016        }
1017        let descendants_len = (builder.len() - len_before) as u32;
1018
1019        let node = builder.node(node);
1020        node.children_len = children_len;
1021        node.descendants_len = descendants_len;
1022
1023        true
1024    }
1025
1026    fn to_access_updates(&self, prev_tree: &WidgetInfoTree, inverse: &InverseAccess, updates: &mut Vec<zng_view_api::access::AccessTree>) {
1027        if !self.is_local_accessible() {
1028            // not accessible
1029            *self.access().view_bounds.lock() = None;
1030            return;
1031        }
1032
1033        let mut bounds_changed = false;
1034        let mut vis_changed = false;
1035        if self.info.is_reused() {
1036            // no info change, check bounds that can change every render
1037
1038            let bounds = Some(self.bounds_info());
1039            let a = self.access();
1040            let mut prev_bounds = a.view_bounds.lock();
1041
1042            bounds_changed = *prev_bounds != bounds;
1043
1044            if !bounds_changed {
1045                return;
1046            }
1047
1048            vis_changed = prev_bounds.is_none() && bounds.is_some();
1049
1050            *prev_bounds = bounds;
1051        }
1052        let bounds_changed = bounds_changed;
1053        let vis_changed = vis_changed;
1054
1055        if let Some(prev) = prev_tree.get(self.info.id()) {
1056            let was_accessible = !vis_changed && prev.access().map(|w| w.is_local_accessible()).unwrap_or(false);
1057            if was_accessible && let Some(prev) = prev.access() {
1058                let mut children = None;
1059                if bounds_changed || !prev.access().info_eq(self.access()) || {
1060                    // check children and cache result
1061                    let c = self.info.access_children_ids(false);
1062                    let changed = c != prev.info.access_children_ids(true);
1063                    children = Some(c);
1064                    changed
1065                } {
1066                    // changed
1067                    let mut node = self.to_access_node_leaf(inverse);
1068
1069                    for child in self.info.access_children() {
1070                        child.to_access_updates(prev_tree, inverse, updates);
1071                    }
1072
1073                    node.children = children.unwrap_or_else(|| {
1074                        self.info
1075                            .access_children()
1076                            .filter_map(|a| if a.is_local_accessible() { Some(a.info.id().into()) } else { None })
1077                            .collect()
1078                    });
1079
1080                    let mut builder = zng_view_api::access::AccessTreeBuilder::default();
1081                    builder.push(node);
1082                    updates.push(builder.build());
1083
1084                    return;
1085                } else {
1086                    // no change in widget our children, may have change in descendants
1087
1088                    for child in self.info.access_children() {
1089                        child.to_access_updates(prev_tree, inverse, updates);
1090                    }
1091
1092                    return;
1093                }
1094            } else {
1095                // was not accessible
1096            }
1097        }
1098
1099        // insert
1100        let mut builder = zng_view_api::access::AccessTreeBuilder::default();
1101        let insert = self.to_access_info(inverse, &mut builder);
1102        assert!(insert);
1103        updates.push(builder.build());
1104    }
1105
1106    /// Returns `true` if access changed by visibility update.
1107    fn to_access_updates_bounds(&self, inverse: &InverseAccess, updates: &mut Vec<zng_view_api::access::AccessTree>) -> bool {
1108        if self.info.meta().contains(*INACCESSIBLE_ID) {
1109            // not accessible
1110            return false;
1111        }
1112        if !self.info.visibility().is_visible() {
1113            // not accessible because not visible
1114            return self.access().view_bounds.lock().take().is_some();
1115        }
1116
1117        let a = self.access();
1118
1119        let mut vis_changed = false;
1120        let mut update;
1121
1122        let new_bounds = Some(self.bounds_info());
1123        {
1124            let mut bounds = a.view_bounds.lock();
1125            update = *bounds != new_bounds;
1126            if update {
1127                vis_changed = bounds.is_none();
1128                *bounds = new_bounds;
1129            }
1130        };
1131
1132        if vis_changed {
1133            // branch now accessible
1134            let mut builder = zng_view_api::access::AccessTreeBuilder::default();
1135            let insert = self.to_access_info(inverse, &mut builder);
1136            assert!(insert);
1137            updates.push(builder.build());
1138        } else {
1139            // update if bounds info changed or a child changed visibility
1140
1141            for child in self.info.access_children() {
1142                let child_vis_changed = child.to_access_updates_bounds(inverse, updates);
1143                update |= child_vis_changed;
1144            }
1145
1146            if update {
1147                let mut node = self.to_access_node_leaf(inverse);
1148                node.children = self
1149                    .info
1150                    .access_children()
1151                    .filter_map(|a| if a.is_local_accessible() { Some(a.info.id().into()) } else { None })
1152                    .collect();
1153
1154                let mut builder = zng_view_api::access::AccessTreeBuilder::default();
1155                builder.push(node);
1156                updates.push(builder.build());
1157            }
1158        }
1159
1160        vis_changed
1161    }
1162}
1163
1164#[derive(PartialEq, Debug, Clone, Copy)]
1165struct ViewBoundsInfo {
1166    transform: PxTransform,
1167    size: PxSize,
1168    scroll_h: Option<Factor>,
1169    scroll_v: Option<Factor>,
1170}
1171
1172#[derive(Default)]
1173struct AccessInfo {
1174    role: Option<AccessRole>,
1175    commands: Vec<AccessCmdName>,
1176    state: Vec<AccessState>,
1177    state_source: Vec<AccessStateSource>,
1178    inverse_state: Vec<InverseAccessState>,
1179
1180    view_bounds: Mutex<Option<ViewBoundsInfo>>,
1181    build_handlers: Vec<Box<dyn Fn(AccessBuildArgs) + Send + Sync>>,
1182}
1183impl AccessInfo {
1184    fn set_state(&mut self, state: AccessState) {
1185        let discriminant = std::mem::discriminant(&state);
1186        if let Some(present) = self.state.iter_mut().find(|s| std::mem::discriminant(&**s) == discriminant) {
1187            *present = state;
1188        } else {
1189            self.state.push(state);
1190        }
1191    }
1192
1193    fn set_state_source(&mut self, state: AccessStateSource) {
1194        let discriminant = std::mem::discriminant(&state);
1195        if let Some(present) = self.state_source.iter_mut().find(|s| std::mem::discriminant(&**s) == discriminant) {
1196            *present = state;
1197        } else {
1198            self.state_source.push(state);
1199        }
1200    }
1201
1202    fn info_eq(&self, other: &Self) -> bool {
1203        self.role == other.role && self.commands == other.commands && self.state == other.state && self.state_source == other.state_source
1204    }
1205}
1206
1207enum AccessStateSource {
1208    Label(Txt),
1209    Placeholder(Txt),
1210    ValueText(Txt),
1211    ScrollHorizontal(Var<Factor>),
1212    ScrollVertical(Var<Factor>),
1213}
1214impl PartialEq for AccessStateSource {
1215    fn eq(&self, other: &Self) -> bool {
1216        match (self, other) {
1217            (Self::Label(l0), Self::Label(r0)) => l0 == r0,
1218            (Self::Placeholder(l0), Self::Placeholder(r0)) => l0 == r0,
1219            (Self::ValueText(l0), Self::ValueText(r0)) => l0 == r0,
1220            // values equality not done here, see `ViewBoundsInfo` usage
1221            (Self::ScrollHorizontal(l0), Self::ScrollHorizontal(r0)) => l0.var_eq(r0),
1222            (Self::ScrollVertical(l0), Self::ScrollVertical(r0)) => l0.var_eq(r0),
1223            _ => false,
1224        }
1225    }
1226}
1227impl From<&AccessStateSource> for AccessState {
1228    fn from(value: &AccessStateSource) -> Self {
1229        match value {
1230            AccessStateSource::Label(l) => AccessState::Label(l.clone()),
1231            AccessStateSource::Placeholder(p) => AccessState::Placeholder(p.clone()),
1232            AccessStateSource::ValueText(v) => AccessState::ValueText(v.clone()),
1233            AccessStateSource::ScrollHorizontal(x) => AccessState::ScrollHorizontal(x.get().0),
1234            AccessStateSource::ScrollVertical(y) => AccessState::ScrollVertical(y.get().0),
1235        }
1236    }
1237}
1238
1239enum InverseAccessState {
1240    Labels(WidgetId),
1241    Describes(WidgetId),
1242}
1243
1244#[derive(Default)]
1245struct InverseAccess {
1246    labelled_by: IdMap<WidgetId, Vec<WidgetId>>,
1247    described_by: IdMap<WidgetId, Vec<WidgetId>>,
1248}
1249
1250static_id! {
1251    static ref ACCESS_INFO_ID: StateId<AccessInfo>;
1252    static ref INACCESSIBLE_ID: StateId<()>;
1253}
1254
1255bitflags::bitflags! {
1256    /// Defines how accessibility info is enabled.
1257    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
1258    #[serde(transparent)]
1259    pub struct AccessEnabled: u8 {
1260        /// Access info is collected in the app-process and is available in ([`WidgetInfo::access`]).
1261        const APP = 0b01;
1262        /// Access info is send to the view-process because it was requested by an external tool, probably a screen reader.
1263        const VIEW = 0b11;
1264    }
1265}
1266impl AccessEnabled {
1267    /// Is enabled in app at least.
1268    pub fn is_enabled(self) -> bool {
1269        !self.is_empty()
1270    }
1271
1272    /// Is not enabled in app nor view.
1273    pub fn is_disabled(self) -> bool {
1274        self.is_empty()
1275    }
1276}
1277
1278/// Arguments for [`on_access_build`] handlers.
1279///
1280/// [`on_access_build`]: WidgetAccessInfoBuilder::on_access_build
1281#[non_exhaustive]
1282pub struct AccessBuildArgs<'a> {
1283    /// Widget that is converting to view info.
1284    pub widget: &'a WidgetAccessInfo,
1285    /// Partially build view info, does not include children info.
1286    pub node: &'a mut zng_view_api::access::AccessNode,
1287}