#![allow(dead_code)]
pub mod content;
pub mod scroll_bar;
pub mod scroll_box;
use std::sync::Arc;
use crate::prelude::*;
use bevy::prelude::*;
#[derive(Component, Default, Reflect, Debug, Copy, Clone, PartialEq)]
pub struct ScrollContext {
pub(super) scroll_x: f32,
pub(super) scroll_y: f32,
pub(super) content_width: f32,
pub(super) content_height: f32,
pub(super) scrollbox_width: f32,
pub(super) scrollbox_height: f32,
pub(super) pad_x: f32,
pub(super) pad_y: f32,
pub(super) mode: ScrollMode,
pub(super) is_dragging: bool,
pub(super) start_pos: Vec2,
pub(super) start_offset: Vec2,
}
#[non_exhaustive]
#[derive(Default, Debug, Reflect, Copy, Clone, PartialEq, Eq)]
pub enum ScrollMode {
#[default]
Clamped,
Infinite,
}
impl ScrollContext {
pub fn scroll_x(&self) -> f32 {
self.scroll_x
}
pub fn scroll_y(&self) -> f32 {
self.scroll_y
}
pub fn content_width(&self) -> f32 {
if self.content_width > self.scrollbox_width {
self.content_width + self.pad_x
} else {
self.content_width
}
}
pub fn content_height(&self) -> f32 {
if self.content_height > self.scrollbox_height {
self.content_height + self.pad_y
} else {
self.content_height
}
}
pub fn scrollable_width(&self) -> f32 {
(self.content_width() - self.scrollbox_width).max(0.0)
}
pub fn scrollable_height(&self) -> f32 {
(self.content_height() - self.scrollbox_height).max(0.0)
}
pub fn mode(&self) -> ScrollMode {
self.mode
}
pub fn set_scroll_x(&mut self, x: f32) {
let min = -self.scrollable_width();
self.scroll_x = match self.mode {
ScrollMode::Clamped => ScrollContext::clamped(x, min, 0.0),
ScrollMode::Infinite => x,
}
}
pub fn set_scroll_y(&mut self, y: f32) {
let min = -self.scrollable_height();
self.scroll_y = match self.mode {
ScrollMode::Clamped => ScrollContext::clamped(y, min, 0.0),
ScrollMode::Infinite => y,
};
}
pub fn percent_x(&self) -> f32 {
let width = self.scrollable_width();
if width <= f32::EPSILON {
0.0
} else {
self.scroll_x / width
}
}
pub fn percent_y(&self) -> f32 {
let height = self.scrollable_height();
if height <= f32::EPSILON {
0.0
} else {
self.scroll_y / height
}
}
fn clamped(value: f32, min: f32, max: f32) -> f32 {
value.clamp(min, max)
}
}
#[derive(Component, Widget, Reflect, Default, PartialEq, Clone)]
#[auto_update(render)]
#[props(ScrollContextProvider)]
#[require(WidgetChildren, WoodpeckerStyle)]
pub struct ScrollContextProvider {
pub initial_value: ScrollContext,
#[reflect(ignore)]
pub tag: Option<TaggedContext>,
}
#[derive(Clone, Reflect)]
pub struct TaggedContext {
pub(crate) f: Arc<dyn Fn(EntityCommands<'_>) + 'static + Send + Sync>,
}
impl PartialEq for TaggedContext {
fn eq(&self, _other: &Self) -> bool {
true
}
}
impl Default for TaggedContext {
fn default() -> Self {
Self {
f: Arc::new(|_| {}),
}
}
}
impl TaggedContext {
pub fn new(c: impl Component + Copy) -> Self {
let f = Arc::new(move |mut commands: EntityCommands<'_>| {
commands.insert(c);
});
Self { f }
}
}
pub fn render(
mut commands: Commands,
mut context: ResMut<HookHelper>,
current_widget: Res<CurrentWidget>,
mut query: Query<(&mut WidgetChildren, &ScrollContextProvider)>,
) {
let Ok((mut children, provider)) = query.get_mut(**current_widget) else {
return;
};
let entity = context.use_context(&mut commands, *current_widget, provider.initial_value);
if let Some(tag) = provider.tag.as_ref() {
(tag.f)(commands.entity(entity));
}
children.apply(current_widget.as_parent());
}
fn map_range(value: f32, from_range: (f32, f32), to_range: (f32, f32)) -> f32 {
let from_diff = from_range.1 - from_range.0;
if from_diff <= f32::EPSILON {
value
} else {
to_range.0 + (value - from_range.0) * (to_range.1 - to_range.0) / from_diff
}
}