use ratatui::{
layout::{Constraint, Layout, Rect},
style::{Color, Modifier, Style},
widgets::{
Block, Borders, Clear, Gauge, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState,
Tabs, Wrap,
},
Frame,
};
pub fn draw_simple_progress_bar_with_text(progress: f64, text: &str) {
let bar_length = 50;
let filled_length = (progress * bar_length as f64) as usize;
let empty_length = bar_length - filled_length;
let filled = "█".repeat(filled_length);
let empty = "░".repeat(empty_length);
let percentage = (progress * 100.0) as usize;
if text.is_empty() {
eprint!("\r[{}] {}%", filled + &empty, percentage);
} else {
eprint!("\r[{}] {}% | {}", filled + &empty, percentage, text);
}
}
pub fn hide_progress_bar() {
eprint!("\r{}", " ".repeat(100)); eprintln!(); }
pub fn draw_tui_progress_bar(frame: &mut Frame, area: Rect, progress: f64, title: &str) {
let progress_widget = Gauge::default()
.block(
Block::default()
.title(title)
.borders(Borders::ALL)
.border_type(ratatui::widgets::BorderType::Rounded),
)
.gauge_style(Style::default().fg(Color::Green).bg(Color::DarkGray))
.ratio(progress);
frame.render_widget(progress_widget, area);
}
pub struct TuiManager;
pub const HEADER_STYLE: Style = Style::new()
.fg(Color::Rgb(144, 238, 144)) .add_modifier(Modifier::BOLD);
pub const SELECTED_ITEM_STYLE: Style = Style::new()
.fg(Color::White)
.bg(Color::Rgb(0, 95, 135)) .add_modifier(Modifier::BOLD);
pub const PANEL_TITLE_STYLE: Style = Style::new()
.fg(Color::Rgb(144, 238, 144)) .add_modifier(Modifier::BOLD);
pub const TEXT_FG_COLOR: Color = Color::Rgb(158, 158, 158);
impl Default for TuiManager {
fn default() -> Self {
Self::new()
}
}
impl TuiManager {
pub fn new() -> Self {
TuiManager
}
pub fn draw_tabs<'a>(&self, tabs: Vec<String>, selected: usize, title: &'a str) -> Tabs<'a> {
Tabs::new(tabs)
.select(selected)
.block(Block::default().borders(Borders::ALL).title(title))
.highlight_style(Style::default().fg(Color::Yellow))
.divider("|")
}
pub fn draw_modal(&self, frame: &mut Frame, message: &str) {
let area = frame.area();
let popup_area = self.popup_area(area, 40, 20);
frame.render_widget(Clear, popup_area);
let chunks = Layout::vertical([
Constraint::Length(3), Constraint::Length(1), Constraint::Length(3), Constraint::Length(1), Constraint::Min(0), ])
.spacing(0)
.split(popup_area);
let block = Block::default()
.title("Success")
.borders(Borders::ALL)
.border_type(ratatui::widgets::BorderType::Rounded)
.style(Style::default().bg(Color::Rgb(28, 28, 28)).fg(Color::White));
frame.render_widget(block, popup_area);
let lines: Vec<&str> = message.split('\n').collect();
let icon = "✓";
let message = format!("{} {}", icon, lines[0]);
let paragraph = Paragraph::new(message)
.style(
Style::default()
.fg(Color::Rgb(144, 238, 144))
.add_modifier(Modifier::BOLD),
)
.alignment(ratatui::layout::Alignment::Center)
.wrap(Wrap { trim: true });
frame.render_widget(paragraph, chunks[2]);
if lines.len() > 1 {
let submessage = Paragraph::new(lines[1])
.style(Style::default().fg(Color::Rgb(200, 200, 200)))
.alignment(ratatui::layout::Alignment::Center)
.wrap(Wrap { trim: true });
frame.render_widget(submessage, chunks[4]);
}
}
fn popup_area(&self, area: Rect, percent_x: u16, percent_y: u16) -> Rect {
let popup_width = (area.width as f32 * (percent_x as f32 / 100.0)) as u16;
let popup_height = (area.height as f32 * (percent_y as f32 / 100.0)) as u16;
let x = area.x + (area.width.saturating_sub(popup_width)) / 2;
let y = area.y + (area.height.saturating_sub(popup_height)) / 2;
Rect::new(x, y, popup_width, popup_height)
}
pub fn draw_scrollbar(
&self,
count: usize,
selected_index: usize,
frame: &mut Frame,
rect: Rect,
) {
let mut scrollbar_state = ScrollbarState::default()
.content_length(count)
.position(selected_index);
frame.render_stateful_widget(
Scrollbar::new(ScrollbarOrientation::VerticalRight)
.begin_symbol(Some("↑"))
.end_symbol(Some("↓")),
rect,
&mut scrollbar_state,
);
}
}