pixel_widgets/widget/
progress.rs

1use crate::draw::Primitive;
2use crate::layout::{Direction, Rectangle, Size};
3use crate::node::{GenericNode, IntoNode, Node};
4use crate::style::Stylesheet;
5use crate::widget::{dummy::Dummy, Widget};
6
7/// A progress bar that fill up according to some progress
8/// The bar part of the progress bar can be styled by selecting the child widget `bar` of the `progress` widget.
9/// Progress accepts the `clip-bar` flag for it's style. When the `clip-bar` flag is set, the bar is always rendered
10/// at full size and then clipped according to the progress. When `clip-bar` is not set, the bar itself is rendered
11/// with a size that matches the progress.
12pub struct Progress<'a, T> {
13    progress: f32,
14    fill: Node<'a, T>,
15}
16
17impl<'a, T: 'a> Progress<'a, T> {
18    /// Construct a new `Progress` with a progress value in the range [0.0, 1.0]
19    pub fn new(progress: f32) -> Self {
20        Self {
21            progress,
22            fill: Dummy::new("bar").into_node(),
23        }
24    }
25
26    /// Sets the progress value, which should be in the range [0.0, 1.0]
27    pub fn val(mut self, val: f32) -> Self {
28        self.progress = val;
29        self
30    }
31}
32
33impl<'a, T: 'a> Default for Progress<'a, T> {
34    fn default() -> Self {
35        Self {
36            progress: 0.0,
37            fill: Dummy::new("bar").into_node(),
38        }
39    }
40}
41
42impl<'a, T: 'a> Widget<'a, T> for Progress<'a, T> {
43    type State = ();
44
45    fn mount(&self) {}
46
47    fn widget(&self) -> &'static str {
48        "progress"
49    }
50
51    fn len(&self) -> usize {
52        1
53    }
54
55    fn visit_children(&mut self, visitor: &mut dyn FnMut(&mut dyn GenericNode<'a, T>)) {
56        visitor(&mut *self.fill);
57    }
58
59    fn size(&self, _: &(), style: &Stylesheet) -> (Size, Size) {
60        (style.width, style.height)
61    }
62
63    fn draw(&mut self, _: &mut (), layout: Rectangle, clip: Rectangle, style: &Stylesheet) -> Vec<Primitive<'a>> {
64        let mut result = Vec::new();
65        result.extend(style.background.render(layout));
66        let fill = layout.after_padding(style.padding);
67        let fill = match style.direction {
68            Direction::LeftToRight => Rectangle {
69                right: fill.left + fill.width() * self.progress,
70                ..fill
71            },
72            Direction::RightToLeft => Rectangle {
73                left: fill.right - fill.width() * self.progress,
74                ..fill
75            },
76            Direction::TopToBottom => Rectangle {
77                bottom: fill.top + fill.height() * self.progress,
78                ..fill
79            },
80            Direction::BottomToTop => Rectangle {
81                top: fill.bottom - fill.height() * self.progress,
82                ..fill
83            },
84        };
85
86        if self.progress > 0.0 {
87            if style.contains("clip-bar") {
88                if let Some(clip) = clip.intersect(&fill) {
89                    result.push(Primitive::PushClip(clip));
90                    result.extend(self.fill.draw(layout.after_padding(style.padding), clip));
91                    result.push(Primitive::PopClip);
92                }
93            } else {
94                result.extend(self.fill.draw(fill, clip));
95            }
96        }
97
98        result
99    }
100}
101
102impl<'a, T: 'a> IntoNode<'a, T> for Progress<'a, T> {
103    fn into_node(self) -> Node<'a, T> {
104        Node::from_widget(self)
105    }
106}