diff --git a/src/main.rs b/src/main.rs index 0610960..557b6d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ use iradio::radio::{app::App, tui}; async fn main() -> Result<()> { color_eyre::install()?; let mut terminal = tui::init()?; + terminal.clear()?; let mut app = App::default(); app.load_stations().await?; let app_result = app.run(&mut terminal); diff --git a/src/radio/app.rs b/src/radio/app.rs index 5341c30..9e77250 100644 --- a/src/radio/app.rs +++ b/src/radio/app.rs @@ -7,20 +7,33 @@ use radiobrowser::RadioBrowserAPI; use ratatui::{ DefaultTerminal, Frame, layout::{Constraint, Layout}, + style::Stylize, widgets::{Block, Paragraph}, }; use std::io::{self}; use crate::radio::station::StationInfo; -use ratatui::style::{Color, Modifier, Style}; use ratatui::widgets::{List, ListItem, ListState}; -#[derive(Debug, Default)] +#[derive(Debug)] pub struct App { exit: bool, stations: Vec, - selected_index: usize, - scroll_offset: usize, + station_list_state: ListState, +} + +impl Default for App { + fn default() -> Self { + Self { + exit: false, + stations: Vec::new(), + station_list_state: { + let mut state = ListState::default(); + state.select(Some(0)); + state + }, + } + } } impl App { @@ -44,33 +57,36 @@ impl App { Layout::vertical([Constraint::Percentage(50), Constraint::Percentage(50)]).areas(right); // Render the left block (e.g., station selection) - let items: Vec = self + let max_name_len = left.width.saturating_sub(4) as usize; + let station_items: Vec = self .stations .iter() - .skip(self.scroll_offset) - .take(15) // max visible - .map(|s| ListItem::new(s.name.clone())) + .map(|s| { + let name = if s.name.len() > max_name_len { + format!("{}...", &s.name[..max_name_len.saturating_sub(3)]) + } else { + s.name.clone() + }; + ListItem::new(name.trim().to_owned()) + }) .collect(); - let mut list_state = ListState::default(); - list_state.select(Some(self.selected_index - self.scroll_offset)); + let stations_list = List::new(station_items) + .block(Block::bordered().title("Station Selection")) + .highlight_symbol(">>") + .highlight_style(ratatui::style::Style::default().reversed()); - let list = List::new(items) - .block(Block::bordered().title("Stations")) - .highlight_style( - Style::default() - .fg(Color::Yellow) - .add_modifier(Modifier::BOLD), - ) - .highlight_symbol("➤ "); - - frame.render_stateful_widget(list, left, &mut list_state); + frame.render_stateful_widget(stations_list, left, &mut self.station_list_state.clone()); // frame.render_widget(Block::bordered().title("Station Selection"), left); // Render the top-right block (e.g., song history) frame.render_widget(Block::bordered().title("Played Songs"), top_right); - let station = self.stations.get(self.selected_index); + let station = self + .station_list_state + .selected() + .and_then(|i| self.stations.get(i)); + let paragraph = Paragraph::new( station .map(|s| format!("Now selected: {}\n{}", s.name, s.url)) @@ -98,19 +114,15 @@ impl App { match key_event.code { KeyCode::Char('q') => self.exit(), KeyCode::Down | KeyCode::Char('j') => { - if self.selected_index + 1 < self.stations.len() { - self.selected_index += 1; - if self.selected_index >= self.scroll_offset + 15 { - self.scroll_offset += 1; - } + if let Some(i) = self.station_list_state.selected() { + let next = (i + 1).min(self.stations.len().saturating_sub(1)); + self.station_list_state.select(Some(next)); } } KeyCode::Up | KeyCode::Char('k') => { - if self.selected_index > 0 { - self.selected_index -= 1; - if self.selected_index < self.scroll_offset { - self.scroll_offset = self.scroll_offset.saturating_sub(1); - } + if let Some(i) = self.station_list_state.selected() { + let prev = i.saturating_sub(1); + self.station_list_state.select(Some(prev)); } } _ => {} @@ -127,7 +139,7 @@ impl App { .map_err(|e| eyre!("Failed to create RadioBrowserAPI: {e}"))?; let stations = api .get_stations() - .country("DE") + .country("Germany") .limit("50") .send() .await