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