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! {
43//! id = "btn-1";
44//! child = Text!("Button 1");
45//! };
46//!
47//! widget::on_info_init = hn!(|_| {
48//! let btn_info = WINDOW.info().get("btn-1").unwrap().access().unwrap();
49//! let txt_info = btn_info.info().children().next().unwrap().access().unwrap();
50//!
51//! assert_eq!(None, btn_info.label());
52//! assert!(btn_info.labelled_by_child());
53//! assert_eq!(Some(Txt::from("Button 1")), txt_info.label());
54//! # WINDOW.close();
55//! });
56//! }
57//! # });
58//! ```
59//!
60//! When accessibility info is build you it can be accessed using [`WidgetInfo::access`]. Note that this is a low level
61//! access info, provided as it was set by the widgets, in the example above the *label* value is only found on the text widget,
62//! accessibility tools will use the text label for the button.
63//!
64//! [`WINDOW.enable_access()`]: crate::window::WINDOW_Ext::enable_access
65//! [`WidgetInfo::access`]: crate::widget::info::WidgetInfo::access
66//!
67//! ## Properties
68//!
69//! Properties of this module only define metadata that indicate that the widget implements a certain UI pattern, by
70//! setting a property you must make sure that the widget actually implements said pattern, for this reason most
71//! of the accessibility definitions are provided by the widget implementations.
72//!
73//! In the example below a `TextInput!` widget instance changes its role to [`AccessRole::SearchBox`], the default
74//! role is set by the widget itself to [`AccessRole::TextInput`], this usage of the widget has a more specific role
75//! so it can be changed, in this case it is up to the app developer to actually implement the search.
76//!
77//! ```
78//! use zng::access::{AccessRole, access_role};
79//! use zng::prelude::*;
80//!
81//! # fn example() {
82//! let search_txt = var(Txt::from(""));
83//! # let _ =
84//! TextInput! {
85//! access_role = AccessRole::SearchBox;
86//! placeholder_txt = "search";
87//! txt = search_txt;
88//! }
89//! # ; }
90//! ```
91//!
92//! # Service & Events
93//!
94//! The [`ACCESS`] service provides methods that control widgets by notifying accessibility events. Access events
95//! are handled by widgets even when accessibility is disabled.
96//!
97//! In the example below the button shows and hides the tooltip of a different widget using [`ACCESS.show_tooltip`]
98//! and [`ACCESS.hide_tooltip`].
99//!
100//! ```
101//! use zng::prelude::*;
102//!
103//! # fn example() {
104//! let mut show_tooltip = false;
105//! Window! {
106//! child_align = Align::CENTER;
107//! child = Stack!(
108//! top_to_bottom,
109//! 50,
110//! ui_vec![
111//! Button! {
112//! on_click = hn!(|_| {
113//! use zng::access::ACCESS;
114//!
115//! show_tooltip = !show_tooltip;
116//! if show_tooltip {
117//! ACCESS.show_tooltip(WINDOW.id(), "tooltip-anchor");
118//! } else {
119//! ACCESS.hide_tooltip(WINDOW.id(), "tooltip-anchor");
120//! }
121//! });
122//! child = Text!("Toggle Tooltip");
123//! },
124//! Text! {
125//! id = "tooltip-anchor";
126//! txt = "tooltip anchor";
127//! tooltip = Tip!(Text!("Tooltip"));
128//! }
129//! ]
130//! );
131//! }
132//! # ; }
133//! ```
134//!
135//! [`ACCESS.show_tooltip`]: ACCESS::show_tooltip
136//! [`ACCESS.hide_tooltip`]: ACCESS::hide_tooltip
137//!
138//! # Full API
139//!
140//! See [`zng_app::access`] and [`zng_wgt_access`] for the full API.
141
142pub use zng_app::access::{
143 ACCESS, ACCESS_CLICK_EVENT, ACCESS_EXPANDER_EVENT, ACCESS_INCREMENT_EVENT, ACCESS_INITED_EVENT, ACCESS_NUMBER_EVENT,
144 ACCESS_SCROLL_EVENT, ACCESS_SELECTION_EVENT, ACCESS_TEXT_EVENT, ACCESS_TOOLTIP_EVENT, AccessClickArgs, AccessExpanderArgs,
145 AccessIncrementArgs, AccessInitedArgs, AccessNumberArgs, AccessScrollArgs, AccessSelectionArgs, AccessTextArgs, AccessToolTipArgs,
146 ScrollCmd,
147};
148pub use zng_wgt_access::{
149 AccessCmdName, AccessRole, AutoComplete, CurrentKind, Invalid, LiveIndicator, Orientation, Popup, SortDirection, access_commands,
150 access_role, accessible, active_descendant, auto_complete, checked, col_count, col_index, col_span, controls, current, described_by,
151 details, error_message, expanded, flows_to, invalid, item_count, item_index, label, labelled_by, labelled_by_child, level, live, modal,
152 multi_selectable, on_access_click, on_access_expander, on_access_increment, on_access_number, on_access_scroll, on_access_selection,
153 on_access_text, on_access_tooltip, on_pre_access_click, on_pre_access_expander, on_pre_access_increment, on_pre_access_number,
154 on_pre_access_scroll, on_pre_access_selection, on_pre_access_text, on_pre_access_tooltip, orientation, owns, placeholder, popup,
155 read_only, required, row_count, row_index, row_span, scroll_horizontal, scroll_vertical, selected, sort, value, value_max, value_min,
156};