Basic page layout, help, and configuration

This commit is contained in:
Ian Keane 2025-11-16 10:19:14 -05:00
parent 2f0cc79631
commit ead09c4a80
5 changed files with 301 additions and 53 deletions

149
src/ui.rs
View file

@ -1,17 +1,25 @@
use ratatui::{
layout::{Constraint, Direction, Layout, Rect},
layout::{Constraint, Direction, Layout, Rect, Alignment},
style::{Color, Modifier, Style},
text::{Line, Span},
widgets::{Block, Borders, List, ListItem, Paragraph},
widgets::{Block, Borders, List, ListItem, Paragraph, Clear},
Frame,
};
use crate::state::{AppState, TimeItem};
use crate::{state::{AppState, TimeItem}, app::{App, Screen}};
const ACTIVE_COLOR: Color = Color::Green;
const INACTIVE_COLOR: Color = Color::Yellow;
pub fn render(frame: &mut Frame, state: &AppState) {
pub fn render(frame: &mut Frame, app: &App) {
match app.current_screen {
Screen::Main => render_main(frame, app),
Screen::Help => render_help(frame),
Screen::ConfigHelp => render_config_help(frame),
}
}
fn render_main(frame: &mut Frame, app: &App) {
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
@ -25,31 +33,46 @@ pub fn render(frame: &mut Frame, state: &AppState) {
frame,
chunks[0],
"Permanent Items",
&state.permanent_items,
state.current_pane == 0,
state.selected_indices[0],
state,
&app.state.permanent_items,
app.state.current_pane == 0,
app.state.selected_indices[0],
&app.state,
);
render_section(
frame,
chunks[1],
"Recurring Items",
&state.recurring_items,
state.current_pane == 1,
state.selected_indices[1],
state,
&app.state.recurring_items,
app.state.current_pane == 1,
app.state.selected_indices[1],
&app.state,
);
render_section(
frame,
chunks[2],
"Recent Items",
&state.recent_items,
state.current_pane == 2,
state.selected_indices[2],
state,
&app.state.recent_items,
app.state.current_pane == 2,
app.state.selected_indices[2],
&app.state,
);
if app.config.show_help_hint {
let help_hint = Paragraph::new("(?) for help")
.alignment(Alignment::Right)
.style(Style::default().fg(Color::DarkGray));
let help_area = Rect {
x: frame.size().width.saturating_sub(12),
y: frame.size().height.saturating_sub(1),
width: 12,
height: 1,
};
frame.render_widget(help_hint, help_area);
}
}
fn render_section(
@ -99,4 +122,98 @@ fn render_section(
let list = List::new(items).block(block);
frame.render_widget(list, area);
}
fn render_help(frame: &mut Frame) {
let area = centered_rect(60, 20, frame.size());
frame.render_widget(Clear, area);
let help_text = vec![
"WAT - Watson Time Tracker Interface",
"",
"This tool helps you track time using Watson with a convenient interface.",
"",
"The interface is divided into three sections:",
"1. Permanent Items: Configured tasks that are always available",
"2. Recurring Items: Frequently used tasks that you might return to",
"3. Recent Items: One-off tasks, showing the last 20 used",
"",
"Navigation:",
"- Use j/k or ↑/↓ to move selection up/down",
"- Use h/l or ←/→ to switch between panes",
"- Press Enter to start/stop time tracking",
"",
"Help Pages:",
"c - Configuration help",
"",
"Main Commands:",
"j/k, arrows - Navigate",
"h/l, arrows - Switch panes",
"Enter - Start/stop timer",
"Ctrl+e - Edit tasks config",
"c - Edit app config",
"q - Quit",
"? (or ESC) - Exit help",
];
let text = help_text.join("\n");
let block = Block::default()
.title("Help")
.borders(Borders::ALL)
.style(Style::default().fg(Color::White));
let paragraph = Paragraph::new(text)
.block(block)
.style(Style::default().fg(Color::White));
frame.render_widget(paragraph, area);
}
fn render_config_help(frame: &mut Frame) {
let area = centered_rect(60, 15, frame.size());
frame.render_widget(Clear, area);
let help_text = vec![
"WAT Configuration",
"",
"The configuration file is in YAML format and supports these options:",
"",
"show_help_hint: true/false",
" Controls visibility of the help hint in the main interface",
"",
"Example configuration:",
"show_help_hint: true",
"",
"",
"Commands:",
"q (or ESC) - Return to main help",
];
let text = help_text.join("\n");
let block = Block::default()
.title("Configuration Help")
.borders(Borders::ALL)
.style(Style::default().fg(Color::White));
let paragraph = Paragraph::new(text)
.block(block)
.style(Style::default().fg(Color::White));
frame.render_widget(paragraph, area);
}
fn centered_rect(width: u16, height: u16, r: Rect) -> Rect {
let x = (r.width.saturating_sub(width)) / 2;
let y = (r.height.saturating_sub(height)) / 2;
Rect {
x: r.x + x,
y: r.y + y,
width: width.min(r.width),
height: height.min(r.height),
}
}