use bevy_ecs::prelude::*;
use super::button::ClickHandler;
use super::color::Color;
use super::widget::Widget;
use crate::scene::{Scene, SceneCommands, SceneEntity};
#[derive(Component, Clone, Debug)]
pub struct Progress {
pub value: f32,
pub fill_over: Option<f32>,
pub width_ratio: f32,
pub height: f32,
pub y_ratio: f32,
pub y_offset: f32,
pub padding: f32,
pub track: Color,
pub fill: Color,
}
impl Progress {
pub fn advance(&mut self, delta: f32) {
let Some(duration) = self.fill_over else {
return;
};
if duration <= 0.0 {
self.value = 1.0;
} else {
self.value = (self.value + delta / duration).min(1.0);
}
}
pub fn filled_width(&self, track_width: f32) -> f32 {
let inner = (track_width - self.padding * 2.0).max(0.0);
inner * self.value.clamp(0.0, 1.0)
}
}
#[derive(Component)]
pub struct OnFull {
handler: ClickHandler,
armed: bool,
}
impl OnFull {
pub fn new(handler: ClickHandler) -> Self {
Self {
handler,
armed: true,
}
}
pub fn is_armed(&self) -> bool {
self.armed
}
pub fn fire(&mut self) {
self.armed = false;
(self.handler)();
}
}
pub struct ProgressBar {
progress: Progress,
on_full: Option<ClickHandler>,
next_scene: Option<Box<dyn Scene>>,
}
impl ProgressBar {
pub fn new() -> Self {
Self {
on_full: None,
next_scene: None,
progress: Progress {
value: 0.0,
fill_over: None,
width_ratio: 0.34,
height: 18.0,
y_ratio: 0.5,
y_offset: 0.0,
padding: 3.0,
track: Color::srgba(0.16, 0.17, 0.22, 0.92),
fill: Color::srgb(0.42, 0.62, 0.86),
},
}
}
pub fn value(mut self, value: f32) -> Self {
self.progress.value = value;
self
}
pub fn fill_over(mut self, seconds: f32) -> Self {
self.progress.fill_over = Some(seconds);
self
}
pub fn width_ratio(mut self, width_ratio: f32) -> Self {
self.progress.width_ratio = width_ratio;
self
}
pub fn height(mut self, height: f32) -> Self {
self.progress.height = height;
self
}
pub fn y_ratio(mut self, y_ratio: f32) -> Self {
self.progress.y_ratio = y_ratio;
self
}
pub fn y_offset(mut self, y_offset: f32) -> Self {
self.progress.y_offset = y_offset;
self
}
pub fn on_full(mut self, on_full: impl FnMut() + Send + Sync + 'static) -> Self {
self.on_full = Some(Box::new(on_full));
self
}
pub fn then_scene(mut self, scene: impl Scene) -> Self {
self.next_scene = Some(Box::new(scene));
self
}
pub fn colors(mut self, track: Color, fill: Color) -> Self {
self.progress.track = track;
self.progress.fill = fill;
self
}
}
impl Default for ProgressBar {
fn default() -> Self {
Self::new()
}
}
impl Widget for ProgressBar {
type Output = Entity;
fn spawn(self, world: &mut World, _screen_width: f32, _screen_height: f32) -> Entity {
let handler = full_handler(world, self.on_full, self.next_scene);
let mut entity = world.spawn((self.progress, SceneEntity));
if let Some(handler) = handler {
entity.insert(OnFull::new(handler));
}
entity.id()
}
}
fn full_handler(
world: &mut World,
on_full: Option<ClickHandler>,
next_scene: Option<Box<dyn Scene>>,
) -> Option<ClickHandler> {
let Some(scene) = next_scene else {
return on_full;
};
let scenes = world
.get_resource_or_insert_with(SceneCommands::default)
.clone();
let scene = std::sync::Mutex::new(Some(scene));
let mut on_full = on_full;
Some(Box::new(move || {
if let Some(on_full) = on_full.as_mut() {
on_full();
}
if let Some(scene) = scene.lock().expect("scene handoff poisoned").take() {
scenes.change_boxed(scene);
}
}))
}