accesskit_consumer/
filters.rs

1// Copyright 2023 The AccessKit Authors. All rights reserved.
2// Licensed under the Apache License, Version 2.0 (found in
3// the LICENSE-APACHE file) or the MIT license (found in
4// the LICENSE-MIT file), at your option.
5
6use 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 the parent clips its children, then exclude this subtree
63            // if this child's bounding box isn't inside the parent's bounding
64            // box, and if the previous or next filtered sibling isn't inside
65            // the parent's bounding box either. The latter condition is meant
66            // to allow off-screen items to be seen by consumers so they can be
67            // scrolled into view.
68            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}