Krolyxon
Commit f665c4e

rmptuif665c4e

better event handling with tick, search

Author
krolxon
Date
2024-04-25 12:58 +0530
Parent
4bc03ce
Commit
f665c4e9f3b4eaa226b0c813f6845e247a4e6977

+107 −33

6 files changed

  1. M Cargo.lock +7 −0
  2. M Cargo.toml +1 −0
  3. M src/app.rs +8 −3
  4. M src/connection.rs +27 −0
  5. M src/handler.rs +26 −6
  6. M src/ui.rs +38 −24
M Cargo.lock View
@@ -388,10 +388,17 @@ dependencies = [
388388 "crossterm",
389389 "mpd",
390390 "ratatui",
391+ "rust-fuzzy-search",
391392 "rust_fzf",
392393 "simple-dmenu",
393394 ]
394395
396+[[package]]
397+name = "rust-fuzzy-search"
398+version = "0.1.1"
399+source = "registry+https://github.com/rust-lang/crates.io-index"
400+checksum = "a157657054ffe556d8858504af8a672a054a6e0bd9e8ee531059100c0fa11bb2"
401+
395402 [[package]]
396403 name = "rust_fzf"
397404 version = "0.3.1"
M Cargo.toml View
@@ -12,3 +12,4 @@ rust_fzf = "0.3.1"
1212 simple-dmenu = "0.1.0"
1313 ratatui = "0.26.2"
1414 crossterm = "0.27.0"
15+rust-fuzzy-search = "0.1.1"
M src/app.rs View
@@ -1,3 +1,5 @@
1+use std::time::Duration;
2+
13 use crate::connection::Connection;
24 use crate::list::ContentList;
35 use mpd::Client;
@@ -56,7 +58,11 @@ impl App {
5658 })
5759 }
5860
59− pub fn tick(&self) {}
61+ pub fn tick(&mut self) {
62+ self.conn.update_state();
63+ self.conn.update_progress();
64+ self.update_queue();
65+ }
6066
6167 pub fn quit(&mut self) {
6268 self.running = false;
@@ -76,8 +82,7 @@ impl App {
7682 // });
7783 conn.conn.queue().unwrap().into_iter().for_each(|x| {
7884 vec.push(x.file);
79− }
80− );
85+ });
8186 }
8287
8388 pub fn update_queue(&mut self) {
M src/connection.rs View
@@ -2,6 +2,7 @@ use mpd::song::Song;
22 use mpd::{Client, State};
33 use simple_dmenu::dmenu;
44 use std::process::Command;
5+use std::time::Duration;
56
67 pub type Result<T> = core::result::Result<T, Error>;
78 pub type Error = Box<dyn std::error::Error>;
@@ -11,6 +12,8 @@ pub struct Connection {
1112 pub conn: Client,
1213 pub songs_filenames: Vec<String>,
1314 pub state: String,
15+ pub elapsed: Duration,
16+ pub total_duration: Duration,
1417 }
1518
1619 impl Connection {
@@ -24,10 +27,14 @@ impl Connection {
2427 .map(|x| x.file)
2528 .collect();
2629
30+ let (elapsed, total) = conn.status().unwrap().time.unwrap_or_default();
31+
2732 Ok(Self {
2833 conn,
2934 songs_filenames,
3035 state: "Stopped".to_string(),
36+ elapsed,
37+ total_duration: total,
3138 })
3239 }
3340
@@ -64,6 +71,26 @@ impl Connection {
6471 self.state.clone()
6572 }
6673
74+ pub fn update_progress(&mut self) {
75+ let (elapsed, total) = self.conn.status().unwrap().time.unwrap_or_default();
76+ self.elapsed = elapsed;
77+ self.total_duration = total;
78+ }
79+
80+ pub fn get_progress_ratio(&self) -> f64 {
81+ let total = self.total_duration.as_secs_f64();
82+ if total == 0.0 {
83+ 0.0
84+ } else {
85+ let ratio = self.elapsed.as_secs_f64() / self.total_duration.as_secs_f64();
86+ if ratio > 1.0 || ratio == 0.0 {
87+ 1.0
88+ } else {
89+ ratio
90+ }
91+ }
92+ }
93+
6794 /// push the given song to queue
6895 pub fn push(&mut self, song: &Song) -> Result<()> {
6996 if self.conn.queue().unwrap().is_empty() {
M src/handler.rs View
@@ -2,6 +2,10 @@ use std::time::Duration;
22
33 use crate::app::{App, AppResult, SelectedTab};
44 use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
5+use mpd::{Query, Term};
6+use ratatui::style::Modifier;
7+use rust_fuzzy_search;
8+use simple_dmenu::dmenu;
59
610 pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
711 match key_event.code {
@@ -39,7 +43,6 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
3943 app.queue_list.list.get(app.queue_list.index).unwrap(),
4044 );
4145 app.conn.push(&song)?;
42−
4346 }
4447 SelectedTab::Playlists => {
4548 app.conn
@@ -52,19 +55,16 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
5255 // Toggle Pause
5356 KeyCode::Char('p') => {
5457 app.conn.toggle_pause();
55− app.conn.update_state();
5658 }
5759
5860 // Pause
5961 KeyCode::Char('s') => {
6062 app.conn.pause();
61− app.conn.update_state();
6263 }
6364
6465 // Clear Queue
6566 KeyCode::Char('x') => {
6667 app.conn.conn.clear()?;
67− app.conn.update_state();
6868 // app.update_queue();
6969 }
7070
@@ -73,7 +73,6 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
7373 app.conn.play_dmenu()?;
7474 }
7575
76−
7776 // add to queue
7877 KeyCode::Char('a') => {
7978 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<()> {
8281 app.conn.conn.push(&song)?;
8382 }
8483
85−
8684 KeyCode::Right => {
8785 app.conn
8886 .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<()> {
138136 // Delete highlighted song from the queue
139137 KeyCode::Char('d') => {
140138 app.conn.conn.delete(app.queue_list.index as u32)?;
139+ app.update_queue();
140+ }
141+
142+ KeyCode::Char('U') => {
143+ app.conn.conn.update()?;
144+ }
145+
146+ KeyCode::Char('L') => {
147+ let str = dmenu!(prompt "Search: ");
148+ let list = app
149+ .conn
150+ .songs_filenames
151+ .iter()
152+ .map(|f| f.as_str())
153+ .collect::<Vec<&str>>();
154+ let (filename, _) = rust_fuzzy_search::fuzzy_search_sorted(&str, &list)
155+ .get(0)
156+ .unwrap()
157+ .clone();
158+
159+ let song = app.conn.get_song_with_only_filename(filename);
160+ app.conn.push(&song)?;
141161 }
142162
143163 _ => {}
M src/ui.rs View
@@ -1,7 +1,6 @@
11 use crate::app::{App, AppResult, SelectedTab};
22 use ratatui::{
33 prelude::*,
4− style::palette::tailwind,
54 widgets::{block::Title, *},
65 };
76
@@ -16,8 +15,9 @@ pub fn render(app: &mut App, frame: &mut Frame) {
1615 let layout = Layout::default()
1716 .direction(Direction::Vertical)
1817 .constraints(vec![
19− Constraint::Percentage(5),
20− Constraint::Percentage(88),
18+ Constraint::Percentage(0),
19+ // Constraint::Percentage(88),
20+ Constraint::Percentage(93),
2121 Constraint::Percentage(7),
2222 ])
2323 .split(frame.size());
@@ -37,30 +37,35 @@ pub fn render(app: &mut App, frame: &mut Frame) {
3737 // draw_playlists(frame, app, inner_layout[1]);
3838 draw_progress_bar(frame, app, layout[2]);
3939
40− let highlight_style = (Color::default(), tailwind::YELLOW.c700);
41− let tab = Tabs::new(vec!["Songs List", "Play Queue", "Playlists"])
42− .block(Block::default().title("Tabs").borders(Borders::ALL))
43− .style(Style::default().white())
44− .highlight_style(highlight_style)
45− .divider(" ")
46− .select(app.selected_tab.clone() as usize)
47− .padding("", "");
48− frame.render_widget(tab, layout[0]);
40+ // let highlight_style = (Color::default(), tailwind::YELLOW.c700);
41+ // let tab = Tabs::new(vec!["Songs List", "Play Queue", "Playlists"])
42+ // .block(Block::default().title("Tabs").borders(Borders::ALL))
43+ // .style(Style::default().white())
44+ // .highlight_style(highlight_style)
45+ // .divider(" ")
46+ // .select(app.selected_tab.clone() as usize)
47+ // .padding("", "");
48+ // frame.render_widget(tab, layout[0]);
4949
5050 match app.selected_tab {
5151 SelectedTab::SongList => draw_song_list(frame, app, layout[1]),
52− SelectedTab::Queue => draw_queue(frame, app, layout[1]),
53− SelectedTab::Playlists => draw_playlists(frame, app, layout[1]),
52+ SelectedTab::Queue => draw_queue(frame, app, layout[1]),
53+ SelectedTab::Playlists => draw_playlists(frame, app, layout[1]),
5454 }
5555 }
5656
5757 /// draws list of songs
5858 fn draw_song_list(frame: &mut Frame, app: &mut App, size: Rect) {
5959 let mut song_state = ListState::default();
60+ let total_songs = app.conn.conn.stats().unwrap().songs.to_string();
6061 let list = List::new(app.conn.songs_filenames.clone())
6162 .block(
6263 Block::default()
6364 .title("Song List".green().bold())
65+ .title(
66+ Title::from(format!("Total Songs: {}", total_songs).bold().green())
67+ .alignment(Alignment::Right),
68+ )
6469 .borders(Borders::ALL),
6570 )
6671 .highlight_style(Style::new().add_modifier(Modifier::REVERSED))
@@ -74,16 +79,13 @@ fn draw_song_list(frame: &mut Frame, app: &mut App, size: Rect) {
7479 /// draws playing queue
7580 fn draw_queue(frame: &mut Frame, app: &mut App, size: Rect) {
7681 let mut queue_state = ListState::default();
77− let title = Block::default()
78− .title(Title::from("Play Queue".green().bold()))
79− .title("Shift + ▲ ▼ to scroll, Shift + Enter to play".yellow());
82+ let title = Block::default().title(Title::from("Play Queue".green().bold()));
8083 let list = List::new(app.queue_list.list.clone())
8184 .block(title.borders(Borders::ALL))
8285 .highlight_style(Style::new().add_modifier(Modifier::REVERSED))
8386 .highlight_symbol(">>")
8487 .repeat_highlight_symbol(true);
8588
86− app.update_queue();
8789 queue_state.select(Some(app.queue_list.index));
8890 frame.render_stateful_widget(list, size, &mut queue_state);
8991 }
@@ -92,9 +94,7 @@ fn draw_queue(frame: &mut Frame, app: &mut App, size: Rect) {
9294 fn draw_playlists(frame: &mut Frame, app: &mut App, size: Rect) {
9395 let mut state = ListState::default();
9496
95− let title = Block::default()
96− .title(Title::from("Playlist".green().bold()))
97− .title("▲ ▼ to scroll, Use ► to add playlist to queue".yellow());
97+ let title = Block::default().title(Title::from("Playlist".green().bold()));
9898 let list = List::new(app.pl_list.list.clone())
9999 .block(title.borders(Borders::ALL))
100100 .highlight_style(Style::new().add_modifier(Modifier::REVERSED))
@@ -118,8 +118,22 @@ fn draw_progress_bar(frame: &mut Frame, app: &mut App, size: Rect) {
118118
119119 let title = Block::default()
120120 .title(Title::from(format!("{}: ", state).red().bold()))
121− .title(Title::from(song.green().bold()));
122− // .title(Title::from(app.conn.conn.status().unwrap_or_default().volume.to_string().yellow())).title_alignment(Alignment::Right);
121+ .title(Title::from(song.green().bold()))
122+ .title(
123+ Title::from(
124+ format!(
125+ "{}/{}",
126+ app.conn.elapsed.as_secs(),
127+ app.conn.total_duration.as_secs()
128+ )
129+ .cyan()
130+ .bold(),
131+ )
132+ .alignment(Alignment::Right),
133+ )
134+ .borders(Borders::ALL);
135+
136+ // .title(Title::from(app.conn.conn.status().unwrap_or_default().volume.to_string().yellow())).title_alignment(Alignment::Right);
123137 let progress_bar = LineGauge::default()
124138 .block(title.borders(Borders::ALL))
125139 .gauge_style(
@@ -129,7 +143,7 @@ fn draw_progress_bar(frame: &mut Frame, app: &mut App, size: Rect) {
129143 .add_modifier(Modifier::BOLD),
130144 )
131145 .line_set(symbols::line::THICK)
132− .ratio(0.2);
146+ .ratio(app.conn.get_progress_ratio());
133147
134148 frame.render_widget(progress_bar, size);
135149 }