use ratatui::{
buffer::Buffer,
layout::{Alignment, Rect},
style::{Color, Style},
text::Line,
widgets::{Block, Gauge, Paragraph, Widget},
};
use std::time::Instant;
#[derive(Clone, Debug, Default, PartialEq)]
pub enum StatusMode {
#[default]
Blank,
Messages,
Progress,
}
#[derive(Clone, Debug, Default)]
pub struct StatusBar<'a> {
mode: StatusMode,
message: String,
progress: u8,
start_time: Option<Instant>,
block: Block<'a>,
}
impl<'a> StatusBar<'a> {
pub fn new() -> Self {
Self::default()
}
pub fn set_blank(&mut self) {
self.mode = StatusMode::Blank;
self.message.clear();
self.progress = 0;
self.start_time = None;
}
pub fn set_messages(&mut self, message: String) {
self.mode = StatusMode::Messages;
self.message = message;
self.progress = 0;
self.start_time = Some(Instant::now());
}
pub fn set_progress(&mut self, message: String) {
self.mode = StatusMode::Progress;
self.message = message;
self.progress = 0;
self.start_time = Some(Instant::now());
}
pub fn set_block(&mut self, block: Block<'a>) {
self.block = block;
}
pub fn update_message(&mut self, message: String) {
if self.mode == StatusMode::Messages {
self.message = message;
}
}
pub fn update_progress(&mut self, message: Option<String>, progress: u8) {
if self.mode == StatusMode::Progress {
if let Some(msg) = message {
self.message = msg;
}
self.progress = progress.min(100);
}
}
fn get_throbber_char(&self) -> char {
if let Some(start_time) = self.start_time {
let elapsed = start_time.elapsed();
let frame = (elapsed.as_millis() / 100) % 10; match frame {
0 => '⠋',
1 => '⠙',
2 => '⠹',
3 => '⠸',
4 => '⠼',
5 => '⠴',
6 => '⠦',
7 => '⠧',
8 => '⠇',
9 => '⠏',
_ => '⠋',
}
} else {
'⠋'
}
}
}
impl Widget for &mut StatusBar<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
match self.mode {
StatusMode::Blank => {
let paragraph = Paragraph::new(Line::from("")).block(self.block.clone());
Widget::render(paragraph, area, buf);
}
StatusMode::Messages => {
let throbber = self.get_throbber_char();
let content = format!("{} {}", throbber, self.message);
let paragraph = Paragraph::new(Line::from(content))
.block(self.block.clone())
.style(Style::default().fg(Color::Yellow))
.alignment(Alignment::Left);
Widget::render(paragraph, area, buf);
}
StatusMode::Progress => {
let throbber = self.get_throbber_char();
let label = format!("{} {}", throbber, self.message);
let gauge = Gauge::default()
.block(self.block.clone())
.gauge_style(Style::default().fg(Color::Green))
.percent(self.progress as u16)
.label(label);
Widget::render(gauge, area, buf);
}
}
}
}