From f665c4e9f3b4eaa226b0c813f6845e247a4e6977 Mon Sep 17 00:00:00 2001 From: krolxon Date: Thu, 25 Apr 2024 12:58:13 +0530 Subject: [PATCH] better event handling with tick, search --- Cargo.lock | 7 ++++++ Cargo.toml | 1 + src/app.rs | 11 ++++++--- src/connection.rs | 27 +++++++++++++++++++++ src/handler.rs | 32 +++++++++++++++++++----- src/ui.rs | 62 +++++++++++++++++++++++++++++------------------ 6 files changed, 107 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 161af92..79b2484 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -388,10 +388,17 @@ dependencies = [ "crossterm", "mpd", "ratatui", + "rust-fuzzy-search", "rust_fzf", "simple-dmenu", ] +[[package]] +name = "rust-fuzzy-search" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a157657054ffe556d8858504af8a672a054a6e0bd9e8ee531059100c0fa11bb2" + [[package]] name = "rust_fzf" version = "0.3.1" diff --git a/Cargo.toml b/Cargo.toml index 1127b66..ef3cf49 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,4 @@ rust_fzf = "0.3.1" simple-dmenu = "0.1.0" ratatui = "0.26.2" crossterm = "0.27.0" +rust-fuzzy-search = "0.1.1" diff --git a/src/app.rs b/src/app.rs index 7b80584..f129fe3 100755 --- a/src/app.rs +++ b/src/app.rs @@ -1,3 +1,5 @@ +use std::time::Duration; + use crate::connection::Connection; use crate::list::ContentList; use mpd::Client; @@ -56,7 +58,11 @@ impl App { }) } - pub fn tick(&self) {} + pub fn tick(&mut self) { + self.conn.update_state(); + self.conn.update_progress(); + self.update_queue(); + } pub fn quit(&mut self) { self.running = false; @@ -76,8 +82,7 @@ impl App { // }); conn.conn.queue().unwrap().into_iter().for_each(|x| { vec.push(x.file); - } - ); + }); } pub fn update_queue(&mut self) { diff --git a/src/connection.rs b/src/connection.rs index b464b23..a741206 100755 --- a/src/connection.rs +++ b/src/connection.rs @@ -2,6 +2,7 @@ use mpd::song::Song; use mpd::{Client, State}; use simple_dmenu::dmenu; use std::process::Command; +use std::time::Duration; pub type Result = core::result::Result; pub type Error = Box; @@ -11,6 +12,8 @@ pub struct Connection { pub conn: Client, pub songs_filenames: Vec, pub state: String, + pub elapsed: Duration, + pub total_duration: Duration, } impl Connection { @@ -24,10 +27,14 @@ impl Connection { .map(|x| x.file) .collect(); + let (elapsed, total) = conn.status().unwrap().time.unwrap_or_default(); + Ok(Self { conn, songs_filenames, state: "Stopped".to_string(), + elapsed, + total_duration: total, }) } @@ -64,6 +71,26 @@ impl Connection { self.state.clone() } + pub fn update_progress(&mut self) { + let (elapsed, total) = self.conn.status().unwrap().time.unwrap_or_default(); + self.elapsed = elapsed; + self.total_duration = total; + } + + pub fn get_progress_ratio(&self) -> f64 { + let total = self.total_duration.as_secs_f64(); + if total == 0.0 { + 0.0 + } else { + let ratio = self.elapsed.as_secs_f64() / self.total_duration.as_secs_f64(); + if ratio > 1.0 || ratio == 0.0 { + 1.0 + } else { + ratio + } + } + } + /// push the given song to queue pub fn push(&mut self, song: &Song) -> Result<()> { if self.conn.queue().unwrap().is_empty() { diff --git a/src/handler.rs b/src/handler.rs index 67121fb..fce3aa8 100755 --- a/src/handler.rs +++ b/src/handler.rs @@ -2,6 +2,10 @@ use std::time::Duration; use crate::app::{App, AppResult, SelectedTab}; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; +use mpd::{Query, Term}; +use ratatui::style::Modifier; +use rust_fuzzy_search; +use simple_dmenu::dmenu; pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { match key_event.code { @@ -39,7 +43,6 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { app.queue_list.list.get(app.queue_list.index).unwrap(), ); app.conn.push(&song)?; - } SelectedTab::Playlists => { app.conn @@ -52,19 +55,16 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { // Toggle Pause KeyCode::Char('p') => { app.conn.toggle_pause(); - app.conn.update_state(); } // Pause KeyCode::Char('s') => { app.conn.pause(); - app.conn.update_state(); } // Clear Queue KeyCode::Char('x') => { app.conn.conn.clear()?; - app.conn.update_state(); // app.update_queue(); } @@ -73,7 +73,6 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { app.conn.play_dmenu()?; } - // add to queue KeyCode::Char('a') => { let song = app.conn.get_song_with_only_filename( @@ -82,7 +81,6 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { app.conn.conn.push(&song)?; } - KeyCode::Right => { app.conn .push_playlist(app.pl_list.list.get(app.pl_list.index).unwrap())?; @@ -138,6 +136,28 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { // Delete highlighted song from the queue KeyCode::Char('d') => { app.conn.conn.delete(app.queue_list.index as u32)?; + app.update_queue(); + } + + KeyCode::Char('U') => { + app.conn.conn.update()?; + } + + KeyCode::Char('L') => { + let str = dmenu!(prompt "Search: "); + let list = app + .conn + .songs_filenames + .iter() + .map(|f| f.as_str()) + .collect::>(); + let (filename, _) = rust_fuzzy_search::fuzzy_search_sorted(&str, &list) + .get(0) + .unwrap() + .clone(); + + let song = app.conn.get_song_with_only_filename(filename); + app.conn.push(&song)?; } _ => {} diff --git a/src/ui.rs b/src/ui.rs index 624e302..d9ef39b 100755 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,7 +1,6 @@ use crate::app::{App, AppResult, SelectedTab}; use ratatui::{ prelude::*, - style::palette::tailwind, widgets::{block::Title, *}, }; @@ -16,8 +15,9 @@ pub fn render(app: &mut App, frame: &mut Frame) { let layout = Layout::default() .direction(Direction::Vertical) .constraints(vec![ - Constraint::Percentage(5), - Constraint::Percentage(88), + Constraint::Percentage(0), + // Constraint::Percentage(88), + Constraint::Percentage(93), Constraint::Percentage(7), ]) .split(frame.size()); @@ -37,30 +37,35 @@ pub fn render(app: &mut App, frame: &mut Frame) { // draw_playlists(frame, app, inner_layout[1]); draw_progress_bar(frame, app, layout[2]); - let highlight_style = (Color::default(), tailwind::YELLOW.c700); - let tab = Tabs::new(vec!["Songs List", "Play Queue", "Playlists"]) - .block(Block::default().title("Tabs").borders(Borders::ALL)) - .style(Style::default().white()) - .highlight_style(highlight_style) - .divider(" ") - .select(app.selected_tab.clone() as usize) - .padding("", ""); - frame.render_widget(tab, layout[0]); + // let highlight_style = (Color::default(), tailwind::YELLOW.c700); + // let tab = Tabs::new(vec!["Songs List", "Play Queue", "Playlists"]) + // .block(Block::default().title("Tabs").borders(Borders::ALL)) + // .style(Style::default().white()) + // .highlight_style(highlight_style) + // .divider(" ") + // .select(app.selected_tab.clone() as usize) + // .padding("", ""); + // frame.render_widget(tab, layout[0]); match app.selected_tab { SelectedTab::SongList => draw_song_list(frame, app, layout[1]), - SelectedTab::Queue => draw_queue(frame, app, layout[1]), - SelectedTab::Playlists => draw_playlists(frame, app, layout[1]), + SelectedTab::Queue => draw_queue(frame, app, layout[1]), + SelectedTab::Playlists => draw_playlists(frame, app, layout[1]), } } /// draws list of songs fn draw_song_list(frame: &mut Frame, app: &mut App, size: Rect) { let mut song_state = ListState::default(); + let total_songs = app.conn.conn.stats().unwrap().songs.to_string(); let list = List::new(app.conn.songs_filenames.clone()) .block( Block::default() .title("Song List".green().bold()) + .title( + Title::from(format!("Total Songs: {}", total_songs).bold().green()) + .alignment(Alignment::Right), + ) .borders(Borders::ALL), ) .highlight_style(Style::new().add_modifier(Modifier::REVERSED)) @@ -74,16 +79,13 @@ fn draw_song_list(frame: &mut Frame, app: &mut App, size: Rect) { /// draws playing queue fn draw_queue(frame: &mut Frame, app: &mut App, size: Rect) { let mut queue_state = ListState::default(); - let title = Block::default() - .title(Title::from("Play Queue".green().bold())) - .title("Shift + ▲ ▼ to scroll, Shift + Enter to play".yellow()); + let title = Block::default().title(Title::from("Play Queue".green().bold())); let list = List::new(app.queue_list.list.clone()) .block(title.borders(Borders::ALL)) .highlight_style(Style::new().add_modifier(Modifier::REVERSED)) .highlight_symbol(">>") .repeat_highlight_symbol(true); - app.update_queue(); queue_state.select(Some(app.queue_list.index)); frame.render_stateful_widget(list, size, &mut queue_state); } @@ -92,9 +94,7 @@ fn draw_queue(frame: &mut Frame, app: &mut App, size: Rect) { fn draw_playlists(frame: &mut Frame, app: &mut App, size: Rect) { let mut state = ListState::default(); - let title = Block::default() - .title(Title::from("Playlist".green().bold())) - .title("▲ ▼ to scroll, Use ► to add playlist to queue".yellow()); + let title = Block::default().title(Title::from("Playlist".green().bold())); let list = List::new(app.pl_list.list.clone()) .block(title.borders(Borders::ALL)) .highlight_style(Style::new().add_modifier(Modifier::REVERSED)) @@ -118,8 +118,22 @@ fn draw_progress_bar(frame: &mut Frame, app: &mut App, size: Rect) { let title = Block::default() .title(Title::from(format!("{}: ", state).red().bold())) - .title(Title::from(song.green().bold())); - // .title(Title::from(app.conn.conn.status().unwrap_or_default().volume.to_string().yellow())).title_alignment(Alignment::Right); + .title(Title::from(song.green().bold())) + .title( + Title::from( + format!( + "{}/{}", + app.conn.elapsed.as_secs(), + app.conn.total_duration.as_secs() + ) + .cyan() + .bold(), + ) + .alignment(Alignment::Right), + ) + .borders(Borders::ALL); + + // .title(Title::from(app.conn.conn.status().unwrap_or_default().volume.to_string().yellow())).title_alignment(Alignment::Right); let progress_bar = LineGauge::default() .block(title.borders(Borders::ALL)) .gauge_style( @@ -129,7 +143,7 @@ fn draw_progress_bar(frame: &mut Frame, app: &mut App, size: Rect) { .add_modifier(Modifier::BOLD), ) .line_set(symbols::line::THICK) - .ratio(0.2); + .ratio(app.conn.get_progress_ratio()); frame.render_widget(progress_bar, size); }