Files
mould-rs/src/ui.rs
Nils Pukropp 7aa12f1a31
All checks were successful
Version Check / check-version (pull_request) Successful in 3s
added theme configuration
2026-03-16 17:47:01 +01:00

177 lines
5.5 KiB
Rust

use crate::app::{App, Mode};
use crate::config::Config;
use ratatui::{
layout::{Constraint, Direction, Layout},
style::{Modifier, Style},
text::{Line, Span},
widgets::{Block, BorderType, Borders, List, ListItem, ListState, Paragraph},
Frame,
};
pub fn draw(f: &mut Frame, app: &mut App, config: &Config) {
let theme = &config.theme;
let size = f.area();
// Background
if !theme.transparent {
f.render_widget(Block::default().style(Style::default().bg(theme.crust())), size);
}
// Main layout with horizontal padding
let outer_layout = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Length(1), // Left padding
Constraint::Min(0), // Content
Constraint::Length(1), // Right padding
])
.split(size);
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(1), // Top padding
Constraint::Min(3), // List
Constraint::Length(3), // Input area
Constraint::Length(1), // Bottom padding
Constraint::Length(1), // Status bar
])
.split(outer_layout[1]);
let max_key_len = app
.vars
.iter()
.map(|v| v.key.len())
.max()
.unwrap_or(20)
.min(40);
// List
let items: Vec<ListItem> = app
.vars
.iter()
.enumerate()
.map(|(i, var)| {
let is_selected = i == app.selected;
let val = if is_selected && matches!(app.mode, Mode::Insert) {
app.input.value()
} else {
&var.value
};
let key_style = if is_selected {
Style::default().fg(theme.crust()).add_modifier(Modifier::BOLD)
} else {
Style::default().fg(theme.lavender())
};
let value_style = if is_selected {
Style::default().fg(theme.crust())
} else {
Style::default().fg(theme.text())
};
let line = Line::from(vec![
Span::styled(format!(" {:<width$} ", var.key, width = max_key_len), key_style),
Span::styled("", Style::default().fg(theme.surface1())),
Span::styled(format!(" {} ", val), value_style),
]);
let item_style = if is_selected {
Style::default().bg(theme.blue())
} else {
Style::default().fg(theme.text())
};
ListItem::new(line).style(item_style)
})
.collect();
let list = List::new(items)
.block(
Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.title(" Config Variables ")
.title_style(Style::default().fg(theme.mauve()).add_modifier(Modifier::BOLD))
.border_style(Style::default().fg(theme.surface1())),
);
let mut state = ListState::default();
state.select(Some(app.selected));
f.render_stateful_widget(list, chunks[1], &mut state);
// Input Area
let current_var = app.vars.get(app.selected);
let input_title = if let Some(var) = current_var {
if var.default_value.is_empty() {
format!(" Editing: {} ", var.key)
} else {
format!(" Editing: {} (Default: {}) ", var.key, var.default_value)
}
} else {
" Input ".to_string()
};
let input_border_color = match app.mode {
Mode::Insert => theme.green(),
Mode::Normal => theme.surface1(),
};
let input_text = app.input.value();
let cursor_pos = app.input.visual_cursor();
let input = Paragraph::new(input_text)
.style(Style::default().fg(theme.text()))
.block(
Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.title(input_title)
.title_style(Style::default().fg(theme.peach()).add_modifier(Modifier::BOLD))
.border_style(Style::default().fg(input_border_color)),
);
f.render_widget(input, chunks[2]);
if let Mode::Insert = app.mode {
f.set_cursor_position(ratatui::layout::Position::new(
chunks[2].x + 1 + cursor_pos as u16,
chunks[2].y + 1,
));
}
// Status bar
let (mode_str, mode_style) = match app.mode {
Mode::Normal => (
" NORMAL ",
Style::default()
.bg(theme.blue())
.fg(theme.crust())
.add_modifier(Modifier::BOLD),
),
Mode::Insert => (
" INSERT ",
Style::default()
.bg(theme.green())
.fg(theme.crust())
.add_modifier(Modifier::BOLD),
),
};
let status_msg = app.status_message.as_deref().unwrap_or_else(|| {
match app.mode {
Mode::Normal => " navigation | i: edit | :w: save | :q: quit ",
Mode::Insert => " Esc: back to normal | Enter: commit ",
}
});
let status_line = Line::from(vec![
Span::styled(mode_str, mode_style),
Span::styled(format!(" {} ", status_msg), Style::default().bg(theme.surface0()).fg(theme.text())),
]);
let status = Paragraph::new(status_line).style(Style::default().bg(theme.surface0()));
f.render_widget(status, chunks[4]);
}