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    // Graft nodes are transparent containers pointing to a subtree
27    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 the parent clips its children, then exclude this subtree
68            // if this child's bounding box isn't inside the parent's bounding
69            // box, and if the previous or next filtered sibling isn't inside
70            // the parent's bounding box either. The latter condition is meant
71            // to allow off-screen items to be seen by consumers so they can be
72            // scrolled into view.
73            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}