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::{BoxedVar, 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().boxed();
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().boxed();
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 {
462                updates: vec![self.to_access_tree()],
463                full_root: Some(root_id),
464                focused: 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 {
474                    updates,
475                    full_root: None,
476                    focused: root_id,
477                });
478            }
479        }
480
481        None
482    }
483
484    /// Build partial access trees for widgets that changed transform, size or visibility.
485    ///
486    /// Returns `None` if not [`access_enabled`] or no transform/visibility changed. The [`focused`] value is always set
487    /// to the root ID, it must be changed to the correct focused widget.
488    ///
489    /// This is usually called by window implementers after each frame that is not [`to_access_updates`].
490    ///
491    /// [`access_enabled`]: Self::access_enabled
492    /// [`focused`]: zng_view_api::access::AccessTreeUpdate::focused
493    /// [`to_access_updates`]: Self::to_access_updates
494    pub fn to_access_updates_bounds(&self) -> Option<zng_view_api::access::AccessTreeUpdate> {
495        let is_enabled = self.access_enabled().is_enabled();
496        let root_id = self.root().id().into();
497
498        if is_enabled && {
499            let frame = self.0.frame.read();
500            frame.stats.bounds_updated_frame == frame.stats.last_frame || frame.stats.vis_updated_frame == frame.stats.last_frame
501        } {
502            let inverse = self.collect_inverse_state();
503            let mut updates = vec![];
504            self.root().access().unwrap().to_access_updates_bounds(&inverse, &mut updates);
505            if !updates.is_empty() {
506                return Some(zng_view_api::access::AccessTreeUpdate {
507                    updates,
508                    full_root: None,
509                    focused: root_id,
510                });
511            }
512        }
513
514        None
515    }
516
517    fn collect_inverse_state(&self) -> InverseAccess {
518        let mut state = InverseAccess::default();
519        for wgt in self.root().self_and_descendants() {
520            if let Some(a) = wgt.access() {
521                if let Some(t) = a.labels() {
522                    state.labelled_by.entry(t.id()).or_default().push(wgt.id());
523                }
524                if let Some(t) = a.describes() {
525                    state.described_by.entry(t.id()).or_default().push(wgt.id());
526                }
527            }
528        }
529        state
530    }
531}
532
533impl WidgetInfo {
534    /// Accessibility info, if the widget is accessible.
535    ///
536    /// The widget is accessible only if [`access_enabled`] and some accessibility metadata was set on the widget.
537    ///
538    /// [`access_enabled`]: crate::widget::info::WidgetInfoTree::access_enabled
539    pub fn access(&self) -> Option<WidgetAccessInfo> {
540        if self.tree.access_enabled().is_enabled() && self.meta().contains(*ACCESS_INFO_ID) {
541            Some(WidgetAccessInfo { info: self.clone() })
542        } else {
543            None
544        }
545    }
546
547    /// Descendant branches that have accessibility info.
548    ///
549    /// The iterator enters descendants only until it finds a node that has access info, these nodes are yielded.
550    pub fn access_children(&self) -> impl Iterator<Item = WidgetAccessInfo> {
551        self.descendants()
552            .tree_filter(|w| {
553                if w.access().is_some() {
554                    super::TreeFilter::SkipDescendants
555                } else {
556                    super::TreeFilter::Skip
557                }
558            })
559            .map(|w| w.access().unwrap())
560    }
561
562    fn access_children_ids(&self, is_prev: bool) -> Vec<zng_view_api::access::AccessNodeId> {
563        self.access_children()
564            .filter_map(|w| {
565                if w.is_local_accessible() {
566                    if is_prev && w.access().view_bounds.lock().is_none() {
567                        // was collapsed
568                        None
569                    } else {
570                        Some(w.info.id().into())
571                    }
572                } else {
573                    None
574                }
575            })
576            .collect()
577    }
578
579    /// First ancestor that is accessible.
580    pub fn access_parent(&self) -> Option<WidgetAccessInfo> {
581        self.ancestors().find_map(|w| w.access())
582    }
583}
584
585/// Accessibility info for a widget.
586pub struct WidgetAccessInfo {
587    info: WidgetInfo,
588}
589macro_rules! get_state {
590    ($self:ident.$Discriminant:ident) => {
591        get_state!($self, state, AccessState, $Discriminant)
592    };
593    ($self:ident.source.$Discriminant:ident) => {
594        get_state!($self, state_source, AccessStateSource, $Discriminant)
595    };
596    ($self:ident.inverse.$Discriminant:ident) => {
597        get_state!($self, inverse_state, InverseAccessState, $Discriminant)
598    };
599    ($self:ident, $state:ident, $State:ident, $Discriminant:ident) => {
600        $self
601            .access()
602            .$state
603            .iter()
604            .find_map(|a| if let $State::$Discriminant(value) = a { Some(value) } else { None })
605    };
606}
607macro_rules! has_state {
608    ($self:ident.$Discriminant:ident) => {
609        $self.access().state.iter().any(|a| matches!(a, AccessState::$Discriminant))
610    };
611}
612macro_rules! get_widgets {
613    ($self:ident.$Discriminant:ident) => {
614        $self
615            .access()
616            .state
617            .iter()
618            .find_map(|a| {
619                if let AccessState::$Discriminant(ids) = a {
620                    Some(ids.iter().filter_map(|id| {
621                        let id = WidgetId::from_raw(id.0);
622                        $self.info.tree.get(id)
623                    }))
624                } else {
625                    None
626                }
627            })
628            .into_iter()
629            .flatten()
630    };
631}
632impl WidgetAccessInfo {
633    /// Full widget info.
634    pub fn info(&self) -> &WidgetInfo {
635        &self.info
636    }
637
638    fn access(&self) -> &AccessInfo {
639        self.info.meta().req(*ACCESS_INFO_ID)
640    }
641
642    /// Accessibility role of the widget.
643    pub fn role(&self) -> Option<AccessRole> {
644        self.access().role
645    }
646
647    /// Accessibility commands supported by the widget.
648    pub fn commands(&self) -> &[AccessCmdName] {
649        &self.access().commands
650    }
651
652    /// How input text triggers display of one or more predictions of the user's intended value.
653    pub fn auto_complete(&self) -> Option<AutoComplete> {
654        get_state!(self.AutoComplete).copied()
655    }
656
657    /// If the widget is checked (`Some(true)`), unchecked (`Some(false)`), or if the checked status is indeterminate (`None`).
658    ///
659    /// Note that the value is wrapped in another `Option<_>` that indicates if it was set or not.
660    pub fn checked(&self) -> Option<Option<bool>> {
661        get_state!(self.Checked).copied()
662    }
663
664    /// Kind of current item the widget represents.
665    pub fn current(&self) -> Option<CurrentKind> {
666        get_state!(self.Current).copied()
667    }
668
669    /// Gets the invalid widget that this widget is an error message for.
670    pub fn error_message(&self) -> Option<WidgetInfo> {
671        let id = get_state!(self.ErrorMessage)?;
672        let id = WidgetId::from_raw(id.0);
673        self.info.tree.get(id)
674    }
675
676    /// Identifies the currently active widget when focus is on a composite widget.
677    pub fn active_descendant(&self) -> Option<WidgetInfo> {
678        let id = get_state!(self.ActiveDescendant)?;
679        let id = WidgetId::from_raw(id.0);
680        self.info.tree.get(id)
681    }
682
683    /// Gets visibility of related widgets.
684    pub fn expanded(&self) -> Option<bool> {
685        get_state!(self.Expanded).copied()
686    }
687
688    /// Indicates the availability and type of interactive popup widget.
689    pub fn has_popup(&self) -> Option<Popup> {
690        get_state!(self.Popup).copied()
691    }
692
693    /// If the widget data has errors.
694    pub fn invalid(&self) -> Invalid {
695        get_state!(self.Invalid).copied().unwrap_or_else(Invalid::empty)
696    }
697
698    /// Gets the accessibility name explicitly set on this widget.
699    pub fn label(&self) -> Option<Txt> {
700        get_state!(self.source.Label).cloned()
701    }
702
703    /// If the widget children must be used like [`labelled_by`].
704    ///
705    /// [`labelled_by`]: Self::labelled_by
706    pub fn labelled_by_child(&self) -> bool {
707        has_state!(self.LabelledByChild)
708    }
709
710    /// Gets the language of texts inside this widget and descendants.
711    ///
712    /// If not set it is the parents language.
713    pub fn lang(&self) -> Option<LanguageIdentifier> {
714        get_state!(self.Lang).cloned()
715    }
716    /// Normalized (0..1) horizontal scroll, 0 is showing the content leftmost edge, 1 is showing the content the rightmost edge.
717    ///
718    /// Also signals that the content is horizontally scrollable.
719    pub fn scroll_horizontal(&self) -> Option<BoxedVar<Factor>> {
720        get_state!(self.source.ScrollHorizontal).cloned()
721    }
722    /// Normalized (0..1) vertical scroll, 0 is showing the content topmost edge, 1 is showing the content the bottommost edge.
723    ///
724    /// Also signals that the content is vertically scrollable.
725    pub fn scroll_vertical(&self) -> Option<BoxedVar<Factor>> {
726        get_state!(self.source.ScrollVertical).cloned()
727    }
728
729    /// Indicates that the user may select more than one item from the current selectable descendants.
730    pub fn is_multi_selectable(&self) -> bool {
731        has_state!(self.MultiSelectable)
732    }
733
734    /// Indicates whether the widget's orientation is horizontal, vertical, or unknown/ambiguous.
735    pub fn orientation(&self) -> Option<Orientation> {
736        get_state!(self.Orientation).copied()
737    }
738
739    /// Short hint (a word or short phrase) intended to help the user with data entry when a form control has no value.
740    pub fn placeholder(&self) -> Option<Txt> {
741        get_state!(self.source.Placeholder).cloned()
742    }
743
744    /// Indicates that the widget is not editable, but is otherwise operable.
745    pub fn is_read_only(&self) -> bool {
746        has_state!(self.ReadOnly)
747    }
748
749    /// Indicates that user input is required on the widget before a form may be submitted.
750    pub fn is_required(&self) -> bool {
751        has_state!(self.Required)
752    }
753
754    /// Defines the hierarchical level of a widget within a structure.
755    pub fn level(&self) -> Option<NonZeroU32> {
756        get_state!(self.Level).copied()
757    }
758
759    /// Indicates that the widget is selected.
760    pub fn is_selected(&self) -> bool {
761        has_state!(self.Selected)
762    }
763
764    /// Indicates if items in a table or grid are sorted in ascending or descending order.
765    pub fn sort(&self) -> Option<SortDirection> {
766        get_state!(self.Sort).copied()
767    }
768
769    /// Maximum value (inclusive).
770    pub fn value_max(&self) -> Option<f64> {
771        get_state!(self.ValueMax).copied()
772    }
773
774    /// Minimum value (inclusive).
775    pub fn value_min(&self) -> Option<f64> {
776        get_state!(self.ValueMin).copied()
777    }
778
779    /// Current value.
780    pub fn value(&self) -> Option<f64> {
781        get_state!(self.Value).copied()
782    }
783
784    /// Current value in a readable format.
785    ///
786    /// Note that this returns `Some(_)` only when a value text was set, [`value`]
787    /// may or may not be set also.
788    ///
789    /// [`value`]: Self::value
790    pub fn value_text(&self) -> Option<Txt> {
791        get_state!(self.source.ValueText).cloned()
792    }
793
794    /// Gets the live indicator, atomic and busy.
795    ///
796    /// See [`AccessState::Live`] for more details.
797    ///
798    /// [`AccessState::Live`]: zng_view_api::access::AccessState::Live
799    pub fn live(&self) -> Option<(LiveIndicator, bool, bool)> {
800        self.access().state.iter().find_map(|s| {
801            if let AccessState::Live { indicator, atomic, busy } = s {
802                Some((*indicator, *atomic, *busy))
803            } else {
804                None
805            }
806        })
807    }
808
809    /// Defines the total number of columns in a [`Table`], [`Grid`], or [`TreeGrid`] when not all columns are present in tree.
810    ///
811    /// The value `0` indicates that not all columns are in the widget and the application cannot determinate the exact number.
812    ///
813    /// [`Table`]: AccessRole::Table
814    /// [`Grid`]: AccessRole::Grid
815    /// [`TreeGrid`]: AccessRole::TreeGrid
816    pub fn col_count(&self) -> Option<usize> {
817        get_state!(self.ColCount).copied()
818    }
819
820    /// Defines a widget's column index in the parent table or grid.
821    pub fn col_index(&self) -> Option<usize> {
822        get_state!(self.ColIndex).copied()
823    }
824
825    /// Defines the number of columns spanned by the widget in the parent table or grid.
826    pub fn col_span(&self) -> Option<usize> {
827        get_state!(self.ColSpan).copied()
828    }
829
830    /// Defines the total number of rows in a [`Table`], [`Grid`], or [`TreeGrid`] when not all rows are present in tree.
831    ///
832    /// The value `0` indicates that not all rows are in the widget and the application cannot determinate the exact number.
833    ///
834    /// [`Table`]: AccessRole::Table
835    /// [`Grid`]: AccessRole::Grid
836    /// [`TreeGrid`]: AccessRole::TreeGrid
837    pub fn row_count(&self) -> Option<usize> {
838        get_state!(self.RowCount).copied()
839    }
840
841    /// Defines a widget's column index in the parent table or grid.
842    pub fn row_index(&self) -> Option<usize> {
843        get_state!(self.RowIndex).copied()
844    }
845
846    /// Defines the number of columns spanned by the widget in the parent table or grid.
847    pub fn row_span(&self) -> Option<usize> {
848        get_state!(self.RowSpan).copied()
849    }
850
851    /// 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.
852    pub fn item_count(&self) -> Option<usize> {
853        get_state!(self.ItemCount).copied()
854    }
855
856    /// 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.
857    pub fn item_index(&self) -> Option<usize> {
858        get_state!(self.ItemIndex).copied()
859    }
860
861    /// Indicates whether the widget is modal when displayed.
862    pub fn modal(&self) -> bool {
863        has_state!(self.Modal)
864    }
865
866    /// Widget(s) whose contents or presence are controlled by this widget.
867    pub fn controls(&self) -> impl Iterator<Item = WidgetInfo> + '_ {
868        get_widgets!(self.Controls)
869    }
870
871    /// Identifies the widget(s) that describes this widget.
872    pub fn described_by(&self) -> impl Iterator<Item = WidgetInfo> + '_ {
873        get_widgets!(self.DescribedBy)
874    }
875
876    /// Identifies the widget that is described by this widget.
877    ///
878    /// Note that this is not a query for all widgets that have this one in their [`described_by`] list, it is only
879    /// set if it was set explicitly during info build.
880    ///
881    /// [`described_by`]: Self::described_by
882    pub fn describes(&self) -> Option<WidgetInfo> {
883        get_state!(self.inverse.Describes).copied().and_then(|id| self.info.tree().get(id))
884    }
885
886    /// Identifies the widget(s) that provide additional information related to this widget.
887    pub fn details(&self) -> impl Iterator<Item = WidgetInfo> + '_ {
888        get_widgets!(self.Details)
889    }
890
891    /// Identifies the widget(s) that labels the widget it is applied to.
892    pub fn labelled_by(&self) -> impl Iterator<Item = WidgetInfo> + '_ {
893        get_widgets!(self.LabelledBy)
894    }
895
896    /// Identifies the widget that is labelled by this widget.
897    ///
898    /// Note that this is not a query for all widgets that have this one in their [`labelled_by`] list, it is only
899    /// set if it was set explicitly during info build.
900    ///
901    /// [`labelled_by`]: Self::labelled_by
902    pub fn labels(&self) -> Option<WidgetInfo> {
903        get_state!(self.inverse.Labels).copied().and_then(|id| self.info.tree().get(id))
904    }
905
906    /// Extra widgets that are *child* to this widget, but are not descendants on the info tree.
907    pub fn owns(&self) -> impl Iterator<Item = WidgetInfo> + '_ {
908        get_widgets!(self.Owns)
909    }
910
911    /// Options for next widget to read.
912    pub fn flows_to(&self) -> impl Iterator<Item = WidgetInfo> + '_ {
913        get_widgets!(self.FlowTo)
914    }
915
916    /// If the widget and descendants is *visible* in the view-process and screen readers.
917    ///   
918    /// Note that the accessibility info for the widget and descendants is still
919    /// available in the app-process.
920    pub fn is_accessible(&self) -> bool {
921        for wgt in self.info.self_and_ancestors() {
922            if wgt.meta().contains(*INACCESSIBLE_ID) || !self.info.visibility().is_visible() {
923                return false;
924            }
925        }
926        true
927    }
928
929    fn is_local_accessible(&self) -> bool {
930        !self.info.meta().contains(*INACCESSIBLE_ID) && self.info.visibility().is_visible()
931    }
932
933    fn to_access_node_leaf(&self, inverse: &InverseAccess) -> zng_view_api::access::AccessNode {
934        let mut node = zng_view_api::access::AccessNode::new(self.info.id().into(), None);
935        let a = self.access();
936
937        let bounds_info = self.bounds_info();
938        node.transform = bounds_info.transform;
939        node.size = bounds_info.size;
940        *a.view_bounds.lock() = Some(bounds_info);
941
942        node.role = a.role;
943        node.state.clone_from(&a.state);
944        node.state.extend(a.state_source.iter().map(From::from));
945
946        if let Some(lb) = inverse.labelled_by.get(&self.info.id()) {
947            let mut done = false;
948            for state in node.state.iter_mut() {
949                if let AccessState::LabelledBy(l) = state {
950                    l.extend(lb.iter().map(|&id| AccessNodeId::from(id)));
951                    done = true;
952                    break;
953                }
954            }
955            if !done {
956                node.state.push(AccessState::LabelledBy(lb.iter().map(|&id| id.into()).collect()));
957            }
958        }
959        if let Some(ds) = inverse.described_by.get(&self.info.id()) {
960            let mut done = false;
961            for state in node.state.iter_mut() {
962                if let AccessState::DescribedBy(l) = state {
963                    l.extend(ds.iter().map(|&id| AccessNodeId::from(id)));
964                    done = true;
965                    break;
966                }
967            }
968            if !done {
969                node.state.push(AccessState::DescribedBy(ds.iter().map(|&id| id.into()).collect()));
970            }
971        }
972
973        node.commands.clone_from(&a.commands);
974
975        for handler in &a.build_handlers {
976            handler(AccessBuildArgs {
977                widget: self,
978                node: &mut node,
979            });
980        }
981
982        node
983    }
984
985    fn bounds_info(&self) -> ViewBoundsInfo {
986        let bounds = self.info.bounds_info();
987        let undo_parent_transform = self
988            .info
989            .access_parent()
990            .and_then(|w| w.info.inner_transform().inverse())
991            .unwrap_or_default();
992        let transform = bounds.inner_transform().then(&undo_parent_transform);
993        let size = bounds.inner_size();
994
995        let scroll_h = get_state!(self.source.ScrollHorizontal).map(|x| x.get());
996        let scroll_v = get_state!(self.source.ScrollVertical).map(|x| x.get());
997
998        ViewBoundsInfo {
999            transform,
1000            size,
1001            scroll_h,
1002            scroll_v,
1003        }
1004    }
1005
1006    fn to_access_info(&self, inverse: &InverseAccess, builder: &mut zng_view_api::access::AccessTreeBuilder) -> bool {
1007        if !self.is_local_accessible() {
1008            if self.info.parent().is_none() {
1009                // root node is required (but can be empty)
1010                builder.push(zng_view_api::access::AccessNode::new(self.info.id().into(), self.access().role));
1011            }
1012            *self.access().view_bounds.lock() = None;
1013            return false;
1014        }
1015
1016        let node = builder.push(self.to_access_node_leaf(inverse));
1017
1018        let mut children_len = 0;
1019        let len_before = builder.len();
1020        for child in self.info.access_children() {
1021            if child.to_access_info(inverse, builder) {
1022                children_len += 1;
1023            }
1024        }
1025        let descendants_len = (builder.len() - len_before) as u32;
1026
1027        let node = builder.node(node);
1028        node.children_len = children_len;
1029        node.descendants_len = descendants_len;
1030
1031        true
1032    }
1033
1034    fn to_access_updates(&self, prev_tree: &WidgetInfoTree, inverse: &InverseAccess, updates: &mut Vec<zng_view_api::access::AccessTree>) {
1035        if !self.is_local_accessible() {
1036            // not accessible
1037            *self.access().view_bounds.lock() = None;
1038            return;
1039        }
1040
1041        let mut bounds_changed = false;
1042        let mut vis_changed = false;
1043        if self.info.is_reused() {
1044            // no info change, check bounds that can change every render
1045
1046            let bounds = Some(self.bounds_info());
1047            let a = self.access();
1048            let mut prev_bounds = a.view_bounds.lock();
1049
1050            bounds_changed = *prev_bounds != bounds;
1051
1052            if !bounds_changed {
1053                return;
1054            }
1055
1056            vis_changed = prev_bounds.is_none() && bounds.is_some();
1057
1058            *prev_bounds = bounds;
1059        }
1060        let bounds_changed = bounds_changed;
1061        let vis_changed = vis_changed;
1062
1063        if let Some(prev) = prev_tree.get(self.info.id()) {
1064            let was_accessible = !vis_changed && prev.access().map(|w| w.is_local_accessible()).unwrap_or(false);
1065            if let (true, Some(prev)) = (was_accessible, prev.access()) {
1066                let mut children = None;
1067                if bounds_changed || !prev.access().info_eq(self.access()) || {
1068                    // check children and cache result
1069                    let c = self.info.access_children_ids(false);
1070                    let changed = c != prev.info.access_children_ids(true);
1071                    children = Some(c);
1072                    changed
1073                } {
1074                    // changed
1075                    let mut node = self.to_access_node_leaf(inverse);
1076
1077                    for child in self.info.access_children() {
1078                        child.to_access_updates(prev_tree, inverse, updates);
1079                    }
1080
1081                    node.children = children.unwrap_or_else(|| {
1082                        self.info
1083                            .access_children()
1084                            .filter_map(|a| if a.is_local_accessible() { Some(a.info.id().into()) } else { None })
1085                            .collect()
1086                    });
1087
1088                    let mut builder = zng_view_api::access::AccessTreeBuilder::default();
1089                    builder.push(node);
1090                    updates.push(builder.build());
1091
1092                    return;
1093                } else {
1094                    // no change in widget our children, may have change in descendants
1095
1096                    for child in self.info.access_children() {
1097                        child.to_access_updates(prev_tree, inverse, updates);
1098                    }
1099
1100                    return;
1101                }
1102            } else {
1103                // was not accessible
1104            }
1105        }
1106
1107        // insert
1108        let mut builder = zng_view_api::access::AccessTreeBuilder::default();
1109        let insert = self.to_access_info(inverse, &mut builder);
1110        assert!(insert);
1111        updates.push(builder.build());
1112    }
1113
1114    /// Returns `true` if access changed by visibility update.
1115    fn to_access_updates_bounds(&self, inverse: &InverseAccess, updates: &mut Vec<zng_view_api::access::AccessTree>) -> bool {
1116        if self.info.meta().contains(*INACCESSIBLE_ID) {
1117            // not accessible
1118            return false;
1119        }
1120        if !self.info.visibility().is_visible() {
1121            // not accessible because not visible
1122            return self.access().view_bounds.lock().take().is_some();
1123        }
1124
1125        let a = self.access();
1126
1127        let mut vis_changed = false;
1128        let mut update;
1129
1130        let new_bounds = Some(self.bounds_info());
1131        {
1132            let mut bounds = a.view_bounds.lock();
1133            update = *bounds != new_bounds;
1134            if update {
1135                vis_changed = bounds.is_none();
1136                *bounds = new_bounds;
1137            }
1138        };
1139
1140        if vis_changed {
1141            // branch now accessible
1142            let mut builder = zng_view_api::access::AccessTreeBuilder::default();
1143            let insert = self.to_access_info(inverse, &mut builder);
1144            assert!(insert);
1145            updates.push(builder.build());
1146        } else {
1147            // update if bounds info changed or a child changed visibility
1148
1149            for child in self.info.access_children() {
1150                let child_vis_changed = child.to_access_updates_bounds(inverse, updates);
1151                update |= child_vis_changed;
1152            }
1153
1154            if update {
1155                let mut node = self.to_access_node_leaf(inverse);
1156                node.children = self
1157                    .info
1158                    .access_children()
1159                    .filter_map(|a| if a.is_local_accessible() { Some(a.info.id().into()) } else { None })
1160                    .collect();
1161
1162                let mut builder = zng_view_api::access::AccessTreeBuilder::default();
1163                builder.push(node);
1164                updates.push(builder.build());
1165            }
1166        }
1167
1168        vis_changed
1169    }
1170}
1171
1172#[derive(PartialEq, Debug, Clone, Copy)]
1173struct ViewBoundsInfo {
1174    transform: PxTransform,
1175    size: PxSize,
1176    scroll_h: Option<Factor>,
1177    scroll_v: Option<Factor>,
1178}
1179
1180#[derive(Default)]
1181struct AccessInfo {
1182    role: Option<AccessRole>,
1183    commands: Vec<AccessCmdName>,
1184    state: Vec<AccessState>,
1185    state_source: Vec<AccessStateSource>,
1186    inverse_state: Vec<InverseAccessState>,
1187
1188    view_bounds: Mutex<Option<ViewBoundsInfo>>,
1189    build_handlers: Vec<Box<dyn Fn(AccessBuildArgs) + Send + Sync>>,
1190}
1191impl AccessInfo {
1192    fn set_state(&mut self, state: AccessState) {
1193        let discriminant = std::mem::discriminant(&state);
1194        if let Some(present) = self.state.iter_mut().find(|s| std::mem::discriminant(&**s) == discriminant) {
1195            *present = state;
1196        } else {
1197            self.state.push(state);
1198        }
1199    }
1200
1201    fn set_state_source(&mut self, state: AccessStateSource) {
1202        let discriminant = std::mem::discriminant(&state);
1203        if let Some(present) = self.state_source.iter_mut().find(|s| std::mem::discriminant(&**s) == discriminant) {
1204            *present = state;
1205        } else {
1206            self.state_source.push(state);
1207        }
1208    }
1209
1210    fn info_eq(&self, other: &Self) -> bool {
1211        self.role == other.role && self.commands == other.commands && self.state == other.state && self.state_source == other.state_source
1212    }
1213}
1214
1215enum AccessStateSource {
1216    Label(Txt),
1217    Placeholder(Txt),
1218    ValueText(Txt),
1219    ScrollHorizontal(BoxedVar<Factor>),
1220    ScrollVertical(BoxedVar<Factor>),
1221}
1222impl PartialEq for AccessStateSource {
1223    fn eq(&self, other: &Self) -> bool {
1224        match (self, other) {
1225            (Self::Label(l0), Self::Label(r0)) => l0 == r0,
1226            (Self::Placeholder(l0), Self::Placeholder(r0)) => l0 == r0,
1227            (Self::ValueText(l0), Self::ValueText(r0)) => l0 == r0,
1228            // values equality not done here, see `ViewBoundsInfo` usage
1229            (Self::ScrollHorizontal(l0), Self::ScrollHorizontal(r0)) => l0.var_ptr() == r0.var_ptr(),
1230            (Self::ScrollVertical(l0), Self::ScrollVertical(r0)) => l0.var_ptr() == r0.var_ptr(),
1231            _ => false,
1232        }
1233    }
1234}
1235impl From<&AccessStateSource> for AccessState {
1236    fn from(value: &AccessStateSource) -> Self {
1237        match value {
1238            AccessStateSource::Label(l) => AccessState::Label(l.clone()),
1239            AccessStateSource::Placeholder(p) => AccessState::Placeholder(p.clone()),
1240            AccessStateSource::ValueText(v) => AccessState::ValueText(v.clone()),
1241            AccessStateSource::ScrollHorizontal(x) => AccessState::ScrollHorizontal(x.get().0),
1242            AccessStateSource::ScrollVertical(y) => AccessState::ScrollVertical(y.get().0),
1243        }
1244    }
1245}
1246
1247enum InverseAccessState {
1248    Labels(WidgetId),
1249    Describes(WidgetId),
1250}
1251
1252#[derive(Default)]
1253struct InverseAccess {
1254    labelled_by: IdMap<WidgetId, Vec<WidgetId>>,
1255    described_by: IdMap<WidgetId, Vec<WidgetId>>,
1256}
1257
1258static_id! {
1259    static ref ACCESS_INFO_ID: StateId<AccessInfo>;
1260    static ref INACCESSIBLE_ID: StateId<()>;
1261}
1262
1263bitflags::bitflags! {
1264    /// Defines how accessibility info is enabled.
1265    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
1266    #[serde(transparent)]
1267    pub struct AccessEnabled: u8 {
1268        /// Access info is collected in the app-process and is available in ([`WidgetInfo::access`]).
1269        const APP = 0b01;
1270        /// Access info is send to the view-process because it was requested by an external tool, probably a screen reader.
1271        const VIEW = 0b11;
1272    }
1273}
1274impl AccessEnabled {
1275    /// Is enabled in app at least.
1276    pub fn is_enabled(self) -> bool {
1277        !self.is_empty()
1278    }
1279
1280    /// Is not enabled in app nor view.
1281    pub fn is_disabled(self) -> bool {
1282        self.is_empty()
1283    }
1284}
1285
1286/// Arguments for [`on_access_build`] handlers.
1287///
1288/// [`on_access_build`]: WidgetAccessInfoBuilder::on_access_build
1289pub struct AccessBuildArgs<'a> {
1290    /// Widget that is converting to view info.
1291    pub widget: &'a WidgetAccessInfo,
1292    /// Partially build view info, does not include children info.
1293    pub node: &'a mut zng_view_api::access::AccessNode,
1294}