use crate::view::interactivity::{ActionHandler, KeyDownHandler, TextInputHandler};
use crate::view::key_context::KeyContext;
use crate::core::{Action, FocusId};
#[derive(Default)]
pub struct DispatchNode {
pub parent: Option<usize>,
pub focus: Option<FocusId>,
pub tab_index: Option<i32>,
pub context: Option<KeyContext>,
pub actions: Vec<(Action, ActionHandler)>,
pub key_down: Option<KeyDownHandler>,
pub text_input: Option<TextInputHandler>,
}
#[derive(Default)]
pub struct DispatchTree {
nodes: Vec<DispatchNode>,
stack: Vec<usize>,
}
impl DispatchTree {
pub fn clear(&mut self) {
self.nodes.clear();
self.nodes.push(DispatchNode::default());
self.stack = vec![0];
}
pub fn push(&mut self, mut node: DispatchNode) {
node.parent = self.stack.last().copied();
self.nodes.push(node);
self.stack.push(self.nodes.len() - 1);
}
pub fn pop(&mut self) {
if self.stack.len() > 1 {
self.stack.pop();
}
}
pub fn node(&self, index: usize) -> Option<&DispatchNode> {
self.nodes.get(index)
}
pub fn path_to_focus(&self, focused: Option<FocusId>) -> Vec<usize> {
let target = focused.and_then(|id| self.nodes.iter().position(|node| node.focus == Some(id)));
let fallback = if self.nodes.len() > 1 {
Some(1)
} else if self.nodes.is_empty() {
None
} else {
Some(0)
};
let mut path = Vec::new();
let mut current = target.or(fallback);
while let Some(index) = current {
path.push(index);
current = self.nodes.get(index).and_then(|node| node.parent);
}
path.reverse();
path
}
pub fn focus_path(&self, path: &[usize]) -> Vec<FocusId> {
path.iter().filter_map(|index| self.nodes.get(*index)?.focus).collect()
}
pub fn context_stack(&self, path: &[usize]) -> Vec<KeyContext> {
path.iter().filter_map(|index| self.nodes.get(*index)?.context.clone()).collect()
}
pub fn tab_order(&self) -> Vec<FocusId> {
let mut focusable: Vec<(i32, usize, FocusId)> = self
.nodes
.iter()
.enumerate()
.filter_map(|(order, node)| Some((node.tab_index?, order, node.focus?)))
.collect();
focusable.sort_by_key(|(tab_index, order, _)| (*tab_index, *order));
focusable.into_iter().map(|(_, _, focus)| focus).collect()
}
}