3
3
// the LICENSE-APACHE file) or the MIT license (found in
4
4
// the LICENSE-MIT file), at your option.
5
5
6
- use accesskit:: Role ;
6
+ use accesskit:: { Rect , Role } ;
7
7
8
8
use crate :: node:: Node ;
9
9
@@ -14,13 +14,41 @@ pub enum FilterResult {
14
14
ExcludeSubtree ,
15
15
}
16
16
17
- pub fn common_filter ( node : & Node ) -> FilterResult {
17
+ fn common_filter_base ( node : & Node ) -> Option < FilterResult > {
18
18
if node. is_focused ( ) {
19
- return FilterResult :: Include ;
19
+ return Some ( FilterResult :: Include ) ;
20
20
}
21
21
22
22
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;
24
52
}
25
53
26
54
if let Some ( parent) = node. parent ( ) {
@@ -29,9 +57,30 @@ pub fn common_filter(node: &Node) -> FilterResult {
29
57
}
30
58
}
31
59
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
+ }
35
84
}
36
85
37
86
FilterResult :: Include
@@ -46,10 +95,21 @@ pub fn common_filter_with_root_exception(node: &Node) -> FilterResult {
46
95
47
96
#[ cfg( test) ]
48
97
mod tests {
49
- use accesskit:: { Node , NodeId , Role , Tree , TreeUpdate } ;
98
+ use accesskit:: { Node , NodeId , Rect , Role , Tree , TreeUpdate } ;
50
99
use alloc:: vec;
51
100
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
+ }
53
113
54
114
#[ test]
55
115
fn normal ( ) {
@@ -66,10 +126,7 @@ mod tests {
66
126
focus : NodeId ( 0 ) ,
67
127
} ;
68
128
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 ) ) ;
73
130
}
74
131
75
132
#[ test]
@@ -91,10 +148,7 @@ mod tests {
91
148
focus : NodeId ( 0 ) ,
92
149
} ;
93
150
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 ) ) ;
98
152
}
99
153
100
154
#[ test]
@@ -116,10 +170,7 @@ mod tests {
116
170
focus : NodeId ( 1 ) ,
117
171
} ;
118
172
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 ) ) ;
123
174
}
124
175
125
176
#[ test]
@@ -137,18 +188,12 @@ mod tests {
137
188
focus : NodeId ( 0 ) ,
138
189
} ;
139
190
let tree = crate :: Tree :: new ( update, false ) ;
191
+ assert_filter_result ( ExcludeNode , & tree, NodeId ( 0 ) ) ;
140
192
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 ,
146
194
common_filter_with_root_exception( & tree. state( ) . node_by_id( NodeId ( 0 ) ) . unwrap( ) )
147
195
) ;
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 ) ) ;
152
197
}
153
198
154
199
#[ test]
@@ -167,14 +212,8 @@ mod tests {
167
212
focus : NodeId ( 0 ) ,
168
213
} ;
169
214
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 ) ) ;
178
217
}
179
218
180
219
#[ test]
@@ -193,14 +232,8 @@ mod tests {
193
232
focus : NodeId ( 1 ) ,
194
233
} ;
195
234
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 ) ) ;
204
237
}
205
238
206
239
#[ test]
@@ -218,9 +251,131 @@ mod tests {
218
251
focus : NodeId ( 0 ) ,
219
252
} ;
220
253
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 ) ) ;
225
380
}
226
381
}
0 commit comments