1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
//! Provide progress feedback to your users.
use crate::{bumpalo, css, Bus, Css, Element, Length, Widget};

pub use iced_style::progress_bar::{Style, StyleSheet};

use std::ops::RangeInclusive;

/// A bar that displays progress.
///
/// # Example
/// ```
/// use iced_web::ProgressBar;
///
/// let value = 50.0;
///
/// ProgressBar::new(0.0..=100.0, value);
/// ```
///
/// ![Progress bar](https://user-images.githubusercontent.com/18618951/71662391-a316c200-2d51-11ea-9cef-52758cab85e3.png)
#[allow(missing_debug_implementations)]
pub struct ProgressBar {
    range: RangeInclusive<f32>,
    value: f32,
    width: Length,
    height: Option<Length>,
    style: Box<dyn StyleSheet>,
}

impl ProgressBar {
    /// Creates a new [`ProgressBar`].
    ///
    /// It expects:
    ///   * an inclusive range of possible values
    ///   * the current value of the [`ProgressBar`]
    pub fn new(range: RangeInclusive<f32>, value: f32) -> Self {
        ProgressBar {
            value: value.max(*range.start()).min(*range.end()),
            range,
            width: Length::Fill,
            height: None,
            style: Default::default(),
        }
    }

    /// Sets the width of the [`ProgressBar`].
    pub fn width(mut self, width: Length) -> Self {
        self.width = width;
        self
    }

    /// Sets the height of the [`ProgressBar`].
    pub fn height(mut self, height: Length) -> Self {
        self.height = Some(height);
        self
    }

    /// Sets the style of the [`ProgressBar`].
    pub fn style(mut self, style: impl Into<Box<dyn StyleSheet>>) -> Self {
        self.style = style.into();
        self
    }
}

impl<Message> Widget<Message> for ProgressBar {
    fn node<'b>(
        &self,
        bump: &'b bumpalo::Bump,
        _bus: &Bus<Message>,
        _style_sheet: &mut Css<'b>,
    ) -> dodrio::Node<'b> {
        use dodrio::builder::*;

        let (range_start, range_end) = self.range.clone().into_inner();
        let amount_filled =
            (self.value - range_start) / (range_end - range_start).max(1.0);

        let style = self.style.style();

        let bar = div(bump)
            .attr(
                "style",
                bumpalo::format!(
                    in bump,
                    "width: {}%; height: 100%; background: {}",
                    amount_filled * 100.0,
                    css::background(style.bar)
                )
                .into_bump_str(),
            )
            .finish();

        let node = div(bump).attr(
            "style",
            bumpalo::format!(
                in bump,
                "width: {}; height: {}; background: {}; border-radius: {}px; overflow: hidden;",
                css::length(self.width),
                css::length(self.height.unwrap_or(Length::Units(30))),
                css::background(style.background),
                style.border_radius
            )
            .into_bump_str(),
        ).children(vec![bar]);

        node.finish()
    }
}

impl<'a, Message> From<ProgressBar> for Element<'a, Message>
where
    Message: 'static,
{
    fn from(container: ProgressBar) -> Element<'a, Message> {
        Element::new(container)
    }
}