Colorize the progress bar

This commit is contained in:
mo8it 2024-04-10 00:42:32 +02:00
parent c8d217ad50
commit 4a80bf6441
2 changed files with 79 additions and 23 deletions

View file

@ -7,7 +7,7 @@ use ratatui::{
Frame, Frame,
}; };
use crate::{exercise::Exercise, progress_bar::progress_bar, state_file::StateFile}; use crate::{exercise::Exercise, progress_bar::progress_bar_ratatui, state_file::StateFile};
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]
pub enum Filter { pub enum Filter {
@ -166,11 +166,11 @@ impl UiState {
); );
frame.render_widget( frame.render_widget(
Paragraph::new(Span::raw(progress_bar( Paragraph::new(progress_bar_ratatui(
self.progress, self.progress,
self.exercises.len() as u16, self.exercises.len() as u16,
area.width, area.width,
)?)) )?)
.block(Block::default().borders(Borders::BOTTOM)), .block(Block::default().borders(Borders::BOTTOM)),
Rect { Rect {
x: 0, x: 0,

View file

@ -1,41 +1,97 @@
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use ratatui::text::{Line, Span};
use std::fmt::Write; use std::fmt::Write;
const PREFIX: &str = "Progress: [";
const PREFIX_WIDTH: u16 = PREFIX.len() as u16;
// Leaving the last char empty (_) for `total` > 99.
const POSTFIX_WIDTH: u16 = "] xxx/xx exercises_".len() as u16;
const WRAPPER_WIDTH: u16 = PREFIX_WIDTH + POSTFIX_WIDTH;
const MIN_LINE_WIDTH: u16 = WRAPPER_WIDTH + 4;
const PROGRESS_EXCEEDS_MAX_ERR: &str =
"The progress of the progress bar is higher than the maximum";
pub fn progress_bar(progress: u16, total: u16, line_width: u16) -> Result<String> { pub fn progress_bar(progress: u16, total: u16, line_width: u16) -> Result<String> {
use crossterm::style::Stylize;
if progress > total { if progress > total {
bail!("The progress of the progress bar is higher than the maximum"); bail!(PROGRESS_EXCEEDS_MAX_ERR);
} }
// "Progress: [".len() == 11 if line_width < MIN_LINE_WIDTH {
// "] xxx/xx exercises_".len() == 19 (leaving the last char empty for `total` > 99)
// 11 + 19 = 30
let wrapper_width = 30;
// If the line width is too low for a progress bar, just show the ratio.
if line_width < wrapper_width + 4 {
return Ok(format!("Progress: {progress}/{total} exercises")); return Ok(format!("Progress: {progress}/{total} exercises"));
} }
let mut line = String::with_capacity(usize::from(line_width)); let mut line = String::with_capacity(usize::from(line_width));
line.push_str("Progress: ["); line.push_str(PREFIX);
let remaining_width = line_width.saturating_sub(wrapper_width); let width = line_width - WRAPPER_WIDTH;
let filled = (remaining_width * progress) / total; let filled = (width * progress) / total;
let mut green_part = String::with_capacity(usize::from(filled + 1));
for _ in 0..filled { for _ in 0..filled {
line.push('='); green_part.push('#');
} }
if filled < remaining_width { if filled < width {
line.push('>'); green_part.push('>');
}
write!(line, "{}", green_part.green()).unwrap();
let width_minus_filled = width - filled;
if width_minus_filled > 1 {
let red_part_width = width_minus_filled - 1;
let mut red_part = String::with_capacity(usize::from(red_part_width));
for _ in 0..red_part_width {
red_part.push('-');
}
write!(line, "{}", red_part.red()).unwrap();
} }
for _ in 0..(remaining_width - filled).saturating_sub(1) { write!(line, "] {progress:>3}/{total} exercises").unwrap();
line.push(' ');
}
line.write_fmt(format_args!("] {progress:>3}/{total} exercises"))
.unwrap();
Ok(line) Ok(line)
} }
pub fn progress_bar_ratatui(progress: u16, total: u16, line_width: u16) -> Result<Line<'static>> {
use ratatui::style::Stylize;
if progress > total {
bail!(PROGRESS_EXCEEDS_MAX_ERR);
}
if line_width < MIN_LINE_WIDTH {
return Ok(Line::raw(format!("Progress: {progress}/{total} exercises")));
}
let mut spans = Vec::with_capacity(4);
spans.push(Span::raw(PREFIX));
let width = line_width - WRAPPER_WIDTH;
let filled = (width * progress) / total;
let mut green_part = String::with_capacity(usize::from(filled + 1));
for _ in 0..filled {
green_part.push('#');
}
if filled < width {
green_part.push('>');
}
spans.push(green_part.green());
let width_minus_filled = width - filled;
if width_minus_filled > 1 {
let red_part_width = width_minus_filled - 1;
let mut red_part = String::with_capacity(usize::from(red_part_width));
for _ in 0..red_part_width {
red_part.push('-');
}
spans.push(red_part.red());
}
spans.push(Span::raw(format!("] {progress:>3}/{total} exercises")));
Ok(Line::from(spans))
}