use ratatui::{
buffer::Buffer,
layout::{Constraint, Layout, Rect},
style::Style,
text::{Line, Span},
widgets::Widget,
};
use crate::app::state::{Notification, Page};
use crate::ui::theme::ThemeColors;
pub struct Footer<'a> {
page: Page,
sample_rate: Option<u32>,
notification: Option<&'a Notification>,
colors: ThemeColors,
}
impl<'a> Footer<'a> {
pub fn new(page: Page, colors: ThemeColors) -> Self {
Self {
page,
sample_rate: None,
notification: None,
colors,
}
}
pub fn sample_rate(mut self, rate: Option<u32>) -> Self {
self.sample_rate = rate;
self
}
pub fn notification(mut self, notification: Option<&'a Notification>) -> Self {
self.notification = notification;
self
}
fn keybinds(&self) -> Vec<(&'static str, &'static str)> {
let mut binds = vec![
("q", "Quit"),
("p/Space", "Pause"),
("h", "Prev"),
("l", "Next"),
];
match self.page {
Page::Browse => {
binds.extend([
("Enter", "Play"),
("e", "Add"),
("n", "Add next"),
("←/→", "Songs/Albums"),
("/", "Search"),
("Tab", "Focus"),
("/", "Search"),
("f", "Star/Un-star"),
]);
}
Page::Artists => {
binds.extend([
("Enter", "Play"),
("←/→", "Focus"),
("/", "Search"),
("e", "Add"),
("n", "Add next"),
("s", "Shuffle"),
("f", "Star/Un-star"),
]);
}
Page::Queue => {
binds.extend([
("Enter", "Play"),
("d", "Remove"),
("J/K", "Move"),
("s", "Shuffle"),
("c", "Clear history"),
("f", "Star/Un-star"),
]);
}
Page::Playlists => {
binds.extend([
("Enter", "Play"),
("←/→", "Focus"),
("e", "Add"),
("n", "Add next"),
("s", "Shuffle play"),
]);
}
Page::Radio => {
binds.extend([
("Enter", "Play"),
("Space", "Play/Pause"),
("Ctrl+R", "Refresh"),
]);
}
Page::Server => {
binds.extend([
("Tab", "Next field"),
("Enter", "Test/Save"),
("Ctrl+R", "Refresh"),
]);
}
Page::Settings => {
binds.extend([("←/→/Enter", "Change theme")]);
}
}
binds
}
}
impl Widget for Footer<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
if area.height < 1 {
return;
}
let chunks = Layout::horizontal([Constraint::Min(40), Constraint::Length(30)]).split(area);
if let Some(notif) = self.notification {
let style = if notif.is_error {
Style::default().fg(self.colors.error)
} else {
Style::default().fg(self.colors.success)
};
buf.set_string(chunks[0].x, chunks[0].y, ¬if.message, style);
} else {
let binds = self.keybinds();
let mut spans = Vec::new();
for (i, (key, desc)) in binds.iter().enumerate() {
if i > 0 {
spans.push(Span::styled(
" │ ",
Style::default().fg(self.colors.secondary),
));
}
spans.push(Span::styled(*key, Style::default().fg(self.colors.accent)));
spans.push(Span::raw(":"));
spans.push(Span::styled(*desc, Style::default().fg(self.colors.muted)));
}
let line = Line::from(spans);
buf.set_line(chunks[0].x, chunks[0].y, &line, chunks[0].width);
}
if let Some(rate) = self.sample_rate {
let rate_str = format!("{}kHz", rate / 1000);
let x = chunks[1].x + chunks[1].width.saturating_sub(rate_str.len() as u16);
buf.set_string(
x,
chunks[1].y,
&rate_str,
Style::default().fg(self.colors.success),
);
}
}
}