use ratatui::{
layout::Rect,
style::{Modifier, Style},
text::{Line, Span},
widgets::Paragraph,
Frame,
};
use crate::app::{App, FocusPanel, InputMode};
use crate::models::Filter;
use super::theme::{self, icons};
pub fn render_status_bar(frame: &mut Frame, app: &App, area: Rect) {
let content = match app.input_mode {
InputMode::Normal => render_normal_mode_hints(app),
InputMode::Editing => render_editing_mode_hints(),
InputMode::Search => render_search_mode_hints(),
};
let status_bar = Paragraph::new(content);
frame.render_widget(status_bar, area);
}
fn key_hint(key: &str, label: &str) -> Vec<Span<'static>> {
vec![
Span::styled(
format!(" {}", key),
Style::default()
.fg(theme::ACCENT)
.add_modifier(Modifier::BOLD),
),
Span::styled(format!(" {} ", label), Style::default().fg(theme::TEXT_MUTED)),
]
}
fn render_normal_mode_hints(app: &App) -> Line<'static> {
if let Some(ref msg) = app.status_message {
return Line::from(vec![
Span::styled(
format!(" {} ", icons::CHECK),
Style::default().fg(theme::SUCCESS),
),
Span::styled(
format!("{} ", msg),
Style::default().fg(theme::SUCCESS),
),
]);
}
match app.focus {
FocusPanel::Sidebar => render_sidebar_hints(app),
FocusPanel::TaskList => render_tasklist_hints(app),
}
}
fn render_sidebar_hints(_app: &App) -> Line<'static> {
let mut spans = Vec::new();
spans.extend(key_hint("a", "New"));
spans.extend(key_hint("e", "Edit"));
spans.extend(key_hint("d", "Delete"));
spans.extend(key_hint("c", "Calendar"));
spans.extend(key_hint("Tab", "Tasks"));
spans.extend(key_hint("?", "Help"));
Line::from(spans)
}
fn render_tasklist_hints(app: &App) -> Line<'static> {
let mut spans = Vec::new();
spans.extend(key_hint("a", "Add"));
spans.extend(key_hint("e", "Edit"));
spans.extend(key_hint("d", "Delete"));
spans.extend(key_hint("Space", "Done"));
spans.extend(key_hint("/", "Search"));
spans.extend(key_hint("f", "Filter"));
spans.extend(key_hint("?", "Help"));
let filter_name = filter_display_name(&app.filter);
if !filter_name.is_empty() {
spans.push(Span::styled(
format!(" {} ", icons::LINE_VERTICAL),
Style::default().fg(theme::BORDER),
));
spans.push(Span::styled(
format!("{} {}", icons::BULLET, filter_name),
Style::default().fg(theme::INFO),
));
}
Line::from(spans)
}
fn filter_display_name(filter: &Filter) -> String {
match filter {
Filter::Pending => String::new(), Filter::All => "All".to_string(),
Filter::Completed => "Completed".to_string(),
Filter::InProgress => "In Progress".to_string(),
Filter::Archived => "Archived".to_string(),
Filter::DueToday => "Due Today".to_string(),
Filter::DueThisWeek => "Due This Week".to_string(),
Filter::Overdue => "Overdue".to_string(),
Filter::ByProject(id) => format!("Project: {}", id),
Filter::ByTag(tag) => format!("Tag: {}", tag),
Filter::ByPriority(p) => format!("Priority: {:?}", p),
}
}
fn render_editing_mode_hints() -> Line<'static> {
let mut spans = vec![
Span::styled(
format!(" {} Editing ", icons::BULLET),
Style::default()
.fg(theme::WARNING)
.add_modifier(Modifier::BOLD),
),
Span::styled(
format!("{} ", icons::LINE_VERTICAL),
Style::default().fg(theme::BORDER),
),
];
spans.extend(key_hint("Enter", "Save"));
spans.extend(key_hint("Esc", "Cancel"));
Line::from(spans)
}
fn render_search_mode_hints() -> Line<'static> {
let mut spans = vec![
Span::styled(
format!(" {} Search ", icons::BULLET),
Style::default()
.fg(theme::INFO)
.add_modifier(Modifier::BOLD),
),
Span::styled(
format!("{} ", icons::LINE_VERTICAL),
Style::default().fg(theme::BORDER),
),
];
spans.extend(key_hint("Enter", "Go"));
spans.extend(key_hint("↑/↓", "Navigate"));
spans.extend(key_hint("Esc", "Cancel"));
Line::from(spans)
}