mirror of
https://github.com/rust-lang/rustlings.git
synced 2024-12-27 00:00:03 +03:00
Colorize the progress bar
This commit is contained in:
parent
c8d217ad50
commit
4a80bf6441
|
@ -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,
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue