33// the LICENSE-APACHE file) or the MIT license (found in
44// the LICENSE-MIT file), at your option.
55
6- use accesskit:: Role ;
6+ use accesskit:: { Rect , Role } ;
77
88use crate :: node:: Node ;
99
@@ -14,13 +14,41 @@ pub enum FilterResult {
1414 ExcludeSubtree ,
1515}
1616
17- pub fn common_filter ( node : & Node ) -> FilterResult {
17+ fn common_filter_base ( node : & Node ) -> Option < FilterResult > {
1818 if node. is_focused ( ) {
19- return FilterResult :: Include ;
19+ return Some ( FilterResult :: Include ) ;
2020 }
2121
2222 if node. is_hidden ( ) {
23- return FilterResult :: ExcludeSubtree ;
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+
34+ fn common_filter_without_parent_checks ( node : & Node ) -> FilterResult {
35+ common_filter_base ( node) . unwrap_or ( FilterResult :: Include )
36+ }
37+
38+ fn 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+
49+ pub fn common_filter ( node : & Node ) -> FilterResult {
50+ if let Some ( result) = common_filter_base ( node) {
51+ return result;
2452 }
2553
2654 if let Some ( parent) = node. parent ( ) {
@@ -29,9 +57,30 @@ pub fn common_filter(node: &Node) -> FilterResult {
2957 }
3058 }
3159
32- let role = node. role ( ) ;
33- if role == Role :: GenericContainer || role == Role :: TextRun {
34- return FilterResult :: ExcludeNode ;
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+ }
3584 }
3685
3786 FilterResult :: Include
@@ -46,10 +95,21 @@ pub fn common_filter_with_root_exception(node: &Node) -> FilterResult {
4695
4796#[ cfg( test) ]
4897mod tests {
49- use accesskit:: { Node , NodeId , Role , Tree , TreeUpdate } ;
98+ use accesskit:: { Node , NodeId , Rect , Role , Tree , TreeUpdate } ;
5099 use alloc:: vec;
51100
52- use super :: { common_filter, common_filter_with_root_exception, FilterResult } ;
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+ }
53113
54114 #[ test]
55115 fn normal ( ) {
@@ -66,10 +126,7 @@ mod tests {
66126 focus : NodeId ( 0 ) ,
67127 } ;
68128 let tree = crate :: Tree :: new ( update, false ) ;
69- assert_eq ! (
70- FilterResult :: Include ,
71- common_filter( & tree. state( ) . node_by_id( NodeId ( 1 ) ) . unwrap( ) )
72- ) ;
129+ assert_filter_result ( Include , & tree, NodeId ( 1 ) ) ;
73130 }
74131
75132 #[ test]
@@ -91,10 +148,7 @@ mod tests {
91148 focus : NodeId ( 0 ) ,
92149 } ;
93150 let tree = crate :: Tree :: new ( update, false ) ;
94- assert_eq ! (
95- FilterResult :: ExcludeSubtree ,
96- common_filter( & tree. state( ) . node_by_id( NodeId ( 1 ) ) . unwrap( ) )
97- ) ;
151+ assert_filter_result ( ExcludeSubtree , & tree, NodeId ( 1 ) ) ;
98152 }
99153
100154 #[ test]
@@ -116,10 +170,7 @@ mod tests {
116170 focus : NodeId ( 1 ) ,
117171 } ;
118172 let tree = crate :: Tree :: new ( update, true ) ;
119- assert_eq ! (
120- FilterResult :: Include ,
121- common_filter( & tree. state( ) . node_by_id( NodeId ( 1 ) ) . unwrap( ) )
122- ) ;
173+ assert_filter_result ( Include , & tree, NodeId ( 1 ) ) ;
123174 }
124175
125176 #[ test]
@@ -137,18 +188,12 @@ mod tests {
137188 focus : NodeId ( 0 ) ,
138189 } ;
139190 let tree = crate :: Tree :: new ( update, false ) ;
191+ assert_filter_result ( ExcludeNode , & tree, NodeId ( 0 ) ) ;
140192 assert_eq ! (
141- FilterResult :: ExcludeNode ,
142- common_filter( & tree. state( ) . node_by_id( NodeId ( 0 ) ) . unwrap( ) )
143- ) ;
144- assert_eq ! (
145- FilterResult :: Include ,
193+ Include ,
146194 common_filter_with_root_exception( & tree. state( ) . node_by_id( NodeId ( 0 ) ) . unwrap( ) )
147195 ) ;
148- assert_eq ! (
149- FilterResult :: Include ,
150- common_filter( & tree. state( ) . node_by_id( NodeId ( 1 ) ) . unwrap( ) )
151- ) ;
196+ assert_filter_result ( Include , & tree, NodeId ( 1 ) ) ;
152197 }
153198
154199 #[ test]
@@ -167,14 +212,8 @@ mod tests {
167212 focus : NodeId ( 0 ) ,
168213 } ;
169214 let tree = crate :: Tree :: new ( update, false ) ;
170- assert_eq ! (
171- FilterResult :: ExcludeSubtree ,
172- common_filter( & tree. state( ) . node_by_id( NodeId ( 0 ) ) . unwrap( ) )
173- ) ;
174- assert_eq ! (
175- FilterResult :: ExcludeSubtree ,
176- common_filter( & tree. state( ) . node_by_id( NodeId ( 1 ) ) . unwrap( ) )
177- ) ;
215+ assert_filter_result ( ExcludeSubtree , & tree, NodeId ( 0 ) ) ;
216+ assert_filter_result ( ExcludeSubtree , & tree, NodeId ( 1 ) ) ;
178217 }
179218
180219 #[ test]
@@ -193,14 +232,8 @@ mod tests {
193232 focus : NodeId ( 1 ) ,
194233 } ;
195234 let tree = crate :: Tree :: new ( update, true ) ;
196- assert_eq ! (
197- FilterResult :: ExcludeSubtree ,
198- common_filter( & tree. state( ) . node_by_id( NodeId ( 0 ) ) . unwrap( ) )
199- ) ;
200- assert_eq ! (
201- FilterResult :: Include ,
202- common_filter( & tree. state( ) . node_by_id( NodeId ( 1 ) ) . unwrap( ) )
203- ) ;
235+ assert_filter_result ( ExcludeSubtree , & tree, NodeId ( 0 ) ) ;
236+ assert_filter_result ( Include , & tree, NodeId ( 1 ) ) ;
204237 }
205238
206239 #[ test]
@@ -218,9 +251,131 @@ mod tests {
218251 focus : NodeId ( 0 ) ,
219252 } ;
220253 let tree = crate :: Tree :: new ( update, false ) ;
221- assert_eq ! (
222- FilterResult :: ExcludeNode ,
223- common_filter( & tree. state( ) . node_by_id( NodeId ( 1 ) ) . unwrap( ) )
224- ) ;
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 ) ) ;
225380 }
226381}
0 commit comments