added search field
This commit is contained in:
+40
-8
@@ -20,6 +20,8 @@ pub struct App {
|
|||||||
exit: bool,
|
exit: bool,
|
||||||
stations: Vec<StationInfo>,
|
stations: Vec<StationInfo>,
|
||||||
station_list_state: ListState,
|
station_list_state: ListState,
|
||||||
|
filtered_stations: Vec<StationInfo>,
|
||||||
|
search_query: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for App {
|
impl Default for App {
|
||||||
@@ -32,12 +34,13 @@ impl Default for App {
|
|||||||
state.select(Some(0));
|
state.select(Some(0));
|
||||||
state
|
state
|
||||||
},
|
},
|
||||||
|
filtered_stations: Vec::new(),
|
||||||
|
search_query: String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
/// runs the application's main loop until the user quits
|
|
||||||
pub fn run(&mut self, terminal: &mut DefaultTerminal) -> Result<()> {
|
pub fn run(&mut self, terminal: &mut DefaultTerminal) -> Result<()> {
|
||||||
while !self.exit {
|
while !self.exit {
|
||||||
terminal.draw(|frame| self.draw(frame))?;
|
terminal.draw(|frame| self.draw(frame))?;
|
||||||
@@ -59,7 +62,7 @@ impl App {
|
|||||||
// 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 = left.width.saturating_sub(4) as usize;
|
||||||
let station_items: Vec<ListItem> = self
|
let station_items: Vec<ListItem> = self
|
||||||
.stations
|
.filtered_stations
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| {
|
.map(|s| {
|
||||||
let name = if s.name.len() > max_name_len {
|
let name = if s.name.len() > max_name_len {
|
||||||
@@ -77,10 +80,12 @@ impl App {
|
|||||||
.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, 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"), bottom_right);
|
||||||
frame.render_widget(Block::bordered().title("Played Songs"), top_right);
|
|
||||||
|
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
|
||||||
@@ -95,13 +100,10 @@ impl App {
|
|||||||
.block(Block::bordered().title("Info"));
|
.block(Block::bordered().title("Info"));
|
||||||
|
|
||||||
frame.render_widget(paragraph, bottom_right);
|
frame.render_widget(paragraph, bottom_right);
|
||||||
// frame.render_widget(Block::bordered().title("Info / Controls"), bottom_right);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_events(&mut self) -> io::Result<()> {
|
fn handle_events(&mut self) -> io::Result<()> {
|
||||||
match event::read()? {
|
match event::read()? {
|
||||||
// it's important to check that the event is a key press event as
|
|
||||||
// crossterm also emits key release and repeat events on Windows.
|
|
||||||
Event::Key(key_event) if key_event.kind == KeyEventKind::Press => {
|
Event::Key(key_event) if key_event.kind == KeyEventKind::Press => {
|
||||||
self.handle_key_event(key_event)
|
self.handle_key_event(key_event)
|
||||||
}
|
}
|
||||||
@@ -125,6 +127,14 @@ impl App {
|
|||||||
self.station_list_state.select(Some(prev));
|
self.station_list_state.select(Some(prev));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
KeyCode::Char(c) => {
|
||||||
|
self.search_query.push(c);
|
||||||
|
self.update_filter();
|
||||||
|
}
|
||||||
|
KeyCode::Backspace => {
|
||||||
|
self.search_query.pop();
|
||||||
|
self.update_filter();
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,6 +155,8 @@ 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());
|
eprintln!("Loaded {} stations", stations.len());
|
||||||
if self.stations.is_empty() {
|
if self.stations.is_empty() {
|
||||||
self.stations.push(StationInfo {
|
self.stations.push(StationInfo {
|
||||||
@@ -163,4 +175,24 @@ impl App {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_filter(&mut self) {
|
||||||
|
if self.search_query.is_empty() {
|
||||||
|
self.filtered_stations = self.stations.clone();
|
||||||
|
} else {
|
||||||
|
let query = self.search_query.to_lowercase();
|
||||||
|
self.filtered_stations = self
|
||||||
|
.stations
|
||||||
|
.iter()
|
||||||
|
.filter(|s| s.name.to_lowercase().contains(&query))
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.filtered_stations.is_empty() {
|
||||||
|
self.station_list_state.select(None);
|
||||||
|
} else {
|
||||||
|
self.station_list_state.select(Some(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user