blitz_dom/
accessibility.rs1use crate::{BaseDocument, Node as BlitzDomNode, local_name};
2use accesskit::{Node as AccessKitNode, NodeId, Role, Tree, TreeUpdate};
3
4impl BaseDocument {
5 pub fn build_accessibility_tree(&self) -> TreeUpdate {
6 let mut nodes = std::collections::HashMap::new();
7 let mut window = AccessKitNode::new(Role::Window);
8
9 self.visit(|node_id, node| {
10 let parent = node
11 .parent
12 .and_then(|parent_id| nodes.get_mut(&parent_id))
13 .map(|(_, parent)| parent)
14 .unwrap_or(&mut window);
15 let (id, builder) = self.build_accessibility_node(node, parent);
16
17 nodes.insert(node_id, (id, builder));
18 });
19
20 let mut nodes: Vec<_> = nodes
21 .into_iter()
22 .map(|(_, (id, node))| (id, node))
23 .collect();
24 nodes.push((NodeId(u64::MAX), window));
25
26 let tree = Tree::new(NodeId(u64::MAX));
27 TreeUpdate {
28 nodes,
29 tree: Some(tree),
30 focus: NodeId(self.focus_node_id.map(|id| id as u64).unwrap_or(u64::MAX)),
31 }
32 }
33
34 fn build_accessibility_node(
35 &self,
36 node: &BlitzDomNode,
37 parent: &mut AccessKitNode,
38 ) -> (NodeId, AccessKitNode) {
39 let id = NodeId(node.id as u64);
40
41 let mut builder = AccessKitNode::default();
42 if node.id == 0 {
43 builder.set_role(Role::Window)
44 } else if let Some(element_data) = node.element_data() {
45 let name = element_data.name.local.to_string();
46
47 let role = match &*name {
49 "button" => Role::Button,
50 "div" => Role::GenericContainer,
51 "header" => Role::Header,
52 "h1" | "h2" | "h3" | "h4" | "h5" | "h6" => Role::Heading,
53 "p" => Role::Paragraph,
54 "section" => Role::Section,
55 "input" => {
56 let ty = element_data.attr(local_name!("type")).unwrap_or("text");
57 match ty {
58 "number" => Role::NumberInput,
59 "checkbox" => Role::CheckBox,
60 _ => Role::TextInput,
61 }
62 }
63 _ => Role::Unknown,
64 };
65
66 builder.set_role(role);
67 builder.set_html_tag(name);
68 } else if node.is_text_node() {
69 builder.set_role(Role::TextRun);
70 builder.set_value(node.text_content());
71 parent.push_labelled_by(id)
72 }
73
74 parent.push_child(id);
75
76 (id, builder)
77 }
78}