Skip to main content

ftui_a11y/
lib.rs

1#![forbid(unsafe_code)]
2
3//! Accessibility layer for FrankenTUI.
4//!
5//! # Role in FrankenTUI
6//!
7//! `ftui-a11y` defines the accessibility tree infrastructure that lets
8//! widgets describe themselves to screen readers and other assistive
9//! technology. It is **Phase 1** of the accessibility initiative: the
10//! core types and trait contract.
11//!
12//! # Architecture
13//!
14//! ```text
15//!  Widget ──impl Accessible──> Vec<A11yNodeInfo>
16//!                                     │
17//!                              A11yTreeBuilder::add_node()
18//!                                     │
19//!                              A11yTreeBuilder::build()
20//!                                     │
21//!                                A11yTree (immutable snapshot)
22//!                                     │
23//!                              A11yTree::diff(&prev)
24//!                                     │
25//!                               A11yTreeDiff
26//!                                     │
27//!                          (future: platform bridge)
28//! ```
29//!
30//! # How it fits in the system
31//!
32//! - **`ftui-core`**: provides `Rect` for node bounding boxes.
33//! - **`ftui-widgets`**: widgets will `impl Accessible` in a future phase.
34//! - **`ftui-render`**: the render pass will collect nodes via the builder.
35//! - **Platform bridges** (future Phase 3): consume `A11yTreeDiff` to
36//!   push updates to AccessKit / platform APIs.
37//!
38//! # Quick start
39//!
40//! ```rust
41//! use ftui_a11y::node::{A11yNodeInfo, A11yRole};
42//! use ftui_a11y::tree::A11yTreeBuilder;
43//! use ftui_core::geometry::Rect;
44//!
45//! // Build a small tree.
46//! let mut builder = A11yTreeBuilder::new();
47//! let root = A11yNodeInfo::new(1, A11yRole::Window, Rect::new(0, 0, 80, 24))
48//!     .with_name("My App")
49//!     .with_children(vec![2]);
50//! let button = A11yNodeInfo::new(2, A11yRole::Button, Rect::new(10, 5, 8, 1))
51//!     .with_name("OK")
52//!     .with_parent(1);
53//! builder.add_node(root);
54//! builder.add_node(button);
55//! builder.set_root(1);
56//! builder.set_focused(Some(2));
57//! let tree = builder.build();
58//!
59//! assert_eq!(tree.node_count(), 2);
60//! assert_eq!(tree.focused().unwrap().name.as_deref(), Some("OK"));
61//! ```
62
63pub mod node;
64pub mod tree;
65
66use ftui_core::geometry::Rect;
67use node::A11yNodeInfo;
68
69// ── Accessible trait ───────────────────────────────────────────────────
70
71/// Trait for widgets that provide accessibility metadata.
72///
73/// Implementing this trait is **opt-in**. Widgets that do not implement
74/// it are invisible to screen readers (treated as presentational /
75/// decorative).
76///
77/// # Contract
78///
79/// - `accessibility_nodes` must return at least one node for the widget.
80/// - The first node in the returned `Vec` is the widget's "primary"
81///   node; additional nodes represent internal structure (e.g. a table
82///   widget returns the table node plus row/cell nodes).
83/// - Node IDs must be unique within a single render pass. Use a
84///   deterministic scheme (e.g. widget identity hash) to keep IDs
85///   stable across frames for efficient diffing.
86/// - `area` is the bounding rectangle the widget was laid out into.
87///
88/// # Example
89///
90/// ```rust
91/// use ftui_core::geometry::Rect;
92/// use ftui_a11y::Accessible;
93/// use ftui_a11y::node::{A11yNodeInfo, A11yRole};
94///
95/// struct MyButton {
96///     label: String,
97///     id: u64,
98/// }
99///
100/// impl Accessible for MyButton {
101///     fn accessibility_nodes(&self, area: Rect) -> Vec<A11yNodeInfo> {
102///         vec![
103///             A11yNodeInfo::new(self.id, A11yRole::Button, area)
104///                 .with_name(&self.label)
105///         ]
106///     }
107/// }
108/// ```
109pub trait Accessible {
110    /// Return accessibility node(s) for this widget at the given bounds.
111    ///
112    /// Container widgets should return a parent node with children IDs,
113    /// plus the child nodes themselves.
114    fn accessibility_nodes(&self, area: Rect) -> Vec<A11yNodeInfo>;
115}