1use bevy::input::mouse::MouseScrollUnit;
2use bevy::picking::events::Scroll;
3use bevy::prelude::*;
4
5#[derive(Component, Default, Debug, Clone, Copy)]
6pub struct MouseWheelScroll;
7
8pub fn scroll_container_node(mut node: Node) -> Node {
9 node.min_width = Val::Px(0.0);
10 node.min_height = Val::Px(0.0);
11 node.overflow = Overflow::scroll_y();
12 node.scrollbar_width = crate::style::scrollbar_width();
13 node
14}
15
16pub(crate) fn materialize_scroll_containers(
17 mut commands: Commands,
18 mut query: Query<
19 (Entity, &mut Node, Has<MouseWheelScroll>),
20 Or<(Added<Node>, Changed<Node>)>,
21 >,
22) {
23 for (entity, mut node, has_mouse_wheel_scroll) in &mut query {
24 let scroll_x = node.overflow.x == OverflowAxis::Scroll;
25 let scroll_y = node.overflow.y == OverflowAxis::Scroll;
26 let is_scroll_container = scroll_x || scroll_y;
27 if !is_scroll_container {
28 if has_mouse_wheel_scroll {
29 commands.entity(entity).remove::<MouseWheelScroll>();
30 }
31 continue;
32 }
33
34 node.min_width = Val::Px(0.0);
35 node.min_height = Val::Px(0.0);
36 node.scrollbar_width = crate::style::scrollbar_width();
37 commands
38 .entity(entity)
39 .try_insert((ScrollPosition::default(), MouseWheelScroll));
40 }
41}
42
43pub(crate) fn handle_mouse_wheel_scroll(
44 mut event: On<Pointer<Scroll>>,
45 mut query: Query<
46 (
47 &mut ScrollPosition,
48 &Node,
49 &ComputedNode,
50 Has<MouseWheelScroll>,
51 ),
52 >,
53 keyboard_input: Res<ButtonInput<KeyCode>>,
54) {
55 let Ok((mut scroll_position, node, computed, enabled)) = query.get_mut(event.entity) else {
56 return;
57 };
58 if !enabled {
59 return;
60 }
61
62 let line_height = crate::style::font_size_control() * 1.5;
63 let mut delta = -Vec2::new(event.x, event.y);
64 if event.unit == MouseScrollUnit::Line {
65 delta *= line_height;
66 }
67
68 if keyboard_input.any_pressed([KeyCode::ControlLeft, KeyCode::ControlRight]) {
69 std::mem::swap(&mut delta.x, &mut delta.y);
70 }
71
72 let max_offset =
73 (computed.content_size() - computed.size()).max(Vec2::ZERO) * computed.inverse_scale_factor();
74
75 let scroll_position = &mut scroll_position.0;
76 if node.overflow.x == OverflowAxis::Scroll && delta.x != 0.0 {
77 let next_x = (scroll_position.x + delta.x).clamp(0.0, max_offset.x);
78 if next_x != scroll_position.x {
79 scroll_position.x = next_x;
80 delta.x = 0.0;
81 }
82 }
83
84 if node.overflow.y == OverflowAxis::Scroll && delta.y != 0.0 {
85 let next_y = (scroll_position.y + delta.y).clamp(0.0, max_offset.y);
86 if next_y != scroll_position.y {
87 scroll_position.y = next_y;
88 delta.y = 0.0;
89 }
90 }
91
92 if delta == Vec2::ZERO {
93 event.propagate(false);
94 }
95}