1use accesskit::{Rect, Role};
7
8use crate::node::Node;
9
10#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
11pub enum FilterResult {
12 Include,
13 ExcludeNode,
14 ExcludeSubtree,
15}
16
17fn common_filter_base(node: &Node) -> Option<FilterResult> {
18 if node.is_focused() {
19 return Some(FilterResult::Include);
20 }
21
22 if node.is_hidden() {
23 return Some(FilterResult::ExcludeSubtree);
24 }
25
26 if node.is_graft() {
28 return Some(FilterResult::ExcludeNode);
29 }
30
31 let role = node.role();
32 if role == Role::GenericContainer || role == Role::TextRun {
33 return Some(FilterResult::ExcludeNode);
34 }
35
36 None
37}
38
39fn common_filter_without_parent_checks(node: &Node) -> FilterResult {
40 common_filter_base(node).unwrap_or(FilterResult::Include)
41}
42
43fn is_first_sibling_in_parent_bbox<'a>(
44 mut siblings: impl Iterator<Item = Node<'a>>,
45 parent_bbox: Rect,
46) -> bool {
47 siblings.next().is_some_and(|sibling| {
48 sibling
49 .bounding_box()
50 .is_some_and(|bbox| !bbox.intersect(parent_bbox).is_empty())
51 })
52}
53
54pub fn common_filter(node: &Node) -> FilterResult {
55 if let Some(result) = common_filter_base(node) {
56 return result;
57 }
58
59 if let Some(parent) = node.parent() {
60 if common_filter(&parent) == FilterResult::ExcludeSubtree {
61 return FilterResult::ExcludeSubtree;
62 }
63 }
64
65 if let Some(parent) = node.filtered_parent(&common_filter_without_parent_checks) {
66 if parent.clips_children() {
67 if let Some(bbox) = node.bounding_box() {
74 if let Some(parent_bbox) = parent.bounding_box() {
75 if bbox.intersect(parent_bbox).is_empty()
76 && !(is_first_sibling_in_parent_bbox(
77 node.following_filtered_siblings(&common_filter_without_parent_checks),
78 parent_bbox,
79 ) || is_first_sibling_in_parent_bbox(
80 node.preceding_filtered_siblings(&common_filter_without_parent_checks),
81 parent_bbox,
82 ))
83 {
84 return FilterResult::ExcludeSubtree;
85 }
86 }
87 }
88 }
89 }
90
91 FilterResult::Include
92}
93
94pub fn common_filter_with_root_exception(node: &Node) -> FilterResult {
95 if node.is_root() {
96 return FilterResult::Include;
97 }
98 common_filter(node)
99}
100
101#[cfg(test)]
102mod tests {
103 use accesskit::{Node, NodeId, Rect, Role, Tree, TreeId, TreeUpdate};
104 use alloc::vec;
105
106 use super::{
107 common_filter, common_filter_with_root_exception,
108 FilterResult::{self, *},
109 };
110 use crate::tests::nid;
111
112 #[track_caller]
113 fn assert_filter_result(expected: FilterResult, tree: &crate::Tree, id: NodeId) {
114 assert_eq!(
115 expected,
116 common_filter(&tree.state().node_by_id(nid(id)).unwrap())
117 );
118 }
119
120 #[test]
121 fn normal() {
122 let update = TreeUpdate {
123 nodes: vec![
124 (NodeId(0), {
125 let mut node = Node::new(Role::Window);
126 node.set_children(vec![NodeId(1)]);
127 node
128 }),
129 (NodeId(1), Node::new(Role::Button)),
130 ],
131 tree: Some(Tree::new(NodeId(0))),
132 tree_id: TreeId::ROOT,
133 focus: NodeId(0),
134 };
135 let tree = crate::Tree::new(update, false);
136 assert_filter_result(Include, &tree, NodeId(1));
137 }
138
139 #[test]
140 fn hidden() {
141 let update = TreeUpdate {
142 nodes: vec![
143 (NodeId(0), {
144 let mut node = Node::new(Role::Window);
145 node.set_children(vec![NodeId(1)]);
146 node
147 }),
148 (NodeId(1), {
149 let mut node = Node::new(Role::Button);
150 node.set_hidden();
151 node
152 }),
153 ],
154 tree: Some(Tree::new(NodeId(0))),
155 tree_id: TreeId::ROOT,
156 focus: NodeId(0),
157 };
158 let tree = crate::Tree::new(update, false);
159 assert_filter_result(ExcludeSubtree, &tree, NodeId(1));
160 }
161
162 #[test]
163 fn hidden_but_focused() {
164 let update = TreeUpdate {
165 nodes: vec![
166 (NodeId(0), {
167 let mut node = Node::new(Role::Window);
168 node.set_children(vec![NodeId(1)]);
169 node
170 }),
171 (NodeId(1), {
172 let mut node = Node::new(Role::Button);
173 node.set_hidden();
174 node
175 }),
176 ],
177 tree: Some(Tree::new(NodeId(0))),
178 tree_id: TreeId::ROOT,
179 focus: NodeId(1),
180 };
181 let tree = crate::Tree::new(update, true);
182 assert_filter_result(Include, &tree, NodeId(1));
183 }
184
185 #[test]
186 fn generic_container() {
187 let update = TreeUpdate {
188 nodes: vec![
189 (NodeId(0), {
190 let mut node = Node::new(Role::GenericContainer);
191 node.set_children(vec![NodeId(1)]);
192 node
193 }),
194 (NodeId(1), Node::new(Role::Button)),
195 ],
196 tree: Some(Tree::new(NodeId(0))),
197 tree_id: TreeId::ROOT,
198 focus: NodeId(0),
199 };
200 let tree = crate::Tree::new(update, false);
201 assert_filter_result(ExcludeNode, &tree, NodeId(0));
202 assert_eq!(
203 Include,
204 common_filter_with_root_exception(&tree.state().node_by_id(nid(NodeId(0))).unwrap())
205 );
206 assert_filter_result(Include, &tree, NodeId(1));
207 }
208
209 #[test]
210 fn hidden_parent() {
211 let update = TreeUpdate {
212 nodes: vec![
213 (NodeId(0), {
214 let mut node = Node::new(Role::GenericContainer);
215 node.set_hidden();
216 node.set_children(vec![NodeId(1)]);
217 node
218 }),
219 (NodeId(1), Node::new(Role::Button)),
220 ],
221 tree: Some(Tree::new(NodeId(0))),
222 tree_id: TreeId::ROOT,
223 focus: NodeId(0),
224 };
225 let tree = crate::Tree::new(update, false);
226 assert_filter_result(ExcludeSubtree, &tree, NodeId(0));
227 assert_filter_result(ExcludeSubtree, &tree, NodeId(1));
228 }
229
230 #[test]
231 fn hidden_parent_but_focused() {
232 let update = TreeUpdate {
233 nodes: vec![
234 (NodeId(0), {
235 let mut node = Node::new(Role::GenericContainer);
236 node.set_hidden();
237 node.set_children(vec![NodeId(1)]);
238 node
239 }),
240 (NodeId(1), Node::new(Role::Button)),
241 ],
242 tree: Some(Tree::new(NodeId(0))),
243 tree_id: TreeId::ROOT,
244 focus: NodeId(1),
245 };
246 let tree = crate::Tree::new(update, true);
247 assert_filter_result(ExcludeSubtree, &tree, NodeId(0));
248 assert_filter_result(Include, &tree, NodeId(1));
249 }
250
251 #[test]
252 fn text_run() {
253 let update = TreeUpdate {
254 nodes: vec![
255 (NodeId(0), {
256 let mut node = Node::new(Role::TextInput);
257 node.set_children(vec![NodeId(1)]);
258 node
259 }),
260 (NodeId(1), Node::new(Role::TextRun)),
261 ],
262 tree: Some(Tree::new(NodeId(0))),
263 tree_id: TreeId::ROOT,
264 focus: NodeId(0),
265 };
266 let tree = crate::Tree::new(update, false);
267 assert_filter_result(ExcludeNode, &tree, NodeId(1));
268 }
269
270 fn clipped_children_test_tree() -> crate::Tree {
271 let update = TreeUpdate {
272 nodes: vec![
273 (NodeId(0), {
274 let mut node = Node::new(Role::ScrollView);
275 node.set_clips_children();
276 node.set_bounds(Rect::new(0.0, 0.0, 30.0, 30.0));
277 node.set_children(vec![
278 NodeId(1),
279 NodeId(2),
280 NodeId(3),
281 NodeId(4),
282 NodeId(5),
283 NodeId(6),
284 NodeId(7),
285 NodeId(8),
286 NodeId(9),
287 NodeId(10),
288 NodeId(11),
289 ]);
290 node
291 }),
292 (NodeId(1), {
293 let mut node = Node::new(Role::Unknown);
294 node.set_bounds(Rect::new(0.0, -30.0, 30.0, -20.0));
295 node
296 }),
297 (NodeId(2), {
298 let mut node = Node::new(Role::Unknown);
299 node.set_bounds(Rect::new(0.0, -20.0, 30.0, -10.0));
300 node
301 }),
302 (NodeId(3), {
303 let mut node = Node::new(Role::Unknown);
304 node.set_bounds(Rect::new(0.0, -10.0, 30.0, 0.0));
305 node
306 }),
307 (NodeId(4), {
308 let mut node = Node::new(Role::Unknown);
309 node.set_hidden();
310 node
311 }),
312 (NodeId(5), {
313 let mut node = Node::new(Role::Unknown);
314 node.set_bounds(Rect::new(0.0, 0.0, 30.0, 10.0));
315 node
316 }),
317 (NodeId(6), {
318 let mut node = Node::new(Role::Unknown);
319 node.set_bounds(Rect::new(0.0, 10.0, 30.0, 20.0));
320 node
321 }),
322 (NodeId(7), {
323 let mut node = Node::new(Role::Unknown);
324 node.set_bounds(Rect::new(0.0, 20.0, 30.0, 30.0));
325 node
326 }),
327 (NodeId(8), {
328 let mut node = Node::new(Role::Unknown);
329 node.set_hidden();
330 node
331 }),
332 (NodeId(9), {
333 let mut node = Node::new(Role::Unknown);
334 node.set_bounds(Rect::new(0.0, 30.0, 30.0, 40.0));
335 node
336 }),
337 (NodeId(10), {
338 let mut node = Node::new(Role::Unknown);
339 node.set_bounds(Rect::new(0.0, 40.0, 30.0, 50.0));
340 node
341 }),
342 (NodeId(11), {
343 let mut node = Node::new(Role::Unknown);
344 node.set_bounds(Rect::new(0.0, 50.0, 30.0, 60.0));
345 node
346 }),
347 ],
348 tree: Some(Tree::new(NodeId(0))),
349 tree_id: TreeId::ROOT,
350 focus: NodeId(0),
351 };
352 crate::Tree::new(update, false)
353 }
354
355 #[test]
356 fn clipped_children_excluded_above() {
357 let tree = clipped_children_test_tree();
358 assert_filter_result(ExcludeSubtree, &tree, NodeId(1));
359 assert_filter_result(ExcludeSubtree, &tree, NodeId(2));
360 }
361
362 #[test]
363 fn clipped_children_included_above() {
364 let tree = clipped_children_test_tree();
365 assert_filter_result(Include, &tree, NodeId(3));
366 }
367
368 #[test]
369 fn clipped_children_hidden() {
370 let tree = clipped_children_test_tree();
371 assert_filter_result(ExcludeSubtree, &tree, NodeId(4));
372 assert_filter_result(ExcludeSubtree, &tree, NodeId(8));
373 }
374
375 #[test]
376 fn clipped_children_visible() {
377 let tree = clipped_children_test_tree();
378 assert_filter_result(Include, &tree, NodeId(5));
379 assert_filter_result(Include, &tree, NodeId(6));
380 assert_filter_result(Include, &tree, NodeId(7));
381 }
382
383 #[test]
384 fn clipped_children_included_below() {
385 let tree = clipped_children_test_tree();
386 assert_filter_result(Include, &tree, NodeId(9));
387 }
388
389 #[test]
390 fn clipped_children_excluded_below() {
391 let tree = clipped_children_test_tree();
392 assert_filter_result(ExcludeSubtree, &tree, NodeId(10));
393 assert_filter_result(ExcludeSubtree, &tree, NodeId(11));
394 }
395
396 #[test]
397 fn graft_node() {
398 use accesskit::Uuid;
399
400 let subtree_id = TreeId(Uuid::from_u128(1));
401 let update = TreeUpdate {
402 nodes: vec![
403 (NodeId(0), {
404 let mut node = Node::new(Role::Window);
405 node.set_children(vec![NodeId(1)]);
406 node
407 }),
408 (NodeId(1), {
409 let mut node = Node::new(Role::GenericContainer);
410 node.set_tree_id(subtree_id);
411 node
412 }),
413 ],
414 tree: Some(Tree::new(NodeId(0))),
415 tree_id: TreeId::ROOT,
416 focus: NodeId(0),
417 };
418 let tree = crate::Tree::new(update, false);
419 assert_filter_result(ExcludeNode, &tree, NodeId(1));
420 }
421}