From 9fc3f0b5d6b6fa25d31596b071834d021940f065 Mon Sep 17 00:00:00 2001 From: Edward Langley Date: Wed, 1 Apr 2026 01:01:19 -0700 Subject: [PATCH] refactor: synthesize previous refactors --- src/main.rs | 164 ++++++++++++++++++++++++++-------------------------- 1 file changed, 81 insertions(+), 83 deletions(-) diff --git a/src/main.rs b/src/main.rs index 58526be..edb9ed6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -277,11 +277,61 @@ fn run_tui( // ── Drawing ────────────────────────────────────────────────────────────────── +fn fill_line(left: String, right: &str, width: u16) -> String { + let pad = " ".repeat((width as usize).saturating_sub(left.len() + right.len())); + format!("{left}{pad}{right}") +} + +fn centered_popup(area: Rect, width: u16, height: u16) -> Rect { + let w = width.min(area.width); + let h = height.min(area.height); + let x = area.x + area.width.saturating_sub(w) / 2; + let y = area.y + area.height.saturating_sub(h) / 2; + Rect::new(x, y, w, h) +} + +fn draw_popup_frame(f: &mut Frame, popup: Rect, title: &str, border_color: Color) -> Rect { + f.render_widget(Clear, popup); + let block = Block::default() + .borders(Borders::ALL) + .border_style(Style::default().fg(border_color)) + .title(title); + let inner = block.inner(popup); + f.render_widget(block, popup); + inner +} + +fn mode_name(mode: &AppMode) -> &'static str { + match mode { + AppMode::Normal => "NORMAL", + AppMode::Editing { .. } => "INSERT", + AppMode::FormulaEdit { .. } => "FORMULA", + AppMode::FormulaPanel => "FORMULAS", + AppMode::CategoryPanel => "CATEGORIES", + AppMode::CategoryAdd { .. } => "NEW CATEGORY", + AppMode::ItemAdd { .. } => "ADD ITEMS", + AppMode::ViewPanel => "VIEWS", + AppMode::TileSelect { .. } => "TILES", + AppMode::ImportWizard => "IMPORT", + AppMode::ExportPrompt { .. } => "EXPORT", + AppMode::CommandMode { .. } => "COMMAND", + AppMode::Help => "HELP", + AppMode::Quit => "QUIT", + } +} + +fn mode_style(mode: &AppMode) -> Style { + match mode { + AppMode::Editing { .. } => Style::default().fg(Color::Black).bg(Color::Green), + AppMode::CommandMode { .. } => Style::default().fg(Color::Black).bg(Color::Yellow), + AppMode::TileSelect { .. } => Style::default().fg(Color::Black).bg(Color::Magenta), + _ => Style::default().fg(Color::Black).bg(Color::DarkGray), + } +} + fn draw(f: &mut Frame, app: &App) { let size = f.area(); - let is_cmd_mode = matches!(app.mode, AppMode::CommandMode { .. }); - let main_chunks = Layout::default() .direction(Direction::Vertical) .constraints([ @@ -295,12 +345,7 @@ fn draw(f: &mut Frame, app: &App) { draw_title(f, main_chunks[0], app); draw_content(f, main_chunks[1], app); draw_tile_bar(f, main_chunks[2], app); - - if is_cmd_mode { - draw_command_bar(f, main_chunks[3], app); - } else { - draw_status(f, main_chunks[3], app); - } + draw_bottom_bar(f, main_chunks[3], app); // Overlays (rendered last so they appear on top) if matches!(app.mode, AppMode::Help) { @@ -315,7 +360,7 @@ fn draw(f: &mut Frame, app: &App) { draw_export_prompt(f, size, app); } if app.is_empty_model() && matches!(app.mode, AppMode::Normal | AppMode::CommandMode { .. }) { - draw_welcome(f, main_chunks[1], app); + draw_welcome(f, main_chunks[1]); } } @@ -330,8 +375,7 @@ fn draw_title(f: &mut Frame, area: Rect, app: &App) { .unwrap_or_default(); let title = format!(" improvise · {}{}{} ", app.model.name, file, dirty); let right = " ?:help :q quit "; - let pad = " ".repeat((area.width as usize).saturating_sub(title.len() + right.len())); - let line = format!("{title}{pad}{right}"); + let line = fill_line(title, right, area.width); f.render_widget( Paragraph::new(line).style( Style::default() @@ -346,6 +390,7 @@ fn draw_title(f: &mut Frame, area: Rect, app: &App) { fn draw_content(f: &mut Frame, area: Rect, app: &App) { let side_open = app.formula_panel_open || app.category_panel_open || app.view_panel_open; + let grid_area; if side_open { let side_w = 32u16; let chunks = Layout::default() @@ -353,10 +398,7 @@ fn draw_content(f: &mut Frame, area: Rect, app: &App) { .constraints([Constraint::Min(40), Constraint::Length(side_w)]) .split(area); - f.render_widget( - GridWidget::new(&app.model, &app.mode, &app.search_query), - chunks[0], - ); + grid_area = chunks[0]; let side = chunks[1]; let panel_count = [ @@ -394,35 +436,27 @@ fn draw_content(f: &mut Frame, area: Rect, app: &App) { ); } } else { - f.render_widget( - GridWidget::new(&app.model, &app.mode, &app.search_query), - area, - ); + grid_area = area; } + + f.render_widget( + GridWidget::new(&app.model, &app.mode, &app.search_query), + grid_area, + ); } fn draw_tile_bar(f: &mut Frame, area: Rect, app: &App) { f.render_widget(TileBar::new(&app.model, &app.mode), area); } -fn draw_status(f: &mut Frame, area: Rect, app: &App) { - let mode_badge = match &app.mode { - AppMode::Normal => "NORMAL", - AppMode::Editing { .. } => "INSERT", - AppMode::FormulaEdit { .. } => "FORMULA", - AppMode::FormulaPanel => "FORMULAS", - AppMode::CategoryPanel => "CATEGORIES", - AppMode::CategoryAdd { .. } => "NEW CATEGORY", - AppMode::ItemAdd { .. } => "ADD ITEMS", - AppMode::ViewPanel => "VIEWS", - AppMode::TileSelect { .. } => "TILES", - AppMode::ImportWizard => "IMPORT", - AppMode::ExportPrompt { .. } => "EXPORT", - AppMode::CommandMode { .. } => "COMMAND", - AppMode::Help => "HELP", - AppMode::Quit => "QUIT", - }; +fn draw_bottom_bar(f: &mut Frame, area: Rect, app: &App) { + match app.mode { + AppMode::CommandMode { ref buffer } => draw_command_bar(f, area, buffer), + _ => draw_status(f, area, app), + } +} +fn draw_status(f: &mut Frame, area: Rect, app: &App) { let search_part = if app.search_mode { format!(" /{}▌", app.search_query) } else { @@ -438,30 +472,16 @@ fn draw_status(f: &mut Frame, area: Rect, app: &App) { let yank_indicator = if app.yanked.is_some() { " [yank]" } else { "" }; let view_badge = format!(" {}{} ", app.model.active_view, yank_indicator); - let left = format!(" {mode_badge}{search_part} {msg}"); - let right = view_badge; - let pad = " ".repeat((area.width as usize).saturating_sub(left.len() + right.len())); - let line = format!("{left}{pad}{right}"); + let left = format!(" {}{search_part} {msg}", mode_name(&app.mode)); + let line = fill_line(left, &view_badge, area.width); - let badge_style = match &app.mode { - AppMode::Editing { .. } => Style::default().fg(Color::Black).bg(Color::Green), - AppMode::CommandMode { .. } => Style::default().fg(Color::Black).bg(Color::Yellow), - AppMode::TileSelect { .. } => Style::default().fg(Color::Black).bg(Color::Magenta), - _ => Style::default().fg(Color::Black).bg(Color::DarkGray), - }; - - f.render_widget(Paragraph::new(line).style(badge_style), area); + f.render_widget(Paragraph::new(line).style(mode_style(&app.mode)), area); } -fn draw_command_bar(f: &mut Frame, area: Rect, app: &App) { - let buf = if let AppMode::CommandMode { buffer } = &app.mode { - buffer.as_str() - } else { - "" - }; - let line = format!(":{buf}▌"); +fn draw_command_bar(f: &mut Frame, area: Rect, buffer: &str) { f.render_widget( - Paragraph::new(line).style(Style::default().fg(Color::White).bg(Color::Black)), + Paragraph::new(format!(":{buffer}▌")) + .style(Style::default().fg(Color::White).bg(Color::Black)), area, ); } @@ -472,39 +492,17 @@ fn draw_export_prompt(f: &mut Frame, area: Rect, app: &App) { } else { "" }; - let popup_w = 64u16.min(area.width); - let x = area.x + area.width.saturating_sub(popup_w) / 2; - let y = area.y + area.height / 2; - let popup_area = Rect::new(x, y, popup_w, 3); - - f.render_widget(Clear, popup_area); - let block = Block::default() - .borders(Borders::ALL) - .border_style(Style::default().fg(Color::Yellow)) - .title(" Export CSV — path (Esc cancel) "); - let inner = block.inner(popup_area); - f.render_widget(block, popup_area); + let popup = centered_popup(area, 64, 3); + let inner = draw_popup_frame(f, popup, " Export CSV — path (Esc cancel) ", Color::Yellow); f.render_widget( Paragraph::new(format!("{buf}▌")).style(Style::default().fg(Color::Green)), inner, ); } -fn draw_welcome(f: &mut Frame, area: Rect, _app: &App) { - let w = 58u16.min(area.width.saturating_sub(4)); - let h = 20u16.min(area.height.saturating_sub(2)); - let x = area.x + area.width.saturating_sub(w) / 2; - let y = area.y + area.height.saturating_sub(h) / 2; - let popup = Rect::new(x, y, w, h); - - f.render_widget(Clear, popup); - - let block = Block::default() - .borders(Borders::ALL) - .border_style(Style::default().fg(Color::Blue)) - .title(" Welcome to improvise "); - let inner = block.inner(popup); - f.render_widget(block, popup); +fn draw_welcome(f: &mut Frame, area: Rect) { + let popup = centered_popup(area, 58, 20); + let inner = draw_popup_frame(f, popup, " Welcome to improvise ", Color::Blue); let lines: &[(&str, Style)] = &[ (