use crate::{builder::validator::progressbar_info::ProgressBarInfo, prelude::*}; use anyhow::Result; use std::sync::{ atomic::{AtomicBool, Ordering::SeqCst}, Arc, Mutex, }; use utilities::prelude::*; use super::{ fill_type::{FillType, InnerFillType}, wrapper::TextableWrapper, }; #[derive(Clone)] pub enum GrowDirection { LeftToRight, BottomToTop, } pub struct ProgressBarBuilder { // bar background: Option, foreground: Option, direction: GrowDirection, // text text: Option, text_ratio: f32, text_color: Color, text_alignment: TextAlignment, } impl ProgressBarBuilder { pub fn set_background(mut self, background: impl Into) -> Self { self.background = Some(background.into()); self } pub fn set_foreground(mut self, foreground: impl Into) -> Self { self.foreground = Some(foreground.into()); self } pub fn set_grow_direction(mut self, direction: GrowDirection) -> Self { self.direction = direction; self } pub fn set_text(mut self, text: &str) -> Self { self.text = Some(text.to_string()); self } pub fn set_text_ratio(mut self, ratio: f32) -> Self { self.text_ratio = ratio; self } pub fn set_text_color(mut self, text_color: Color) -> Self { self.text_color = text_color; self } pub fn set_text_alignment(mut self, text_alignment: TextAlignment) -> Self { self.text_alignment = text_alignment; self } pub fn build(self, gui_handler: Arc) -> Result> { let framable = Framable::new(gui_handler, false)?; let background = self .background .map(|r#type| FillType::new(framable.clone(), r#type)) .transpose()?; let foreground = self .foreground .map(|r#type| FillType::new(framable.clone(), r#type)) .transpose()?; let textable_wrapper = TextableWrapper::new( framable.clone(), self.text_color, self.text_ratio, self.text_alignment, ); if let Some(text) = self.text { textable_wrapper.set_text(&text, false)?; } Ok(Arc::new(ProgressBar { framable, textable_wrapper, background, foreground, direction: self.direction, visible: AtomicBool::new(false), progress: Mutex::new(0.0), })) } } pub struct ProgressBar { framable: Arc, textable_wrapper: TextableWrapper, background: Option, foreground: Option, direction: GrowDirection, visible: AtomicBool, progress: Mutex, } impl ProgressBar { pub fn builder() -> ProgressBarBuilder { ProgressBarBuilder { background: None, foreground: None, direction: GrowDirection::LeftToRight, text: None, text_ratio: 0.7, text_color: Color::default(), text_alignment: TextAlignment::default(), } } pub fn set_progress(&self, mut progress: f32) -> Result<()> { if progress < 0.0 { progress = 0.0; } else if progress > 1.0 { progress = 1.0; } if !almost_eq(self.progress(), progress) { *self.progress.lock().unwrap() = progress; if let Some(foreground) = &self.foreground { match self.direction { GrowDirection::LeftToRight => match &foreground.inner { InnerFillType::Image(displayable) => { displayable.set_right_factor(progress); displayable.set_right_uv_factor(progress); displayable.update_frame()?; } InnerFillType::Color(colorable) => { colorable.set_right_factor(progress); colorable.update_frame()?; } }, GrowDirection::BottomToTop => match &foreground.inner { InnerFillType::Image(displayable) => { displayable.set_top_factor(progress); displayable.set_top_uv_factor(progress); displayable.update_frame()?; } InnerFillType::Color(colorable) => { colorable.set_top_factor(progress); colorable.update_frame()?; } }, } } } Ok(()) } pub fn set_text(&self, text: impl ToString) -> Result<()> { self.textable_wrapper.set_text(text, self.visible()) } pub fn set_text_color(&self, text_color: Color) -> Result<()> { self.textable_wrapper.set_text_color(text_color) } pub fn set_text_ratio(&self, ratio: f32) -> Result<()> { self.textable_wrapper.set_height_ratio(ratio) } pub fn set_text_alignment(&self, text_alignment: TextAlignment) -> Result<()> { self.textable_wrapper.set_alignment(text_alignment) } pub fn try_from( progress_bar_info: &ProgressBarInfo, gui_handler: &Arc, ) -> Result> { let mut progress_bar_builder = ProgressBar::builder() .set_text_alignment(progress_bar_info.text_alignment) .set_text_color(progress_bar_info.text_color); if let Some(text_ratio) = progress_bar_info.text_ratio { progress_bar_builder = progress_bar_builder.set_text_ratio(text_ratio); } let text = progress_bar_info.text.read().unwrap().clone(); if !text.is_empty() { progress_bar_builder = progress_bar_builder.set_text(&text); } if let Some(background) = progress_bar_info.background.clone() { progress_bar_builder = progress_bar_builder.set_background(background); } if let Some(foreground) = progress_bar_info.foreground.clone() { progress_bar_builder = progress_bar_builder.set_foreground(foreground); } if let Some(dir) = progress_bar_info.direction.clone() { progress_bar_builder = progress_bar_builder.set_grow_direction(dir); } progress_bar_builder.build(gui_handler.clone()) } #[inline] fn progress(&self) -> f32 { *self.progress.lock().unwrap() } fn disable_base(&self) -> Result<()> { Framable::delete(&self.framable)?; if let Some(background) = &self.background { background.disable()?; } if let Some(foreground) = &self.foreground { foreground.disable()?; } self.textable_wrapper.disable()?; Ok(()) } } impl GuiElementTraits for ProgressBar { fn gridable(&self) -> Option<&dyn Gridable> { Some(self) } fn visibility(&self) -> Option<&dyn Visibility> { Some(self) } fn downcast<'a>(&'a self) -> Option> { Some(GuiElement::ProgressBar(self)) } } impl Visibility for ProgressBar { fn visible(&self) -> bool { self.visible.load(SeqCst) } fn set_visibility(&self, visibility: bool) -> Result<()> { if visibility != self.visible() { self.visible.store(visibility, SeqCst); if visibility { Framable::add(&self.framable)?; if let Some(background) = &self.background { background.enable()?; } if let Some(foreground) = &self.foreground { foreground.enable()?; } self.textable_wrapper.enable()?; } else { self.disable_base()?; } } Ok(()) } } impl Gridable for ProgressBar { fn set_frame( &self, x: i32, y: i32, w: u32, h: u32, vert_align: VerticalAlign, hori_align: HorizontalAlign, ) -> Result<()> { self.framable.set_frame(x, y, w, h, vert_align, hori_align); if let Some(background) = &self.background { background.update_frame()?; } if let Some(foreground) = &self.foreground { foreground.update_frame()?; } self.textable_wrapper.update()?; Ok(()) } fn selectable(&self) -> Option<&Arc> { None } fn type_name(&self) -> &str { "ProgressBar" } fn set_layer(&self, layer: i32) -> Result<()> { self.framable.set_ui_layer(layer); self.textable_wrapper.set_ui_layer(layer)?; if let Some(background) = &self.background { background.set_ui_layer(layer); } if let Some(foreground) = &self.foreground { foreground.set_ui_layer(layer); } Ok(()) } fn position_extent(&self) -> (i32, i32, u32, u32) { self.framable.position() } } impl Drop for ProgressBar { fn drop(&mut self) { if self.visible.load(SeqCst) { self.disable_base().unwrap(); } } }