//! Progress bars visualize the progression of an extended computer operation, such as a download, file transfer, or installation. //! //! # Example //! ```no_run //! # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } //! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; //! # //! use iced::widget::progress_bar; //! //! struct State { //! progress: f32, //! } //! //! enum Message { //! // ... //! } //! //! fn view(state: &State) -> Element<'_, Message> { //! progress_bar(0.0..=100.0, state.progress).into() //! } //! ``` use crate::core::border::{self, Border}; use crate::core::layout; use crate::core::mouse; use crate::core::renderer; use crate::core::widget::Tree; use crate::core::{ self, Background, Color, Element, Layout, Length, Rectangle, Size, Theme, Widget, }; use std::ops::RangeInclusive; /// A bar that displays progress. /// /// # Example /// ```no_run /// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } /// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; /// # /// use iced::widget::progress_bar; /// /// struct State { /// progress: f32, /// } /// /// enum Message { /// // ... /// } /// /// fn view(state: &State) -> Element<'_, Message> { /// progress_bar(0.0..=100.0, state.progress).into() /// } /// ``` #[allow(missing_debug_implementations)] pub struct ProgressBar<'a, Theme = crate::Theme> where Theme: Catalog, { range: RangeInclusive, value: f32, length: Length, girth: Length, is_vertical: bool, class: Theme::Class<'a>, } impl<'a, Theme> ProgressBar<'a, Theme> where Theme: Catalog, { /// The default girth of a [`ProgressBar`]. pub const DEFAULT_GIRTH: f32 = 30.0; /// Creates a new [`ProgressBar`]. /// /// It expects: /// * an inclusive range of possible values /// * the current value of the [`ProgressBar`] pub fn new(range: RangeInclusive, value: f32) -> Self { ProgressBar { value: value.clamp(*range.start(), *range.end()), range, length: Length::Fill, girth: Length::from(Self::DEFAULT_GIRTH), is_vertical: false, class: Theme::default(), } } /// Sets the width of the [`ProgressBar`]. pub fn length(mut self, length: impl Into) -> Self { self.length = length.into(); self } /// Sets the height of the [`ProgressBar`]. pub fn girth(mut self, girth: impl Into) -> Self { self.girth = girth.into(); self } /// Turns the [`ProgressBar`] into a vertical [`ProgressBar`]. /// /// By default, a [`ProgressBar`] is horizontal. pub fn vertical(mut self) -> Self { self.is_vertical = true; self } /// Sets the style of the [`ProgressBar`]. #[must_use] pub fn style(mut self, style: impl Fn(&Theme) -> Style + 'a) -> Self where Theme::Class<'a>: From>, { self.class = (Box::new(style) as StyleFn<'a, Theme>).into(); self } /// Sets the style class of the [`ProgressBar`]. #[cfg(feature = "advanced")] #[must_use] pub fn class(mut self, class: impl Into>) -> Self { self.class = class.into(); self } fn width(&self) -> Length { if self.is_vertical { self.girth } else { self.length } } fn height(&self) -> Length { if self.is_vertical { self.length } else { self.girth } } } impl Widget for ProgressBar<'_, Theme> where Theme: Catalog, Renderer: core::Renderer, { fn size(&self) -> Size { Size { width: self.width(), height: self.height(), } } fn layout( &self, _tree: &mut Tree, _renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { layout::atomic(limits, self.width(), self.height()) } fn draw( &self, _state: &Tree, renderer: &mut Renderer, theme: &Theme, _style: &renderer::Style, layout: Layout<'_>, _cursor: mouse::Cursor, _viewport: &Rectangle, ) { let bounds = layout.bounds(); let (range_start, range_end) = self.range.clone().into_inner(); let length = if self.is_vertical { bounds.height } else { bounds.width }; let active_progress_length = if range_start >= range_end { 0.0 } else { length * (self.value - range_start) / (range_end - range_start) }; let style = theme.style(&self.class); renderer.fill_quad( renderer::Quad { bounds: Rectangle { ..bounds }, border: style.border, ..renderer::Quad::default() }, style.background, ); if active_progress_length > 0.0 { let bounds = if self.is_vertical { Rectangle { y: bounds.y + bounds.height - active_progress_length, height: active_progress_length, ..bounds } } else { Rectangle { width: active_progress_length, ..bounds } }; renderer.fill_quad( renderer::Quad { bounds, border: Border { color: Color::TRANSPARENT, ..style.border }, ..renderer::Quad::default() }, style.bar, ); } } } impl<'a, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> where Message: 'a, Theme: 'a + Catalog, Renderer: 'a + core::Renderer, { fn from( progress_bar: ProgressBar<'a, Theme>, ) -> Element<'a, Message, Theme, Renderer> { Element::new(progress_bar) } } /// The appearance of a progress bar. #[derive(Debug, Clone, Copy, PartialEq)] pub struct Style { /// The [`Background`] of the progress bar. pub background: Background, /// The [`Background`] of the bar of the progress bar. pub bar: Background, /// The [`Border`] of the progress bar. pub border: Border, } /// The theme catalog of a [`ProgressBar`]. pub trait Catalog: Sized { /// The item class of the [`Catalog`]. type Class<'a>; /// The default class produced by the [`Catalog`]. fn default<'a>() -> Self::Class<'a>; /// The [`Style`] of a class with the given status. fn style(&self, class: &Self::Class<'_>) -> Style; } /// A styling function for a [`ProgressBar`]. /// /// This is just a boxed closure: `Fn(&Theme, Status) -> Style`. pub type StyleFn<'a, Theme> = Box Style + 'a>; impl Catalog for Theme { type Class<'a> = StyleFn<'a, Self>; fn default<'a>() -> Self::Class<'a> { Box::new(primary) } fn style(&self, class: &Self::Class<'_>) -> Style { class(self) } } /// The primary style of a [`ProgressBar`]. pub fn primary(theme: &Theme) -> Style { let palette = theme.extended_palette(); styled(palette.background.strong.color, palette.primary.base.color) } /// The secondary style of a [`ProgressBar`]. pub fn secondary(theme: &Theme) -> Style { let palette = theme.extended_palette(); styled( palette.background.strong.color, palette.secondary.base.color, ) } /// The success style of a [`ProgressBar`]. pub fn success(theme: &Theme) -> Style { let palette = theme.extended_palette(); styled(palette.background.strong.color, palette.success.base.color) } /// The warning style of a [`ProgressBar`]. pub fn warning(theme: &Theme) -> Style { let palette = theme.extended_palette(); styled(palette.background.strong.color, palette.warning.base.color) } /// The danger style of a [`ProgressBar`]. pub fn danger(theme: &Theme) -> Style { let palette = theme.extended_palette(); styled(palette.background.strong.color, palette.danger.base.color) } fn styled( background: impl Into, bar: impl Into, ) -> Style { Style { background: background.into(), bar: bar.into(), border: border::rounded(2), } }