1use crate::item_tree::ItemTreeNodeArray;
11
12pub fn step_out_of_node(
13 index: u32,
14 item_tree: &crate::item_tree::ItemTreeNodeArray,
15) -> Option<u32> {
16 let mut self_or_ancestor = index;
17 loop {
18 if let Some(sibling) = item_tree.next_sibling(self_or_ancestor) {
19 return Some(sibling);
20 }
21 self_or_ancestor = item_tree.parent(self_or_ancestor)?;
22 }
23}
24
25pub fn default_next_in_local_focus_chain(
26 index: u32,
27 item_tree: &crate::item_tree::ItemTreeNodeArray,
28) -> Option<u32> {
29 if let Some(child) = item_tree.first_child(index) {
30 return Some(child);
31 }
32
33 step_out_of_node(index, item_tree)
34}
35
36fn step_into_node(item_tree: &ItemTreeNodeArray, index: u32) -> u32 {
37 let mut node = index;
38 loop {
39 if let Some(last_child) = item_tree.last_child(node) {
40 node = last_child;
41 } else {
42 return node;
43 }
44 }
45}
46
47pub fn default_previous_in_local_focus_chain(
48 index: u32,
49 item_tree: &crate::item_tree::ItemTreeNodeArray,
50) -> Option<u32> {
51 if let Some(previous) = item_tree.previous_sibling(index) {
52 Some(step_into_node(item_tree, previous))
53 } else {
54 item_tree.parent(index)
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61 use std::vec;
62
63 use crate::item_tree::ItemTreeNode;
64
65 fn validate_focus_chains(item_tree: ItemTreeNodeArray<'_>) {
66 let forward_chain = {
67 let mut tmp = alloc::vec::Vec::with_capacity(item_tree.node_count());
68 let mut node = 0;
69
70 loop {
71 tmp.push(node);
72 if let Some(next_node) = default_next_in_local_focus_chain(node, &item_tree) {
73 node = next_node;
74 } else {
75 break;
76 }
77 }
78 tmp
79 };
80 let reverse_backward_chain = {
81 let mut tmp = alloc::vec::Vec::with_capacity(item_tree.node_count());
82 let mut node = step_into_node(&item_tree, 0);
83
84 loop {
85 tmp.push(node);
86 if let Some(next_node) = default_previous_in_local_focus_chain(node, &item_tree) {
87 node = next_node;
88 } else {
89 break;
90 }
91 }
92 tmp.reverse();
93 tmp
94 };
95
96 assert_eq!(forward_chain, reverse_backward_chain);
97 assert_eq!(forward_chain.len(), item_tree.node_count());
98 }
99
100 #[test]
101 fn test_focus_chain_root_only() {
102 let nodes = vec![ItemTreeNode::Item {
103 is_accessible: false,
104 children_count: 0,
105 children_index: 1,
106 parent_index: 0,
107 item_array_index: 0,
108 }];
109
110 let tree: ItemTreeNodeArray = (nodes.as_slice()).into();
111 validate_focus_chains(tree);
112 }
113
114 #[test]
115 fn test_focus_chain_one_child() {
116 let nodes = vec![
117 ItemTreeNode::Item {
118 is_accessible: false,
119 children_count: 1,
120 children_index: 1,
121 parent_index: 0,
122 item_array_index: 0,
123 },
124 ItemTreeNode::Item {
125 is_accessible: false,
126 children_count: 0,
127 children_index: 2,
128 parent_index: 0,
129 item_array_index: 0,
130 },
131 ];
132
133 let tree: ItemTreeNodeArray = (nodes.as_slice()).into();
134 validate_focus_chains(tree);
135 }
136
137 #[test]
138 fn test_focus_chain_three_children() {
139 let nodes = vec![
140 ItemTreeNode::Item {
141 is_accessible: false,
142 children_count: 3,
143 children_index: 1,
144 parent_index: 0,
145 item_array_index: 0,
146 },
147 ItemTreeNode::Item {
148 is_accessible: false,
149 children_count: 0,
150 children_index: 4,
151 parent_index: 0,
152 item_array_index: 0,
153 },
154 ItemTreeNode::Item {
155 is_accessible: false,
156 children_count: 0,
157 children_index: 4,
158 parent_index: 0,
159 item_array_index: 0,
160 },
161 ItemTreeNode::Item {
162 is_accessible: false,
163 children_count: 0,
164 children_index: 4,
165 parent_index: 0,
166 item_array_index: 0,
167 },
168 ];
169
170 let tree: ItemTreeNodeArray = (nodes.as_slice()).into();
171 validate_focus_chains(tree);
172 }
173
174 #[test]
175 fn test_focus_chain_complex_tree() {
176 let nodes = vec![
177 ItemTreeNode::Item {
178 is_accessible: false,
180 children_count: 2,
181 children_index: 1,
182 parent_index: 0,
183 item_array_index: 0,
184 },
185 ItemTreeNode::Item {
186 is_accessible: false,
188 children_count: 2,
189 children_index: 3,
190 parent_index: 0,
191 item_array_index: 0,
192 },
193 ItemTreeNode::Item {
194 is_accessible: false,
196 children_count: 1,
197 children_index: 11,
198 parent_index: 0,
199 item_array_index: 0,
200 },
201 ItemTreeNode::Item {
202 is_accessible: false,
204 children_count: 1,
205 children_index: 5,
206 parent_index: 1,
207 item_array_index: 0,
208 },
209 ItemTreeNode::Item {
210 is_accessible: false,
212 children_count: 2,
213 children_index: 6,
214 parent_index: 1,
215 item_array_index: 0,
216 },
217 ItemTreeNode::Item {
218 is_accessible: false,
220 children_count: 0,
221 children_index: 0,
222 parent_index: 3,
223 item_array_index: 0,
224 },
225 ItemTreeNode::Item {
226 is_accessible: false,
228 children_count: 2,
229 children_index: 8,
230 parent_index: 4,
231 item_array_index: 0,
232 },
233 ItemTreeNode::Item {
234 is_accessible: false,
236 children_count: 1,
237 children_index: 10,
238 parent_index: 4,
239 item_array_index: 0,
240 },
241 ItemTreeNode::Item {
242 is_accessible: false,
244 children_count: 0,
245 children_index: 0,
246 parent_index: 6,
247 item_array_index: 0,
248 },
249 ItemTreeNode::Item {
250 is_accessible: false,
252 children_count: 0,
253 children_index: 0,
254 parent_index: 6,
255 item_array_index: 0,
256 },
257 ItemTreeNode::Item {
258 is_accessible: false,
260 children_count: 0,
261 children_index: 0,
262 parent_index: 7,
263 item_array_index: 0,
264 },
265 ItemTreeNode::Item {
266 is_accessible: false,
268 children_count: 2,
269 children_index: 12,
270 parent_index: 2,
271 item_array_index: 0,
272 },
273 ItemTreeNode::Item {
274 is_accessible: false,
276 children_count: 0,
277 children_index: 0,
278 parent_index: 11,
279 item_array_index: 0,
280 },
281 ItemTreeNode::Item {
282 is_accessible: false,
284 children_count: 0,
285 children_index: 0,
286 parent_index: 11,
287 item_array_index: 0,
288 },
289 ];
290
291 let tree: ItemTreeNodeArray = (nodes.as_slice()).into();
292 validate_focus_chains(tree);
293 }
294}