diff --git a/src/ui/mod.rs b/src/ui/mod.rs index affdb92..b95794d 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -1,6 +1,6 @@ use crate::state::{AppState, PlayerState}; use ratatui::{ - layout::{Constraint, Direction, Layout, Rect}, + layout::{Alignment, Constraint, Direction, Layout, Rect}, style::{Color, Style}, text::{Line, Span}, widgets::{Block, Borders, List, ListItem, Paragraph}, @@ -8,19 +8,26 @@ use ratatui::{ }; pub fn render(frame: &mut Frame, state: &AppState) { + // Three-section layout: title bar, main content, statusbar (like cm-dashboard) let main_chunks = Layout::default() .direction(Direction::Vertical) - .constraints([Constraint::Min(0), Constraint::Length(1)]) + .constraints([ + Constraint::Length(1), // Title bar + Constraint::Min(0), // Main content + Constraint::Length(1), // Status bar + ]) .split(frame.area()); - let top_chunks = Layout::default() + // Main content: left (files) | right (status + playlist) + let content_chunks = Layout::default() .direction(Direction::Horizontal) .constraints([Constraint::Percentage(50), Constraint::Percentage(50)]) - .split(main_chunks[0]); + .split(main_chunks[1]); - render_file_panel(frame, state, top_chunks[0]); - render_right_panel(frame, state, top_chunks[1]); - render_status_bar(frame, state, main_chunks[1]); + render_title_bar(frame, state, main_chunks[0]); + render_file_panel(frame, state, content_chunks[0]); + render_right_panel(frame, state, content_chunks[1]); + render_status_bar(frame, state, main_chunks[2]); } fn render_file_panel(frame: &mut Frame, state: &AppState, area: Rect) { @@ -65,17 +72,7 @@ fn render_right_panel(frame: &mut Frame, state: &AppState, area: Rect) { .constraints([Constraint::Length(3), Constraint::Min(0)]) .split(area); - // Combined status line: State | Progress | Volume - let state_text = if state.is_refreshing { - "Refreshing library..." - } else { - match state.player_state { - PlayerState::Stopped => "Stopped", - PlayerState::Playing => "Playing", - PlayerState::Paused => "Paused", - } - }; - + // Combined status line: Progress | Volume let progress_text = if state.current_duration > 0.0 { let position_mins = (state.current_position / 60.0) as u32; let position_secs = (state.current_position % 60.0) as u32; @@ -89,9 +86,9 @@ fn render_right_panel(frame: &mut Frame, state: &AppState, area: Rect) { "00:00/00:00".to_string() }; - let combined_status = format!("{} | {} | Vol: {}%", state_text, progress_text, state.volume); + let combined_status = format!("{} • Vol: {}%", progress_text, state.volume); let status_widget = Paragraph::new(combined_status) - .block(Block::default().borders(Borders::ALL).title("Status")) + .block(Block::default().borders(Borders::ALL).title("Player")) .style(Style::default().fg(Color::White)); frame.render_widget(status_widget, chunks[0]); @@ -133,30 +130,30 @@ fn render_right_panel(frame: &mut Frame, state: &AppState, area: Rect) { frame.render_widget(playlist_widget, chunks[1]); } -fn render_status_bar(frame: &mut Frame, _state: &AppState, area: Rect) { - let help = Line::from(vec![ - Span::styled("j/k", Style::default().fg(Color::Cyan)), - Span::raw(" Nav | "), - Span::styled("h/l", Style::default().fg(Color::Cyan)), - Span::raw(" Fold | "), - Span::styled("t", Style::default().fg(Color::Cyan)), - Span::raw(" Mark | "), - Span::styled("c", Style::default().fg(Color::Cyan)), - Span::raw(" Clear | "), - Span::styled("Enter", Style::default().fg(Color::Cyan)), - Span::raw(" Play | "), - Span::styled("Space", Style::default().fg(Color::Cyan)), - Span::raw(" Pause | "), - Span::styled("n/p", Style::default().fg(Color::Cyan)), - Span::raw(" Next/Prev | "), - Span::styled("r", Style::default().fg(Color::Cyan)), - Span::raw(" Rescan | "), - Span::styled("q", Style::default().fg(Color::Cyan)), - Span::raw(" Quit"), - ]); +fn render_title_bar(frame: &mut Frame, state: &AppState, area: Rect) { + let status_text = if state.is_refreshing { + "Refreshing library..." + } else { + match state.player_state { + PlayerState::Stopped => "Stopped", + PlayerState::Playing => "Playing", + PlayerState::Paused => "Paused", + } + }; - let status_bar = Paragraph::new(help) - .style(Style::default().fg(Color::White).bg(Color::DarkGray)); + let title_text = format!("cm-player • {}", status_text); + let title = Paragraph::new(title_text) + .style(Style::default().fg(Color::Black).bg(Color::Cyan)); + + frame.render_widget(title, area); +} + +fn render_status_bar(frame: &mut Frame, _state: &AppState, area: Rect) { + let shortcuts = "↑↓/jk: Navigate • h/l: Fold • t: Mark • c: Clear • Enter: Play • Space: Pause • n/p: Next/Prev • r: Rescan • q: Quit"; + + let status_bar = Paragraph::new(shortcuts) + .style(Style::default().fg(Color::White)) + .alignment(Alignment::Center); frame.render_widget(status_bar, area); }