1use ansiq_core::Node;
2
3#[derive(Debug, Default)]
4pub struct FocusState {
5 order: Vec<usize>,
6 current: Option<usize>,
7 scope_key: Option<String>,
8}
9
10impl FocusState {
11 pub fn sync_from_tree<Message>(&mut self, tree: &Node<Message>) {
12 let mut next_order = Vec::new();
13 if let Some(scope_key) = self.scope_key.as_deref() {
14 if let Some(scope_root) = find_scope_root(tree, scope_key) {
15 collect_focusable_ids(scope_root, &mut next_order);
16 } else {
17 collect_focusable_ids(tree, &mut next_order);
18 }
19 } else {
20 collect_focusable_ids(tree, &mut next_order);
21 }
22
23 self.current = if next_order.is_empty() {
24 None
25 } else if self
26 .current
27 .is_some_and(|current| next_order.contains(¤t))
28 {
29 self.current
30 } else {
31 next_order.first().copied()
32 };
33 self.order = next_order;
34 }
35
36 pub fn current(&self) -> Option<usize> {
37 self.current
38 }
39
40 pub fn set_current(&mut self, current: Option<usize>) {
41 self.current = current;
42 }
43
44 pub fn set_scope_key(&mut self, scope_key: Option<String>) {
45 self.scope_key = scope_key;
46 }
47
48 pub fn scope_key(&self) -> Option<&str> {
49 self.scope_key.as_deref()
50 }
51
52 pub fn next(&mut self) {
53 self.step(1);
54 }
55
56 pub fn prev(&mut self) {
57 self.step_back();
58 }
59
60 fn step(&mut self, amount: usize) {
61 if self.order.is_empty() {
62 self.current = None;
63 return;
64 }
65
66 let index = self
67 .current
68 .and_then(|current| self.order.iter().position(|id| *id == current))
69 .unwrap_or(0);
70 let next = (index + amount) % self.order.len();
71 self.current = Some(self.order[next]);
72 }
73
74 fn step_back(&mut self) {
75 if self.order.is_empty() {
76 self.current = None;
77 return;
78 }
79
80 let index = self
81 .current
82 .and_then(|current| self.order.iter().position(|id| *id == current))
83 .unwrap_or(0);
84 let next = if index == 0 {
85 self.order.len() - 1
86 } else {
87 index - 1
88 };
89 self.current = Some(self.order[next]);
90 }
91}
92
93fn collect_focusable_ids<Message>(node: &Node<Message>, output: &mut Vec<usize>) {
94 if node.element.focusable {
95 output.push(node.id);
96 }
97
98 for child in &node.children {
99 collect_focusable_ids(child, output);
100 }
101}
102
103fn find_scope_root<'a, Message>(
104 node: &'a Node<Message>,
105 scope_key: &str,
106) -> Option<&'a Node<Message>> {
107 if node.element.continuity_key() == Some(scope_key) {
108 return Some(node);
109 }
110
111 for child in &node.children {
112 if let Some(found) = find_scope_root(child, scope_key) {
113 return Some(found);
114 }
115 }
116
117 None
118}