use accesskit::{Node, NodeId as AccessKitNodeId, Rect, Tree, TreeUpdate};
use indextree::NodeId as ComponentNodeId;
use crate::{
accessibility::AccessibilityId,
component_tree::{ComponentNodeMetaDatas, ComponentNodeTree, ComputedData},
px::PxPosition,
};
pub fn build_tree_update(
tree: &ComponentNodeTree,
metadatas: &ComponentNodeMetaDatas,
root_node_id: ComponentNodeId,
root_label: Option<&str>,
) -> Option<TreeUpdate> {
let mut nodes = Vec::new();
let mut focus = None;
let root_accesskit_id = AccessibilityId::from_component_node_id(root_node_id);
let has_nodes = traverse_and_collect(
tree,
metadatas,
root_node_id,
&mut nodes,
&mut focus,
true,
root_label,
);
if !has_nodes || nodes.is_empty() {
return None;
}
let tree_struct = Tree::new(root_accesskit_id.to_accesskit_id());
Some(TreeUpdate {
nodes,
tree: Some(tree_struct),
focus: focus.unwrap_or_else(|| root_accesskit_id.to_accesskit_id()),
})
}
fn traverse_and_collect(
tree: &ComponentNodeTree,
metadatas: &ComponentNodeMetaDatas,
node_id: ComponentNodeId,
nodes: &mut Vec<(AccessKitNodeId, Node)>,
focus: &mut Option<AccessKitNodeId>,
is_root: bool,
root_label: Option<&str>,
) -> bool {
let metadata = match metadatas.get(&node_id) {
Some(metadata) => metadata,
None => return false,
};
let accessibility_node = metadata.accessibility.clone();
let abs_position = metadata.abs_position;
let computed_data = metadata.computed_data;
drop(metadata);
let mut has_accessible_descendants = false;
let mut accessible_children = Vec::new();
for child_id in node_id.children(tree) {
let child_has_accessibility =
traverse_and_collect(tree, metadatas, child_id, nodes, focus, false, root_label);
has_accessible_descendants |= child_has_accessibility;
if child_has_accessibility {
let child_accesskit_id = AccessibilityId::from_component_node_id(child_id);
accessible_children.push(child_accesskit_id.to_accesskit_id());
}
}
if let Some(accessibility_node) = accessibility_node {
let accesskit_id = AccessibilityId::from_component_node_id(node_id);
let mut node = Node::new(accessibility_node.role.unwrap_or(accesskit::Role::Unknown));
if let Some(label) = accessibility_node.label {
node.set_label(label);
}
if let Some(description) = accessibility_node.description {
node.set_description(description);
}
if let Some(value) = accessibility_node.value {
node.set_value(value);
}
if let Some(numeric_value) = accessibility_node.numeric_value {
node.set_numeric_value(numeric_value);
}
if accessibility_node.focusable {
node.add_action(accesskit::Action::Focus);
}
if accessibility_node.focused {
*focus = Some(accesskit_id.to_accesskit_id());
}
if let Some(toggled) = accessibility_node.toggled {
node.set_toggled(toggled);
}
if accessibility_node.disabled {
node.set_disabled();
}
if accessibility_node.hidden {
node.set_hidden();
}
for action in accessibility_node.actions {
node.add_action(action);
}
if !accessible_children.is_empty() {
node.set_children(accessible_children);
}
if let Some(bounds) = rect_from_geometry(abs_position, computed_data) {
node.set_bounds(bounds);
}
nodes.push((accesskit_id.to_accesskit_id(), node));
true
} else if is_root || has_accessible_descendants {
let accesskit_id = AccessibilityId::from_component_node_id(node_id);
let mut node = if is_root {
let mut root_node = Node::new(accesskit::Role::Window);
if let Some(label) = root_label {
root_node.set_label(label.to_string());
}
root_node
} else {
Node::new(accesskit::Role::GenericContainer)
};
if !accessible_children.is_empty() {
node.set_children(accessible_children);
}
if let Some(bounds) = rect_from_geometry(abs_position, computed_data) {
node.set_bounds(bounds);
}
nodes.push((accesskit_id.to_accesskit_id(), node));
true
} else {
false
}
}
fn rect_from_geometry(
abs_position: Option<PxPosition>,
computed_data: Option<ComputedData>,
) -> Option<Rect> {
let position = abs_position?;
let size = computed_data?;
let x0 = position.x.0 as f64;
let y0 = position.y.0 as f64;
let x1 = x0 + size.width.0 as f64;
let y1 = y0 + size.height.0 as f64;
Some(Rect { x0, y0, x1, y1 })
}
pub fn dispatch_action(
tree: &ComponentNodeTree,
metadatas: &ComponentNodeMetaDatas,
action_request: accesskit::ActionRequest,
) -> bool {
let accessibility_id = AccessibilityId::from_accesskit_id(action_request.target);
let index = std::num::NonZero::new(accessibility_id.0 as usize);
let component_node_id = index.and_then(|idx| tree.get_node_id_at(idx));
if let Some(node_id) = component_node_id
&& let Some(metadata) = metadatas.get(&node_id)
&& let Some(handler) = &metadata.accessibility_action_handler
{
handler(action_request.action);
return true;
}
false
}