use bevy::input::mouse::MouseScrollUnit;
use bevy::picking::events::Scroll;
use bevy::prelude::*;
#[derive(Component, Default, Debug, Clone, Copy)]
pub struct MouseWheelScroll;
pub fn scroll_container_node(mut node: Node) -> Node {
node.min_width = Val::Px(0.0);
node.min_height = Val::Px(0.0);
node.overflow = Overflow::scroll_y();
node.scrollbar_width = crate::style::scrollbar_width();
node
}
pub(crate) fn materialize_scroll_containers(
mut commands: Commands,
mut query: Query<
(Entity, &mut Node, Has<MouseWheelScroll>),
Or<(Added<Node>, Changed<Node>)>,
>,
) {
for (entity, mut node, has_mouse_wheel_scroll) in &mut query {
let scroll_x = node.overflow.x == OverflowAxis::Scroll;
let scroll_y = node.overflow.y == OverflowAxis::Scroll;
let is_scroll_container = scroll_x || scroll_y;
if !is_scroll_container {
if has_mouse_wheel_scroll {
commands.entity(entity).remove::<MouseWheelScroll>();
}
continue;
}
node.min_width = Val::Px(0.0);
node.min_height = Val::Px(0.0);
node.scrollbar_width = crate::style::scrollbar_width();
commands
.entity(entity)
.try_insert((ScrollPosition::default(), MouseWheelScroll));
}
}
pub(crate) fn handle_mouse_wheel_scroll(
mut event: On<Pointer<Scroll>>,
mut query: Query<
(
&mut ScrollPosition,
&Node,
&ComputedNode,
Has<MouseWheelScroll>,
),
>,
keyboard_input: Res<ButtonInput<KeyCode>>,
) {
let Ok((mut scroll_position, node, computed, enabled)) = query.get_mut(event.entity) else {
return;
};
if !enabled {
return;
}
let line_height = crate::style::font_size_control() * 1.5;
let mut delta = -Vec2::new(event.x, event.y);
if event.unit == MouseScrollUnit::Line {
delta *= line_height;
}
if keyboard_input.any_pressed([KeyCode::ControlLeft, KeyCode::ControlRight]) {
std::mem::swap(&mut delta.x, &mut delta.y);
}
let max_offset =
(computed.content_size() - computed.size()).max(Vec2::ZERO) * computed.inverse_scale_factor();
let scroll_position = &mut scroll_position.0;
if node.overflow.x == OverflowAxis::Scroll && delta.x != 0.0 {
let next_x = (scroll_position.x + delta.x).clamp(0.0, max_offset.x);
if next_x != scroll_position.x {
scroll_position.x = next_x;
delta.x = 0.0;
}
}
if node.overflow.y == OverflowAxis::Scroll && delta.y != 0.0 {
let next_y = (scroll_position.y + delta.y).clamp(0.0, max_offset.y);
if next_y != scroll_position.y {
scroll_position.y = next_y;
delta.y = 0.0;
}
}
if delta == Vec2::ZERO {
event.propagate(false);
}
}