Skip to main content

beuvy_runtime/
scroll.rs

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}