zng/access.rs
1//! Accessibility service, events and properties.
2//!
3//! The accessibility API helps external tools to query the state of a widget and issue programmatic commands to it.
4//! This API is mainly used by accessibility assistants like [`NVDA`] to narrate and operate the current screen, but
5//! usage is not limited to accessibility, the access provided to widgets also enables external automation tools and
6//! internal operations such as programmatically clicking a button.
7//!
8//! [`NVDA`]: https://en.wikipedia.org/wiki/NonVisual_Desktop_Access
9//!
10//! # Metadata
11//!
12//! Metadata is collected on demand during info build, there is a small performance impact to this so the access
13//! builder is only available after accessibility was requested at least once for the window.
14//!
15//! ```
16//! use zng::prelude_wgt::*;
17//!
18//! # let _ =
19//! match_node_leaf(|op| match op {
20//! UiNodeOp::Info { info } => {
21//! if let Some(mut a) = info.access() {
22//! // accessibility requested for this window
23//! a.set_label("label");
24//! }
25//! }
26//! _ => {}
27//! })
28//! # ;
29//! ```
30//!
31//! You can also enables access info programmatically using [`WINDOW.enable_access()`], if the view-process did not
32//! request accessibility the window still skips sending the access tree, so the performance impact is minimal.
33//!
34//! ```
35//! use zng::prelude::*;
36//!
37//! # let mut app = APP.defaults().run_headless(false);
38//! # app.doc_test_window(async {
39//! WINDOW.enable_access();
40//!
41//! Window! {
42//! child = Button! { id = "btn-1"; child = Text!("Button 1") };
43//!
44//! widget::on_info_init = hn!(|_| {
45//! let btn_info = WINDOW.info().get("btn-1").unwrap().access().unwrap();
46//! let txt_info = btn_info.info().children().next().unwrap().access().unwrap();
47//!
48//! assert_eq!(None, btn_info.label());
49//! assert!(btn_info.labelled_by_child());
50//! assert_eq!(Some(Txt::from("Button 1")), txt_info.label());
51//! # WINDOW.close();
52//! });
53//! }
54//! # });
55//! ```
56//!
57//! When accessibility info is build you it can be accessed using [`WidgetInfo::access`]. Note that this is a low level
58//! access info, provided as it was set by the widgets, in the example above the *label* value is only found on the text widget,
59//! accessibility tools will use the text label for the button.
60//!
61//! [`WINDOW.enable_access()`]: crate::window::WINDOW_Ext::enable_access
62//! [`WidgetInfo::access`]: crate::widget::info::WidgetInfo::access
63//!
64//! ## Properties
65//!
66//! Properties of this module only define metadata that indicate that the widget implements a certain UI pattern, by
67//! setting a property you must make sure that the widget actually implements said pattern, for this reason most
68//! of the accessibility definitions are provided by the widget implementations.
69//!
70//! In the example below a `TextInput!` widget instance changes its role to [`AccessRole::SearchBox`], the default
71//! role is set by the widget itself to [`AccessRole::TextInput`], this usage of the widget has a more specific role
72//! so it can be changed, in this case it is up to the app developer to actually implement the search.
73//!
74//! ```
75//! use zng::prelude::*;
76//! use zng::access::{access_role, AccessRole};
77//!
78//! # let _scope = APP.defaults();
79//! let search_txt = var(Txt::from(""));
80//! # let _ =
81//! TextInput! {
82//! access_role = AccessRole::SearchBox;
83//! placeholder_txt = "search";
84//! txt = search_txt;
85//! }
86//! # ;
87//! ```
88//!
89//! # Service & Events
90//!
91//! The [`ACCESS`] service provides methods that control widgets by notifying accessibility events. Access events
92//! are handled by widgets even when accessibility is disabled.
93//!
94//! In the example below the button shows and hides the tooltip of a different widget using [`ACCESS.show_tooltip`]
95//! and [`ACCESS.hide_tooltip`].
96//!
97//! ```
98//! use zng::prelude::*;
99//!
100//! let mut show_tooltip = false;
101//! # let _scope = APP.defaults(); let _ =
102//! Window! {
103//! child_align = Align::CENTER;
104//! child = Stack!(top_to_bottom, 50, ui_vec![
105//! Button! {
106//! on_click = hn!(|_| {
107//! use zng::access::ACCESS;
108//!
109//! show_tooltip = !show_tooltip;
110//! if show_tooltip {
111//! ACCESS.show_tooltip(WINDOW.id(), "tooltip-anchor");
112//! } else {
113//! ACCESS.hide_tooltip(WINDOW.id(), "tooltip-anchor");
114//! }
115//! });
116//! child = Text!("Toggle Tooltip");
117//! },
118//! Text! {
119//! id = "tooltip-anchor";
120//! txt = "tooltip anchor";
121//! tooltip = Tip!(Text!("Tooltip"));
122//! }
123//! ])
124//! }
125//! # ;
126//! ```
127//!
128//! [`ACCESS.show_tooltip`]: ACCESS::show_tooltip
129//! [`ACCESS.hide_tooltip`]: ACCESS::hide_tooltip
130//!
131//! # Full API
132//!
133//! See [`zng_app::access`] and [`zng_wgt_access`] for the full API.
134
135pub use zng_app::access::{
136 ACCESS, ACCESS_CLICK_EVENT, ACCESS_EXPANDER_EVENT, ACCESS_INCREMENT_EVENT, ACCESS_INITED_EVENT, ACCESS_NUMBER_EVENT,
137 ACCESS_SCROLL_EVENT, ACCESS_SELECTION_EVENT, ACCESS_TEXT_EVENT, ACCESS_TOOLTIP_EVENT, AccessClickArgs, AccessExpanderArgs,
138 AccessIncrementArgs, AccessInitedArgs, AccessNumberArgs, AccessScrollArgs, AccessSelectionArgs, AccessTextArgs, AccessToolTipArgs,
139 ScrollCmd,
140};
141pub use zng_wgt_access::{
142 AccessCmdName, AccessRole, AutoComplete, CurrentKind, Invalid, LiveIndicator, Orientation, Popup, SortDirection, access_commands,
143 access_role, accessible, active_descendant, auto_complete, checked, col_count, col_index, col_span, controls, current, described_by,
144 details, error_message, expanded, flows_to, invalid, item_count, item_index, label, labelled_by, labelled_by_child, level, live, modal,
145 multi_selectable, on_access_click, on_access_expander, on_access_increment, on_access_number, on_access_scroll, on_access_selection,
146 on_access_text, on_access_tooltip, on_pre_access_click, on_pre_access_expander, on_pre_access_increment, on_pre_access_number,
147 on_pre_access_scroll, on_pre_access_selection, on_pre_access_text, on_pre_access_tooltip, orientation, owns, placeholder, popup,
148 read_only, required, row_count, row_index, row_span, scroll_horizontal, scroll_vertical, selected, sort, value, value_max, value_min,
149};