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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
//! Module housing the `progress_bar` widget
use le::layout::Vec2;
use le::{Layout, Orientation};
use crate::{prelude::*, widgets::Filler, Buffer, Rect, StyledText, Widget};
/// A progress bar to display a value between `0` and
/// [`value`](ProgressBar::value)
pub struct ProgressBar<'a> {
/// The maximum value which the progress bar can go up to
pub total: f64,
/// The value which the progress bar displays.
///
/// This value should always be less than or equal to
/// total whilst being positive.
pub value: f64,
/// The foreground (the parts less than [`value`](ProgressBar::value)) of
/// the bar
pub fg: Vec<StyledText<'a>>,
/// The edge (the separator between [`fg`](ProgressBar::fg) and
/// [`bg`](ProgressBar::bg) of the bar
pub edge: StyledText<'a>,
/// The background (the parts more than [`value`](ProgressBar::value) of
/// the bar
pub bg: Vec<StyledText<'a>>,
/// The direction of the bar
pub orientation: Orientation,
}
impl<'a> Default for ProgressBar<'a> {
fn default() -> Self {
ProgressBar {
fg: vec![" ".styled().bg_green()],
bg: vec!["─".styled().cyan().bold()],
edge: "▌".styled().green(),
total: 1.0,
value: 0.0,
orientation: Orientation::Horizontal,
}
}
}
impl<'a> ProgressBar<'a> {
/// Creates a new [`ProgressBar`]
///
/// By default it has a green foreground and a cyan line as the background
/// to indicate the maximum value. It has the total of 1.0 and a value of
/// 0.0, whilst being Horizontal.
pub fn new() -> Box<Self> {
Box::<ProgressBar<'_>>::default()
}
/// Sets the [`ProgressBar::total`] field.
pub fn total(mut self: Box<Self>, total: f64) -> Box<Self> {
self.total = total;
self
}
/// Sets the [`ProgressBar::value`] field.
pub fn value(mut self: Box<Self>, value: f64) -> Box<Self> {
self.value = value;
self
}
/// Sets the [`ProgressBar::fg`] field.
pub fn fg(mut self: Box<Self>, fg: Vec<StyledText<'a>>) -> Box<Self> {
self.fg = fg;
self
}
/// Sets the [`ProgressBar::bg`] field.
pub fn bg(mut self: Box<Self>, bg: Vec<StyledText<'a>>) -> Box<Self> {
self.bg = bg;
self
}
/// Sets the [`ProgressBar::edge`] field.
pub fn edge(mut self: Box<Self>, edge: StyledText<'a>) -> Box<Self> {
self.edge = edge;
self
}
/// Sets the [`ProgressBar::orientation`] field to [`Orientation::Vertical`]
pub fn vertical(mut self: Box<Self>) -> Box<Self> {
self.orientation = Orientation::Vertical;
self
}
}
impl Layout for ProgressBar<'_> {
fn width_for_height(&self, _width: usize) -> usize {
1
}
fn height_for_width(&self, _height: usize) -> usize {
1
}
fn prefered_size(&self) -> Vec2 {
Vec2::new(1, 1)
}
}
impl Widget for ProgressBar<'_> {
fn render<'a>(&self, rect: Rect, buffer: &mut Buffer) {
let value = self.value / self.total;
let fg_filler = Filler::new_texts(self.fg.clone()).can_cut(true);
let bg_filler = Filler::new_texts(self.bg.clone()).can_cut(true);
match self.orientation {
Orientation::Vertical => {
let abs_value = (rect.size.y as f64 * value) as usize;
fg_filler.render(
Rect::new(
rect.start.x,
rect.start.y,
rect.size.x,
abs_value,
),
buffer,
);
bg_filler.render(
Rect::new(
rect.start.x,
rect.start.y + abs_value + 1,
rect.size.x,
rect.size.y.saturating_sub(abs_value + 1),
),
buffer,
);
buffer.write_span(
Vec2::new(rect.start.x, rect.start.y + abs_value),
&self.edge,
);
}
Orientation::Horizontal => {
let abs_value = (rect.size.x as f64 * value) as usize;
fg_filler.render(
Rect::new(
rect.start.x,
rect.start.y,
abs_value,
rect.size.y,
),
buffer,
);
bg_filler.render(
Rect::new(
rect.start.x + abs_value + 1,
rect.start.y,
rect.size.x.saturating_sub(abs_value + 1),
rect.size.y,
),
buffer,
);
buffer.write_span(
Vec2::new(rect.start.x + abs_value, rect.start.y),
&self.edge,
);
}
}
}
}