use crate::{
layout::system::{WidgetLayout, WidgetPreviousLayout},
picking_backend::MouseWheelScroll,
prelude::*,
};
use bevy::prelude::*;
use super::ScrollContext;
#[derive(Widget, Component, Reflect, Default, Clone, PartialEq)]
#[auto_update(render)]
#[props(ScrollBox, PassedChildren, WidgetLayout)]
#[context(ScrollContext)]
#[require(WoodpeckerStyle, WidgetChildren, PassedChildren)]
pub struct ScrollBox {
pub always_show_scrollbar: bool,
pub disable_horizontal: bool,
pub disable_vertical: bool,
pub hide_horizontal: bool,
pub hide_vertical: bool,
pub scrollbar_thickness: Option<f32>,
pub thumb_thickness: Option<f32>,
pub scroll_line: Option<f32>,
pub thumb_color: Option<Color>,
pub thumb_styles: Option<WoodpeckerStyle>,
pub track_color: Option<Color>,
pub track_styles: Option<WoodpeckerStyle>,
}
pub fn render(
mut commands: Commands,
mut context_helper: ResMut<HookHelper>,
current_widget: Res<CurrentWidget>,
mut query: Query<(
&ScrollBox,
&PassedChildren,
&mut WidgetChildren,
&mut WoodpeckerStyle,
&WidgetLayout,
&WidgetPreviousLayout,
)>,
mut context_query: Query<&mut ScrollContext>,
) {
let Ok((scroll_box, passed_children, mut children, mut styles, layout, prev_layout)) =
query.get_mut(**current_widget)
else {
return;
};
let context_entity =
context_helper.use_context(&mut commands, *current_widget, ScrollContext::default());
let Ok(mut context) = context_query.get_mut(context_entity) else {
return;
};
let always_show_scrollbar = scroll_box.always_show_scrollbar;
let disable_horizontal = scroll_box.disable_horizontal;
let disable_vertical = scroll_box.disable_vertical;
let hide_horizontal = scroll_box.hide_horizontal;
let hide_vertical = scroll_box.hide_vertical;
let scrollbar_thickness = scroll_box.scrollbar_thickness.unwrap_or(10.0);
let scroll_line = scroll_box.scroll_line.unwrap_or(64.0);
let thumb_color = scroll_box.thumb_color;
let thumb_styles = scroll_box.thumb_styles;
let track_color = scroll_box.track_color;
let track_styles = scroll_box.track_styles;
let scrollable_width = context.scrollable_width();
let scrollable_height = context.scrollable_height();
let hori_thickness = scrollbar_thickness;
let vert_thickness = scrollbar_thickness;
let hide_horizontal =
hide_horizontal || !always_show_scrollbar && scrollable_width < f32::EPSILON;
let hide_vertical = hide_vertical || !always_show_scrollbar && scrollable_height < f32::EPSILON;
let pad_x = if hide_vertical { 0.0 } else { vert_thickness };
let pad_y = if hide_horizontal { 0.0 } else { hori_thickness };
if pad_x != context.pad_x || pad_y != context.pad_y {
context.pad_x = pad_x;
context.pad_y = pad_y;
}
if prev_layout != layout {
context.scrollbox_width = layout.width();
context.scrollbox_height = layout.height();
}
*styles = WoodpeckerStyle {
width: Units::Percentage(100.0),
height: Units::Percentage(100.0),
margin: Edge::all(0.0).right(scrollbar_thickness / 2.0),
..*styles
};
let hbox_styles = WoodpeckerStyle {
width: Units::Percentage(100.0),
..Default::default()
};
let vbox_styles = WoodpeckerStyle {
width: Units::Percentage(100.0),
..Default::default()
};
let scroll_content_bundle = (ScrollContent, passed_children.0.clone());
let mut vbox_children = WidgetChildren::default().with_child::<Clip>((
Clip,
WidgetChildren::default().with_child::<ScrollContent>(scroll_content_bundle),
));
if !hide_horizontal {
vbox_children.add::<ScrollBar>(ScrollBar {
disabled: disable_horizontal,
horizontal: true,
thickness: hori_thickness,
thumb_thickness: scroll_box.thumb_thickness,
thumb_color,
thumb_styles,
track_color,
track_styles,
});
}
let mut element_wrapper_children =
WidgetChildren::default().with_child::<Element>((Element, vbox_styles, vbox_children));
if !hide_vertical {
element_wrapper_children.add::<ScrollBar>(ScrollBar {
disabled: disable_vertical,
thickness: hori_thickness,
thumb_thickness: scroll_box.thumb_thickness,
thumb_color,
thumb_styles,
track_color,
track_styles,
..Default::default()
});
}
children
.add::<Element>((
Element,
hbox_styles,
element_wrapper_children,
Pickable::default(),
))
.observe(
*current_widget,
move |mut trigger: Trigger<Pointer<MouseWheelScroll>>,
mut context_query: Query<&mut ScrollContext>| {
let x = trigger.scroll.x;
let y = trigger.scroll.y;
trigger.propagate(false);
if let Ok(mut context) = context_query.get_mut(context_entity) {
let scroll_x = context.scroll_x();
let scroll_y = context.scroll_y();
if !disable_horizontal {
context.set_scroll_x(scroll_x - x * scroll_line);
}
if !disable_vertical {
context.set_scroll_y(scroll_y + y * scroll_line);
}
}
},
);
children.apply(current_widget.as_parent());
}