aboutsummaryrefslogtreecommitdiff
path: root/src/ui/menus.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/menus.rs')
-rw-r--r--src/ui/menus.rs210
1 files changed, 210 insertions, 0 deletions
diff --git a/src/ui/menus.rs b/src/ui/menus.rs
new file mode 100644
index 00000000..537ac9a1
--- /dev/null
+++ b/src/ui/menus.rs
@@ -0,0 +1,210 @@
+use ratatui::{
+ layout::{Alignment, Rect},
+ style::{Modifier, Style},
+ text::{Line, Span},
+ widgets::{Clear, List, ListItem, ListState, Paragraph},
+ Frame,
+};
+
+use super::widgets::{panel_contrast_fg, render_panel_shell};
+use crate::app::AppState;
+
+fn render_bottom_bar(frame: &mut Frame, area: Rect, line: Line<'_>, bg: ratatui::style::Color) {
+ frame.render_widget(Clear, area);
+ let buf = frame.buffer_mut();
+ for x in area.x..area.x + area.width {
+ buf[(x, area.y)].set_style(Style::default().bg(bg));
+ }
+ frame.render_widget(Paragraph::new(line), area);
+}
+
+pub(super) fn render_navigate_overlay(app: &AppState, frame: &mut Frame, area: Rect) {
+ let key = Style::default()
+ .fg(app.palette.accent)
+ .add_modifier(Modifier::BOLD);
+ let dim = Style::default().fg(app.palette.overlay0);
+
+ let mode_style = Style::default()
+ .fg(panel_contrast_fg(&app.palette))
+ .bg(app.palette.accent)
+ .add_modifier(Modifier::BOLD);
+
+ let kb = &app.keybinds;
+ let line = Line::from(vec![
+ Span::styled(" NAVIGATE ", mode_style),
+ Span::raw(" "),
+ Span::styled("esc", key),
+ Span::styled(" back ", dim),
+ Span::styled("↑↓", key),
+ Span::styled(" ws ", dim),
+ Span::styled("⇥", key),
+ Span::styled(" pane ", dim),
+ Span::styled(kb.new_tab_label.as_str(), key),
+ Span::styled(" new tab ", dim),
+ Span::styled(kb.split_vertical_label.as_str(), key),
+ Span::styled(" split│ ", dim),
+ Span::styled(kb.split_horizontal_label.as_str(), key),
+ Span::styled(" split─ ", dim),
+ Span::styled(kb.close_pane_label.as_str(), key),
+ Span::styled(" close ", dim),
+ Span::styled(kb.fullscreen_label.as_str(), key),
+ Span::styled(" full ", dim),
+ Span::styled(kb.resize_mode_label.as_str(), key),
+ Span::styled(" resize ", dim),
+ Span::styled("?", key),
+ Span::styled(" keybinds ", dim),
+ Span::styled("s", key),
+ Span::styled(" settings ", dim),
+ Span::styled("q", key),
+ Span::styled(" quit", dim),
+ ]);
+
+ let overlay_y = area.y + area.height.saturating_sub(1);
+ let overlay_area = Rect::new(area.x, overlay_y, area.width, 1);
+ render_bottom_bar(frame, overlay_area, line, app.palette.panel_bg);
+
+ if app.update_available.is_some() {
+ let status = Line::from(vec![Span::styled(
+ " update ready",
+ Style::default()
+ .fg(app.palette.accent)
+ .add_modifier(Modifier::BOLD),
+ )]);
+ let width = 13u16.min(overlay_area.width);
+ let status_area = Rect::new(
+ overlay_area.x + overlay_area.width.saturating_sub(width),
+ overlay_area.y,
+ width,
+ overlay_area.height,
+ );
+ frame.render_widget(Clear, status_area);
+ frame.render_widget(
+ Paragraph::new(status).alignment(Alignment::Right),
+ status_area,
+ );
+ }
+}
+
+pub(super) fn render_global_launcher_menu(app: &AppState, frame: &mut Frame) {
+ let rect = app.global_menu_rect();
+ let Some(inner) = render_panel_shell(frame, rect, app.palette.accent, app.palette.panel_bg)
+ else {
+ return;
+ };
+
+ let items = app.global_menu_labels();
+ for (idx, item) in items.iter().enumerate() {
+ let y = inner.y + idx as u16;
+ if y >= inner.y + inner.height {
+ break;
+ }
+ let selected = idx == app.global_menu.highlighted;
+ let rect = Rect::new(inner.x, y, inner.width, 1);
+
+ if *item == "update ready" {
+ let line = if selected {
+ Line::from(vec![
+ Span::styled(
+ " update ready ",
+ Style::default()
+ .fg(panel_contrast_fg(&app.palette))
+ .bg(app.palette.accent)
+ .add_modifier(Modifier::BOLD),
+ ),
+ Span::styled(
+ "● ",
+ Style::default()
+ .fg(panel_contrast_fg(&app.palette))
+ .bg(app.palette.accent)
+ .add_modifier(Modifier::BOLD),
+ ),
+ ])
+ } else {
+ Line::from(vec![
+ Span::styled(" update ready ", Style::default().fg(app.palette.text)),
+ Span::styled(
+ "● ",
+ Style::default()
+ .fg(app.palette.accent)
+ .add_modifier(Modifier::BOLD),
+ ),
+ ])
+ };
+ frame.render_widget(Paragraph::new(line).alignment(Alignment::Left), rect);
+ continue;
+ }
+
+ let style = if selected {
+ Style::default()
+ .fg(panel_contrast_fg(&app.palette))
+ .bg(app.palette.accent)
+ .add_modifier(Modifier::BOLD)
+ } else {
+ Style::default().fg(app.palette.text)
+ };
+ frame.render_widget(
+ Paragraph::new(format!(" {item} "))
+ .style(style)
+ .alignment(Alignment::Left),
+ rect,
+ );
+ }
+}
+
+pub(super) fn render_resize_overlay(app: &AppState, frame: &mut Frame, area: Rect) {
+ let key = Style::default()
+ .fg(app.palette.accent)
+ .add_modifier(Modifier::BOLD);
+ let dim = Style::default().fg(app.palette.overlay0);
+
+ let mode_style = Style::default()
+ .fg(panel_contrast_fg(&app.palette))
+ .bg(app.palette.mauve)
+ .add_modifier(Modifier::BOLD);
+
+ let line = Line::from(vec![
+ Span::styled(" RESIZE ", mode_style),
+ Span::raw(" "),
+ Span::styled("h/l", key),
+ Span::styled(" width ", dim),
+ Span::styled("j/k", key),
+ Span::styled(" height ", dim),
+ Span::styled("esc", key),
+ Span::styled(" done", dim),
+ ]);
+
+ let overlay_y = area.y + area.height.saturating_sub(1);
+ let overlay_area = Rect::new(area.x, overlay_y, area.width, 1);
+ render_bottom_bar(frame, overlay_area, line, app.palette.panel_bg);
+}
+
+pub(super) fn render_context_menu(app: &AppState, frame: &mut Frame) {
+ let Some(menu) = &app.context_menu else {
+ return;
+ };
+
+ let p = &app.palette;
+ let Some(menu_rect) = app.context_menu_rect() else {
+ return;
+ };
+ let Some(inner) = render_panel_shell(frame, menu_rect, p.accent, p.panel_bg) else {
+ return;
+ };
+
+ let items: Vec<ListItem> = menu
+ .items()
+ .iter()
+ .map(|item| ListItem::new(Line::from(*item)))
+ .collect();
+ let list = List::new(items)
+ .style(Style::default().fg(p.text))
+ .highlight_style(
+ Style::default()
+ .bg(p.accent)
+ .fg(panel_contrast_fg(p))
+ .add_modifier(Modifier::BOLD),
+ )
+ .highlight_symbol(" ");
+ let mut state = ListState::default().with_selected(Some(menu.list.highlighted));
+ frame.render_stateful_widget(list, inner, &mut state);
+}