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};