use ratatui::{
layout::{Constraint, Flex, Layout, Rect},
style::{Modifier, Style},
text::{Line, Span},
widgets::{Block, Borders, Clear, Paragraph},
Frame,
};
use crate::app::ConfigPopupState;
use super::styles::{GraphStyle, Palette, Theme};
const ACTIVE_BULLET: &str = "●";
const INACTIVE_BULLET: &str = "○";
pub fn render_config_popup(
f: &mut Frame,
state: &ConfigPopupState,
graph_style: GraphStyle,
theme: Theme,
palette: &Palette,
) {
let popup_height = 2 + GraphStyle::ALL.len() + 1 + 1 + Theme::ALL.len() + 2 + 2;
let area = centered_rect(44, popup_height as u16, f.area());
f.render_widget(Clear, area);
let lines = build_lines(state, graph_style, theme, palette);
let block = Block::default()
.title(" Settings ")
.title_style(palette.title_style())
.borders(Borders::ALL)
.border_style(palette.border_style());
f.render_widget(Paragraph::new(lines).block(block), area);
}
fn build_lines<'a>(
state: &ConfigPopupState,
graph_style: GraphStyle,
theme: Theme,
palette: &Palette,
) -> Vec<Line<'a>> {
let section_style = palette.header_style();
let text_style = Style::new().fg(palette.foreground);
let cursor_style = Style::new().fg(palette.claude).add_modifier(Modifier::BOLD);
let dim_style = palette.dim_style();
let active_style = Style::new().fg(palette.codex).add_modifier(Modifier::BOLD);
let mut lines: Vec<Line> = Vec::new();
lines.push(Line::from(""));
let mut row_counter = 0usize;
lines.push(Line::from(vec![
Span::styled(" ", text_style),
Span::styled(ConfigPopupState::SECTIONS[0].0, section_style),
]));
for option in GraphStyle::ALL {
let is_cursor = row_counter == state.cursor;
let is_active = option == graph_style;
lines.push(option_line(
is_cursor,
is_active,
option.label(),
cursor_style,
active_style,
text_style,
dim_style,
));
row_counter += 1;
}
lines.push(Line::from(""));
lines.push(Line::from(vec![
Span::styled(" ", text_style),
Span::styled(ConfigPopupState::SECTIONS[1].0, section_style),
]));
for option in Theme::ALL {
let is_cursor = row_counter == state.cursor;
let is_active = option == theme;
lines.push(option_line(
is_cursor,
is_active,
option.label(),
cursor_style,
active_style,
text_style,
dim_style,
));
row_counter += 1;
}
lines.push(Line::from(""));
lines.push(Line::from(vec![
Span::styled(" ", text_style),
Span::styled("↑↓", cursor_style),
Span::styled(" Navigate ", dim_style),
Span::styled("Enter", cursor_style),
Span::styled(" Apply ", dim_style),
Span::styled("Esc/c", cursor_style),
Span::styled(" Close", dim_style),
]));
lines
}
#[allow(clippy::too_many_arguments)]
fn option_line<'a>(
is_cursor: bool,
is_active: bool,
label: &'a str,
cursor_style: Style,
active_style: Style,
text_style: Style,
dim_style: Style,
) -> Line<'a> {
let arrow = if is_cursor { "> " } else { " " };
let bullet = if is_active {
ACTIVE_BULLET
} else {
INACTIVE_BULLET
};
let bullet_style = if is_active { active_style } else { dim_style };
let label_style = if is_cursor { cursor_style } else { text_style };
Line::from(vec![
Span::styled(" ", text_style),
Span::styled(arrow, cursor_style),
Span::styled(bullet, bullet_style),
Span::styled(" ", text_style),
Span::styled(label, label_style),
])
}
fn centered_rect(width: u16, height: u16, area: Rect) -> Rect {
let vertical = Layout::vertical([Constraint::Length(height)])
.flex(Flex::Center)
.split(area);
Layout::horizontal([Constraint::Length(width)])
.flex(Flex::Center)
.split(vertical[0])[0]
}