use ratatui::{
Frame,
layout::Rect,
style::{Modifier, Style},
text::{Line, Span},
widgets::{Block, BorderType, Borders, Paragraph}
};
use rust_i18n::t;
use crate::tui::{
app::{App, NavKind, Pane},
themes::Palette
};
fn context_name(app: &App) -> String {
if app.detail_open {
return app
.selected_resource()
.map_or_else(|| app.active_tab.display_name().into_owned(), |(_, n)| n);
}
match app.nav_current() {
Some(NavKind::Create) => t!("sidebar.create").into_owned(),
Some(NavKind::Settings) => t!("sidebar.settings").into_owned(),
Some(NavKind::Project(i)) => app
.projects
.get(i)
.map_or_else(String::new, |p| p.name.clone()),
Some(NavKind::Service(tab)) => tab.display_name().into_owned(),
None => app.active_tab.display_name().into_owned()
}
}
fn hints(app: &App) -> Vec<(&'static str, String)> {
if app.filter_editing {
return vec![
("\u{23ce}", t!("ui.status_apply").into_owned()),
("Esc", t!("ui.status_clear").into_owned()),
];
}
if app.picker_open() {
return vec![
("\u{2191}\u{2193}", t!("ui.status_nav").into_owned()),
("\u{23ce}", t!("ui.status_pick").into_owned()),
("Esc", t!("ui.status_back").into_owned()),
];
}
if app.detail_open {
return vec![
("\u{2191}\u{2193}", t!("ui.status_nav").into_owned()),
("y", t!("ui.status_copy").into_owned()),
("\u{23ce}", t!("ui.status_actions").into_owned()),
("Esc", t!("ui.status_back").into_owned()),
("Q", t!("ui.status_quit").into_owned()),
];
}
if app.pane == Pane::Sidebar {
return vec![
("\u{2191}\u{2193}", t!("ui.status_nav").into_owned()),
("\u{23ce}", t!("ui.status_open").into_owned()),
("^K", t!("ui.status_cmds").into_owned()),
("?", t!("ui.status_help").into_owned()),
("Q", t!("ui.status_quit").into_owned()),
];
}
let mut hints = vec![(
"\u{2190}\u{2192}\u{2191}\u{2193}",
t!("ui.status_nav").into_owned()
)];
match app.nav_current() {
Some(NavKind::Create) => {
hints.push(("\u{23ce}", t!("ui.status_create_hint").into_owned()));
}
Some(NavKind::Settings) => {
hints.push(("\u{23ce}", t!("ui.status_change").into_owned()));
}
Some(NavKind::Project(_)) => {
hints.push(("\u{23ce}", t!("ui.status_details").into_owned()));
}
Some(NavKind::Service(_)) | None => {
hints.push(("\u{23ce}", t!("ui.status_details").into_owned()));
hints.push(("/", t!("ui.status_filter").into_owned()));
}
}
hints.push(("Esc", t!("ui.status_back").into_owned()));
hints.push(("Q", t!("ui.status_quit").into_owned()));
hints
}
pub(super) fn render_status_bar(frame: &mut Frame, area: Rect, app: &App, palette: &Palette) {
let key = |k: &str| {
Span::styled(
k.to_string(),
Style::default()
.fg(palette.accent)
.add_modifier(Modifier::BOLD)
)
};
let lbl = |t: String| Span::styled(t, Style::default().fg(palette.dim));
let mut spans = vec![
Span::styled(
format!(" {} ", context_name(app)),
Style::default()
.fg(palette.bg)
.bg(palette.tab_active)
.add_modifier(Modifier::BOLD)
),
Span::raw(" "),
];
for (k, label) in hints(app) {
spans.push(key(k));
spans.push(lbl(format!(" {label} ")));
}
let (message, color) = match (&app.error_message, &app.status_message) {
(Some(err), _) => (format!("\u{25CF} {err}"), palette.error),
(_, Some(msg)) => (format!("\u{25CF} {msg}"), palette.success),
_ => (
t!("ui.status_updated", s => app.last_refresh.elapsed().as_secs()).into_owned(),
palette.dim
)
};
let used: usize = spans.iter().map(|s| s.content.chars().count()).sum();
let inner_w = usize::from(area.width.saturating_sub(2));
let msg_len = message.chars().count();
if used + msg_len < inner_w {
spans.push(Span::raw(" ".repeat(inner_w - used - msg_len)));
spans.push(Span::styled(message, Style::default().fg(color)));
} else if inner_w > used + 2 {
let cut: String = message.chars().take(inner_w - used - 1).collect();
spans.push(Span::raw(" "));
spans.push(Span::styled(cut, Style::default().fg(color)));
}
let paragraph = Paragraph::new(Line::from(spans)).block(
Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.border_style(Style::default().fg(palette.border))
);
frame.render_widget(paragraph, area);
}