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