src/ui/widgets/progress_bar.rs (2711B)
1 use ratatui::layout::{Constraint, Direction, Layout, Rect};
2 use ratatui::style::{Color, Style};
3 use ratatui::text::{Line, Span};
4 use ratatui::widgets::{Block, Borders, Paragraph, Gauge};
5 use ratatui::Frame;
6
7 use crate::app::App;
8 use crate::app::state::PlayState;
9 use crate::ui::theme::Theme;
10 use crate::ui::text::format_time;
11
12 pub fn render_progress_bar(frame: &mut Frame, area: Rect, app: &App, theme: &Theme) {
13 let block = Block::default()
14 .borders(Borders::ALL)
15 .border_style(Style::default().fg(theme.border_unfocused));
16
17 let inner = block.inner(area);
18 frame.render_widget(block, area);
19
20 let cols = Layout::default()
21 .direction(Direction::Horizontal)
22 .constraints([
23 Constraint::Length(3), // Play icon
24 Constraint::Min(10), // Gauge bar
25 Constraint::Length(13), // Time display
26 ])
27 .split(inner);
28
29 // Play icon
30 let icon = match app.playback.state {
31 PlayState::Playing => "\u{F04B}", // nf-fa-play
32 PlayState::Paused => "\u{F04C}", // nf-fa-pause
33 PlayState::Stopped => "\u{F04D}", // nf-fa-stop
34 };
35 let icon_color = match app.playback.state {
36 PlayState::Playing => Color::Rgb(80, 255, 120),
37 PlayState::Paused => Color::Rgb(255, 200, 80),
38 PlayState::Stopped => Color::Rgb(255, 100, 100),
39 };
40 let icon_widget = Paragraph::new(Line::from(Span::styled(
41 format!(" {}", icon),
42 Style::default().fg(icon_color),
43 )));
44 frame.render_widget(icon_widget, cols[0]);
45
46 // Gauge
47 let ratio = if app.playback.duration_secs > 0.0 {
48 (app.playback.position_secs / app.playback.duration_secs).clamp(0.0, 1.0)
49 } else {
50 0.0
51 };
52
53 let gauge = Gauge::default()
54 .ratio(ratio)
55 .label("")
56 .gauge_style(Style::default().fg(theme.progress_filled).bg(theme.progress_empty));
57 frame.render_widget(gauge, cols[1]);
58
59 // Time
60 let pos = format_time(app.playback.position_secs);
61 let dur = format_time(app.playback.duration_secs);
62 let time_widget = Paragraph::new(Line::from(Span::styled(
63 format!(" {} / {}", pos, dur),
64 Style::default().fg(Color::White),
65 )));
66 frame.render_widget(time_widget, cols[2]);
67 }
68
69 /// Returns the gauge area for mouse click seeking
70 pub fn progress_gauge_area(area: Rect) -> Rect {
71 let block = Block::default().borders(Borders::ALL);
72 let inner = block.inner(area);
73
74 let cols = Layout::default()
75 .direction(Direction::Horizontal)
76 .constraints([
77 Constraint::Length(3),
78 Constraint::Min(10),
79 Constraint::Length(13),
80 ])
81 .split(inner);
82
83 cols[1]
84 }
85