@@ -15,14 +15,99 @@ fn main() {
15
15
app. add_plugins ( DefaultPlugins )
16
16
. insert_resource ( WinitSettings :: desktop_app ( ) )
17
17
. add_systems ( Startup , setup)
18
- . add_systems ( Update , update_scroll_position) ;
18
+ . add_systems ( Update , send_scroll_events)
19
+ . add_observer ( on_scroll_handler) ;
19
20
20
21
app. run ( ) ;
21
22
}
22
23
23
- const FONT_SIZE : f32 = 20. ;
24
24
const LINE_HEIGHT : f32 = 21. ;
25
25
26
+ /// Injects scroll events into the UI hierarchy.
27
+ fn send_scroll_events (
28
+ mut mouse_wheel_events : EventReader < MouseWheel > ,
29
+ hover_map : Res < HoverMap > ,
30
+ keyboard_input : Res < ButtonInput < KeyCode > > ,
31
+ mut commands : Commands ,
32
+ ) {
33
+ for event in mouse_wheel_events. read ( ) {
34
+ let mut delta = -Vec2 :: new ( event. x , event. y ) ;
35
+
36
+ if event. unit == MouseScrollUnit :: Line {
37
+ delta *= LINE_HEIGHT ;
38
+ }
39
+
40
+ if keyboard_input. any_pressed ( [ KeyCode :: ControlLeft , KeyCode :: ControlRight ] ) {
41
+ std:: mem:: swap ( & mut delta. x , & mut delta. y ) ;
42
+ }
43
+
44
+ for pointer_map in hover_map. values ( ) {
45
+ for entity in pointer_map. keys ( ) {
46
+ commands. trigger_targets ( Scroll { delta } , * entity) ;
47
+ }
48
+ }
49
+ }
50
+ }
51
+
52
+ /// UI scrolling event.
53
+ #[ derive( Event , EntityEvent , Debug ) ]
54
+ #[ entity_event( auto_propagate, traversal = & ' static ChildOf ) ]
55
+ struct Scroll {
56
+ /// Scroll delta in logical coordinates.
57
+ delta : Vec2 ,
58
+ }
59
+
60
+ fn on_scroll_handler (
61
+ mut trigger : On < Scroll > ,
62
+ mut query : Query < ( & mut ScrollPosition , & Node , & ComputedNode ) > ,
63
+ ) {
64
+ let target = trigger. target ( ) ;
65
+ let delta = & mut trigger. event_mut ( ) . delta ;
66
+
67
+ let Ok ( ( mut scroll_position, node, computed) ) = query. get_mut ( target) else {
68
+ return ;
69
+ } ;
70
+
71
+ let max_offset = ( computed. content_size ( ) - computed. size ( ) ) * computed. inverse_scale_factor ( ) ;
72
+
73
+ if node. overflow . x == OverflowAxis :: Scroll && delta. x != 0. {
74
+ // Is this node already scrolled all the way in the direction of the scroll?
75
+ let max = if delta. x > 0. {
76
+ scroll_position. x >= max_offset. x
77
+ } else {
78
+ scroll_position. x <= 0.
79
+ } ;
80
+
81
+ if !max {
82
+ scroll_position. x += delta. x ;
83
+ // Consume the X portion of the scroll delta.
84
+ delta. x = 0. ;
85
+ }
86
+ }
87
+
88
+ if node. overflow . y == OverflowAxis :: Scroll && delta. y != 0. {
89
+ // Is this node already scrolled all the way in the direction of the scroll?
90
+ let max = if delta. y > 0. {
91
+ scroll_position. y >= max_offset. y
92
+ } else {
93
+ scroll_position. y <= 0.
94
+ } ;
95
+
96
+ if !max {
97
+ scroll_position. y += delta. y ;
98
+ // Consume the Y portion of the scroll delta.
99
+ delta. y = 0. ;
100
+ }
101
+ }
102
+
103
+ // Stop propagating when the delta is fully consumed.
104
+ if * delta == Vec2 :: ZERO {
105
+ trigger. propagate ( false ) ;
106
+ }
107
+ }
108
+
109
+ const FONT_SIZE : f32 = 20. ;
110
+
26
111
fn setup ( mut commands : Commands , asset_server : Res < AssetServer > ) {
27
112
// Camera
28
113
commands. spawn ( ( Camera2d , IsDefaultUiCamera ) ) ;
@@ -39,7 +124,6 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
39
124
flex_direction : FlexDirection :: Column ,
40
125
..default ( )
41
126
} )
42
- . insert ( Pickable :: IGNORE )
43
127
. with_children ( |parent| {
44
128
// horizontal scroll example
45
129
parent
@@ -83,16 +167,12 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
83
167
} ,
84
168
Label ,
85
169
AccessibilityNode ( Accessible :: new ( Role :: ListItem ) ) ,
170
+ Node {
171
+ min_width : Val :: Px ( 200. ) ,
172
+ align_content : AlignContent :: Center ,
173
+ ..default ( )
174
+ } ,
86
175
) )
87
- . insert ( Node {
88
- min_width : Val :: Px ( 200. ) ,
89
- align_content : AlignContent :: Center ,
90
- ..default ( )
91
- } )
92
- . insert ( Pickable {
93
- should_block_lower : false ,
94
- ..default ( )
95
- } )
96
176
. observe (
97
177
|trigger : On < Pointer < Press > > , mut commands : Commands | {
98
178
if trigger. event ( ) . button == PointerButton :: Primary {
@@ -159,10 +239,6 @@ fn vertically_scrolling_list(font_handle: Handle<Font>) -> impl Bundle {
159
239
max_height: Val :: Px ( LINE_HEIGHT ) ,
160
240
..default ( )
161
241
} ,
162
- Pickable {
163
- should_block_lower: false ,
164
- ..default ( )
165
- } ,
166
242
children![ (
167
243
Text ( format!( "Item {i}" ) ) ,
168
244
TextFont {
@@ -171,10 +247,6 @@ fn vertically_scrolling_list(font_handle: Handle<Font>) -> impl Bundle {
171
247
} ,
172
248
Label ,
173
249
AccessibilityNode ( Accessible :: new( Role :: ListItem ) ) ,
174
- Pickable {
175
- should_block_lower: false ,
176
- ..default ( )
177
- }
178
250
) ] ,
179
251
)
180
252
} ) ) )
@@ -217,7 +289,6 @@ fn bidirectional_scrolling_list(font_handle: Handle<Font>) -> impl Bundle {
217
289
flex_direction: FlexDirection :: Row ,
218
290
..default ( )
219
291
} ,
220
- Pickable :: IGNORE ,
221
292
Children :: spawn( SpawnIter ( ( 0 ..10 ) . map( {
222
293
let value = font_handle. clone( ) ;
223
294
move |i| {
@@ -229,10 +300,6 @@ fn bidirectional_scrolling_list(font_handle: Handle<Font>) -> impl Bundle {
229
300
} ,
230
301
Label ,
231
302
AccessibilityNode ( Accessible :: new( Role :: ListItem ) ) ,
232
- Pickable {
233
- should_block_lower: false ,
234
- ..default ( )
235
- } ,
236
303
)
237
304
}
238
305
} ) ) ) ,
@@ -264,45 +331,38 @@ fn nested_scrolling_list(font_handle: Handle<Font>) -> impl Bundle {
264
331
Label ,
265
332
) ,
266
333
(
267
- // Outer, horizontal scrolling container
334
+ // Outer, bi-directional scrolling container
268
335
Node {
269
336
column_gap: Val :: Px ( 20. ) ,
270
337
flex_direction: FlexDirection :: Row ,
271
338
align_self: AlignSelf :: Stretch ,
272
339
height: Val :: Percent ( 50. ) ,
273
- overflow: Overflow :: scroll_x ( ) , // n.b.
340
+ overflow: Overflow :: scroll ( ) ,
274
341
..default ( )
275
342
} ,
276
343
BackgroundColor ( Color :: srgb( 0.10 , 0.10 , 0.10 ) ) ,
277
344
// Inner, scrolling columns
278
- Children :: spawn( SpawnIter ( ( 0 ..30 ) . map( move |oi| {
345
+ Children :: spawn( SpawnIter ( ( 0 ..5 ) . map( move |oi| {
279
346
(
280
347
Node {
281
348
flex_direction: FlexDirection :: Column ,
282
349
align_self: AlignSelf :: Stretch ,
350
+ height: Val :: Percent ( 200. / 5. * ( oi as f32 + 1. ) ) ,
283
351
overflow: Overflow :: scroll_y( ) ,
284
352
..default ( )
285
353
} ,
286
354
BackgroundColor ( Color :: srgb( 0.05 , 0.05 , 0.05 ) ) ,
287
- Pickable {
288
- should_block_lower: false ,
289
- ..default ( )
290
- } ,
291
- Children :: spawn( SpawnIter ( ( 0 ..30 ) . map( {
355
+ Children :: spawn( SpawnIter ( ( 0 ..20 ) . map( {
292
356
let value = font_handle. clone( ) ;
293
357
move |i| {
294
358
(
295
- Text ( format!( "Item {}" , ( oi * 25 ) + i) ) ,
359
+ Text ( format!( "Item {}" , ( oi * 20 ) + i) ) ,
296
360
TextFont {
297
361
font: value. clone( ) ,
298
362
..default ( )
299
363
} ,
300
364
Label ,
301
365
AccessibilityNode ( Accessible :: new( Role :: ListItem ) ) ,
302
- Pickable {
303
- should_block_lower: false ,
304
- ..default ( )
305
- } ,
306
366
)
307
367
}
308
368
} ) ) ) ,
@@ -312,36 +372,3 @@ fn nested_scrolling_list(font_handle: Handle<Font>) -> impl Bundle {
312
372
] ,
313
373
)
314
374
}
315
-
316
- /// Updates the scroll position of scrollable nodes in response to mouse input
317
- pub fn update_scroll_position (
318
- mut mouse_wheel_events : EventReader < MouseWheel > ,
319
- hover_map : Res < HoverMap > ,
320
- mut scrolled_node_query : Query < & mut ScrollPosition > ,
321
- keyboard_input : Res < ButtonInput < KeyCode > > ,
322
- ) {
323
- for mouse_wheel_event in mouse_wheel_events. read ( ) {
324
- let ( mut dx, mut dy) = match mouse_wheel_event. unit {
325
- MouseScrollUnit :: Line => (
326
- mouse_wheel_event. x * LINE_HEIGHT ,
327
- mouse_wheel_event. y * LINE_HEIGHT ,
328
- ) ,
329
- MouseScrollUnit :: Pixel => ( mouse_wheel_event. x , mouse_wheel_event. y ) ,
330
- } ;
331
-
332
- if keyboard_input. pressed ( KeyCode :: ControlLeft )
333
- || keyboard_input. pressed ( KeyCode :: ControlRight )
334
- {
335
- std:: mem:: swap ( & mut dx, & mut dy) ;
336
- }
337
-
338
- for ( _pointer, pointer_map) in hover_map. iter ( ) {
339
- for ( entity, _hit) in pointer_map. iter ( ) {
340
- if let Ok ( mut scroll_position) = scrolled_node_query. get_mut ( * entity) {
341
- scroll_position. x -= dx;
342
- scroll_position. y -= dy;
343
- }
344
- }
345
- }
346
- }
347
- }
0 commit comments