Improve search and playlist management
- Add folder priority boost in fuzzy search scoring - Show search results in top status bar after Enter - Change keybindings: v for mark, a for add to playlist, c for clear - Add playlist management: add_to_playlist() and clear_playlist() - Fix playlist index reset when clearing playlist - Display incremental search status in bottom bar while typing
This commit is contained in:
126
src/main.rs
126
src/main.rs
@@ -7,7 +7,7 @@ mod ui;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use crossterm::{
|
||||
event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind},
|
||||
event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers},
|
||||
execute,
|
||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||
};
|
||||
@@ -105,7 +105,7 @@ async fn run_app<B: ratatui::backend::Backend>(
|
||||
if event::poll(std::time::Duration::from_millis(100))? {
|
||||
if let Event::Key(key) = event::read()? {
|
||||
if key.kind == KeyEventKind::Press {
|
||||
handle_key_event(state, player, key.code).await?;
|
||||
handle_key_event(state, player, key).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,51 +118,109 @@ async fn run_app<B: ratatui::backend::Backend>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_key_event(state: &mut AppState, player: &mut player::Player, key_code: KeyCode) -> Result<()> {
|
||||
match key_code {
|
||||
KeyCode::Char('q') => {
|
||||
async fn handle_key_event(state: &mut AppState, player: &mut player::Player, key: KeyEvent) -> Result<()> {
|
||||
// Handle search mode separately
|
||||
if state.search_mode {
|
||||
match key.code {
|
||||
KeyCode::Char(c) => {
|
||||
state.append_search_char(c);
|
||||
}
|
||||
KeyCode::Backspace => {
|
||||
state.backspace_search();
|
||||
}
|
||||
KeyCode::Tab => {
|
||||
state.tab_search_next();
|
||||
}
|
||||
KeyCode::BackTab => {
|
||||
state.tab_search_prev();
|
||||
}
|
||||
KeyCode::Enter => {
|
||||
state.execute_search();
|
||||
}
|
||||
KeyCode::Esc => {
|
||||
state.exit_search_mode();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match (key.code, key.modifiers) {
|
||||
(KeyCode::Char('q'), _) => {
|
||||
state.should_quit = true;
|
||||
}
|
||||
KeyCode::Char('k') => {
|
||||
state.move_selection_up();
|
||||
(KeyCode::Char('/'), _) => {
|
||||
state.enter_search_mode();
|
||||
}
|
||||
KeyCode::Char('j') => {
|
||||
state.move_selection_down();
|
||||
(KeyCode::Esc, _) => {
|
||||
state.search_matches.clear();
|
||||
}
|
||||
KeyCode::Char('h') => {
|
||||
state.collapse_selected();
|
||||
}
|
||||
KeyCode::Char('l') => {
|
||||
state.expand_selected();
|
||||
}
|
||||
KeyCode::Char('t') => {
|
||||
state.toggle_mark();
|
||||
}
|
||||
KeyCode::Char('c') => {
|
||||
state.clear_marks();
|
||||
}
|
||||
KeyCode::Char('n') => {
|
||||
state.play_next();
|
||||
if let Some(ref path) = state.current_file {
|
||||
player.play(path)?;
|
||||
tracing::info!("Next track: {:?}", path);
|
||||
(KeyCode::Char('n'), _) => {
|
||||
if !state.search_matches.is_empty() {
|
||||
state.next_search_match();
|
||||
} else {
|
||||
// If stopped, start from current index (0), otherwise go to next
|
||||
if state.player_state == PlayerState::Stopped && !state.playlist.is_empty() {
|
||||
state.current_file = Some(state.playlist[state.playlist_index].clone());
|
||||
state.player_state = PlayerState::Playing;
|
||||
if let Some(ref path) = state.current_file {
|
||||
player.play(path)?;
|
||||
tracing::info!("Starting playlist: {:?}", path);
|
||||
}
|
||||
} else {
|
||||
state.play_next();
|
||||
if let Some(ref path) = state.current_file {
|
||||
player.play(path)?;
|
||||
tracing::info!("Next track: {:?}", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
KeyCode::Char('p') => {
|
||||
(KeyCode::Char('N'), KeyModifiers::SHIFT) => {
|
||||
state.prev_search_match();
|
||||
}
|
||||
(KeyCode::Char('d'), KeyModifiers::CONTROL) => {
|
||||
state.page_down();
|
||||
}
|
||||
(KeyCode::Char('u'), KeyModifiers::CONTROL) => {
|
||||
state.page_up();
|
||||
}
|
||||
(KeyCode::Char('k'), _) | (KeyCode::Up, _) => {
|
||||
state.move_selection_up();
|
||||
}
|
||||
(KeyCode::Char('j'), _) | (KeyCode::Down, _) => {
|
||||
state.move_selection_down();
|
||||
}
|
||||
(KeyCode::Char('h'), _) => {
|
||||
state.collapse_selected();
|
||||
}
|
||||
(KeyCode::Char('l'), _) => {
|
||||
state.expand_selected();
|
||||
}
|
||||
(KeyCode::Char('v'), _) => {
|
||||
state.toggle_mark();
|
||||
}
|
||||
(KeyCode::Char('a'), _) => {
|
||||
state.add_to_playlist();
|
||||
}
|
||||
(KeyCode::Char('c'), _) => {
|
||||
state.clear_playlist();
|
||||
}
|
||||
(KeyCode::Char('p'), _) => {
|
||||
state.play_previous();
|
||||
if let Some(ref path) = state.current_file {
|
||||
player.play(path)?;
|
||||
tracing::info!("Previous track: {:?}", path);
|
||||
}
|
||||
}
|
||||
KeyCode::Enter => {
|
||||
(KeyCode::Enter, _) => {
|
||||
state.play_selection();
|
||||
if let Some(ref path) = state.current_file {
|
||||
player.play(path)?;
|
||||
tracing::info!("Playing: {:?} (playlist: {} tracks)", path, state.playlist.len());
|
||||
}
|
||||
}
|
||||
KeyCode::Char(' ') => {
|
||||
(KeyCode::Char(' '), _) => {
|
||||
match state.player_state {
|
||||
PlayerState::Playing => {
|
||||
player.pause()?;
|
||||
@@ -177,31 +235,31 @@ async fn handle_key_event(state: &mut AppState, player: &mut player::Player, key
|
||||
PlayerState::Stopped => {}
|
||||
}
|
||||
}
|
||||
KeyCode::Left => {
|
||||
(KeyCode::Left, _) => {
|
||||
if state.player_state != PlayerState::Stopped {
|
||||
player.seek(-10.0)?;
|
||||
tracing::info!("Seek backward 10s");
|
||||
}
|
||||
}
|
||||
KeyCode::Right => {
|
||||
(KeyCode::Right, _) => {
|
||||
if state.player_state != PlayerState::Stopped {
|
||||
player.seek(10.0)?;
|
||||
tracing::info!("Seek forward 10s");
|
||||
}
|
||||
}
|
||||
KeyCode::Char('+') | KeyCode::Char('=') => {
|
||||
(KeyCode::Char('+'), _) | (KeyCode::Char('='), _) => {
|
||||
let new_volume = (state.volume + 5).min(100);
|
||||
state.volume = new_volume;
|
||||
player.set_volume(new_volume)?;
|
||||
tracing::info!("Volume: {}%", new_volume);
|
||||
}
|
||||
KeyCode::Char('-') => {
|
||||
(KeyCode::Char('-'), _) => {
|
||||
let new_volume = (state.volume - 5).max(0);
|
||||
state.volume = new_volume;
|
||||
player.set_volume(new_volume)?;
|
||||
tracing::info!("Volume: {}%", new_volume);
|
||||
}
|
||||
KeyCode::Char('r') => {
|
||||
(KeyCode::Char('r'), _) => {
|
||||
state.is_refreshing = true;
|
||||
tracing::info!("Rescanning...");
|
||||
let cache_dir = cache::get_cache_dir()?;
|
||||
|
||||
Reference in New Issue
Block a user