added input modes

This commit is contained in:
2025-08-05 20:45:11 +02:00
parent 6c05c76f3f
commit a2d14ca5ce
+59 -36
View File
@@ -15,6 +15,12 @@ use std::io::{self};
use crate::radio::station::StationInfo; use crate::radio::station::StationInfo;
use ratatui::widgets::{List, ListItem, ListState}; use ratatui::widgets::{List, ListItem, ListState};
#[derive(Debug, PartialEq)]
pub enum InputMode {
Normal,
Search,
}
#[derive(Debug)] #[derive(Debug)]
pub struct App { pub struct App {
exit: bool, exit: bool,
@@ -22,6 +28,7 @@ pub struct App {
station_list_state: ListState, station_list_state: ListState,
filtered_stations: Vec<StationInfo>, filtered_stations: Vec<StationInfo>,
search_query: String, search_query: String,
input_mode: InputMode,
} }
impl Default for App { impl Default for App {
@@ -35,6 +42,7 @@ impl Default for App {
state state
}, },
filtered_stations: Vec::new(), filtered_stations: Vec::new(),
input_mode: InputMode::Normal,
search_query: String::new(), search_query: String::new(),
} }
} }
@@ -57,10 +65,13 @@ impl App {
.areas(area); .areas(area);
let [top_right, bottom_right] = let [top_right, bottom_right] =
Layout::vertical([Constraint::Percentage(50), Constraint::Percentage(50)]).areas(right); Layout::vertical([Constraint::Percentage(10), Constraint::Percentage(90)]).areas(right);
let [top_left, bottom_left] =
Layout::vertical([Constraint::Percentage(50), Constraint::Percentage(50)]).areas(left);
// Render the left block (e.g., station selection) // Render the left block (e.g., station selection)
let max_name_len = left.width.saturating_sub(4) as usize; let max_name_len = top_left.width.saturating_sub(4) as usize;
let station_items: Vec<ListItem> = self let station_items: Vec<ListItem> = self
.filtered_stations .filtered_stations
.iter() .iter()
@@ -79,27 +90,28 @@ impl App {
.highlight_symbol(">>") .highlight_symbol(">>")
.highlight_style(ratatui::style::Style::default().reversed()); .highlight_style(ratatui::style::Style::default().reversed());
frame.render_stateful_widget(stations_list, left, &mut self.station_list_state.clone()); frame.render_stateful_widget(
stations_list,
frame.render_widget(Block::bordered().title("Played Songs"), bottom_right); top_left,
&mut self.station_list_state.clone(),
let search = Paragraph::new(format!("Search: {}", self.search_query)) );
.block(Block::bordered().title("Search"));
frame.render_widget(search, top_right);
let station = self let station = self
.station_list_state .station_list_state
.selected() .selected()
.and_then(|i| self.stations.get(i)); .and_then(|i| self.filtered_stations.get(i));
let paragraph = Paragraph::new( let paragraph = Paragraph::new(
station station
.map(|s| format!("Now selected: {}\n{}", s.name, s.url)) .map(|s| format!("Now selected: {}\n{}", s.name, s.url))
.unwrap_or_else(|| "No station selected".into()), .unwrap_or_else(|| "No station selected".into()),
) )
.block(Block::bordered().title("Info")); .block(Block::bordered().title("Info"));
frame.render_widget(paragraph, bottom_left);
frame.render_widget(paragraph, bottom_right); let search = Paragraph::new(format!("{}", self.search_query))
.block(Block::bordered().title("Search"));
frame.render_widget(search, top_right);
frame.render_widget(Block::bordered().title("Played Songs"), bottom_right);
} }
fn handle_events(&mut self) -> io::Result<()> { fn handle_events(&mut self) -> io::Result<()> {
@@ -113,29 +125,41 @@ impl App {
} }
fn handle_key_event(&mut self, key_event: KeyEvent) { fn handle_key_event(&mut self, key_event: KeyEvent) {
match key_event.code { match self.input_mode {
KeyCode::Char('q') => self.exit(), InputMode::Normal => match key_event.code {
KeyCode::Down | KeyCode::Char('j') => { KeyCode::Char('/') => {
if let Some(i) = self.station_list_state.selected() { self.input_mode = InputMode::Search;
let next = (i + 1).min(self.stations.len().saturating_sub(1)); self.search_query.clear();
self.station_list_state.select(Some(next));
} }
} KeyCode::Char('q') => self.exit(),
KeyCode::Up | KeyCode::Char('k') => { KeyCode::Down | KeyCode::Char('j') => {
if let Some(i) = self.station_list_state.selected() { if let Some(i) = self.station_list_state.selected() {
let prev = i.saturating_sub(1); let next = (i + 1).min(self.stations.len().saturating_sub(1));
self.station_list_state.select(Some(prev)); self.station_list_state.select(Some(next));
}
} }
} KeyCode::Up | KeyCode::Char('k') => {
KeyCode::Char(c) => { if let Some(i) = self.station_list_state.selected() {
self.search_query.push(c); let prev = i.saturating_sub(1);
self.update_filter(); self.station_list_state.select(Some(prev));
} }
KeyCode::Backspace => { }
self.search_query.pop(); _ => {}
self.update_filter(); },
} InputMode::Search => match key_event.code {
_ => {} KeyCode::Char(c) => {
self.search_query.push(c);
self.update_filter();
}
KeyCode::Backspace => {
self.search_query.pop();
self.update_filter();
}
KeyCode::Esc | KeyCode::Enter => {
self.input_mode = InputMode::Normal;
}
_ => {}
},
} }
} }
@@ -155,9 +179,6 @@ impl App {
.await .await
.map_err(|e| eyre!("Failed to fetch station: {e}"))?; .map_err(|e| eyre!("Failed to fetch station: {e}"))?;
self.filtered_stations = self.stations.clone();
eprintln!("Loaded {} stations", stations.len());
if self.stations.is_empty() { if self.stations.is_empty() {
self.stations.push(StationInfo { self.stations.push(StationInfo {
name: "No stations found".into(), name: "No stations found".into(),
@@ -173,6 +194,8 @@ impl App {
}) })
.collect(); .collect();
self.update_filter();
Ok(()) Ok(())
} }