use ratatui::layout::{Alignment, Constraint, Layout, Rect}; use ratatui::style::Style; use ratatui::text::{Line, Span}; use ratatui::widgets::{Cell, Paragraph, Row, Table}; use ratatui::Frame; use tui_big_text::{BigText, PixelSize}; use crate::state::ui::UiState; use crate::theme; pub fn render(frame: &mut Frame, area: Rect, ui: &UiState, plugin_mode: bool) { let theme = theme::get(); frame.render_widget(&ui.sparkles, area); let author_style = Style::new().fg(theme.title.author); let link_style = Style::new().fg(theme.title.link); let license_style = Style::new().fg(theme.title.license); let big_title = BigText::builder() .pixel_size(PixelSize::Full) .style(Style::new().fg(theme.title.big_title).bold()) .lines(vec!["CAGIRE".into()]) .centered() .build(); let info_lines = vec![ Line::from(""), Line::from(vec![ Span::styled("A Forth Music Sequencer by ", Style::new().fg(theme.title.subtitle)), Span::styled("BuboBubo", author_style), ]), Line::from(Span::styled( format!("v{}", env!("CARGO_PKG_VERSION")), Style::new().fg(theme.title.subtitle), )), Line::from(""), Line::from(Span::styled("https://raphaelforment.fr", link_style)), Line::from(""), Line::from(Span::styled("AGPL-3.0", license_style)), ]; let mut keybindings = vec![ ("Ctrl+Arrows", "Navigate Views"), ("Enter", "Edit Step"), ("Space", "Play/Stop"), ("s", "Save"), ("l", "Load"), ]; if !plugin_mode { keybindings.push(("q", "Quit")); } keybindings.push(("?", "Keybindings")); let key_style = Style::new().fg(theme.modal.confirm); let desc_style = Style::new().fg(theme.ui.text_primary); let rows: Vec = keybindings .iter() .enumerate() .map(|(i, (key, desc))| { let bg = if i % 2 == 0 { theme.table.row_even } else { theme.table.row_odd }; Row::new(vec![ Cell::from(*key).style(key_style), Cell::from(*desc).style(desc_style), ]) .style(Style::new().bg(bg)) }) .collect(); let table = Table::new( rows, [Constraint::Length(14), Constraint::Fill(1)], ) .column_spacing(2); let press_line = Line::from(Span::styled( "Press any key to continue", Style::new().fg(theme.title.subtitle), )); let info_height = info_lines.len() as u16; let table_height = keybindings.len() as u16; let table_width: u16 = 42; let big_text_height: u16 = 8; let content_height = info_height + table_height + 3; // +3 for gap + empty line + press line let show_big_title = area.height >= (big_text_height + content_height) && area.width >= 30; let total_height = if show_big_title { big_text_height + content_height } else { content_height }; let v_pad = area.height.saturating_sub(total_height) / 2; let mut constraints = Vec::new(); constraints.push(Constraint::Length(v_pad)); if show_big_title { constraints.push(Constraint::Length(big_text_height)); } constraints.push(Constraint::Length(info_height)); constraints.push(Constraint::Length(1)); // gap constraints.push(Constraint::Length(table_height)); constraints.push(Constraint::Length(1)); // empty line constraints.push(Constraint::Length(1)); // press any key constraints.push(Constraint::Fill(1)); let areas = Layout::vertical(constraints).split(area); let mut idx = 1; // skip padding if show_big_title { frame.render_widget(big_title, areas[idx]); idx += 1; } let info = Paragraph::new(info_lines).alignment(Alignment::Center); frame.render_widget(info, areas[idx]); idx += 1; idx += 1; // skip gap let tw = table_width.min(areas[idx].width); let tx = areas[idx].x + (areas[idx].width.saturating_sub(tw)) / 2; let table_area = Rect::new(tx, areas[idx].y, tw, areas[idx].height); frame.render_widget(table, table_area); idx += 2; // skip empty line let press = Paragraph::new(press_line).alignment(Alignment::Center); frame.render_widget(press, areas[idx]); }