From 00c5c12d82a2d6b9ca5f56f6ca468b2b3212c792 Mon Sep 17 00:00:00 2001 From: krolxon Date: Thu, 25 Apr 2024 14:31:50 +0530 Subject: [PATCH] use filetree view instead of all song list --- src/app.rs | 19 +++++++---- src/browser.rs | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/handler.rs | 23 ++++++++----- src/lib.rs | 3 ++ src/ui.rs | 38 +++++++++++++++++++-- 5 files changed, 158 insertions(+), 17 deletions(-) create mode 100755 src/browser.rs diff --git a/src/app.rs b/src/app.rs index f129fe3..deee711 100755 --- a/src/app.rs +++ b/src/app.rs @@ -1,5 +1,6 @@ use std::time::Duration; +use crate::browser::FileBrowser; use crate::connection::Connection; use crate::list::ContentList; use mpd::Client; @@ -17,11 +18,12 @@ pub struct App { pub queue_list: ContentList, pub pl_list: ContentList, pub selected_tab: SelectedTab, + pub browser: FileBrowser, } #[derive(Debug, PartialEq, Clone)] pub enum SelectedTab { - SongList, + DirectoryBrowser, Queue, Playlists, } @@ -29,9 +31,9 @@ pub enum SelectedTab { impl SelectedTab { fn as_usize(&self) { match self { - SelectedTab::SongList => 0, - SelectedTab::Queue => 1, - SelectedTab::Playlists => 2, + SelectedTab::Queue => 0, + SelectedTab::Playlists => 1, + SelectedTab::DirectoryBrowser => 2, }; } } @@ -48,13 +50,15 @@ impl App { let mut song_list = ContentList::new(); song_list.list = conn.songs_filenames.clone(); + let browser = FileBrowser::new(); Ok(Self { running: true, conn, song_list, queue_list, pl_list, - selected_tab: SelectedTab::SongList, + selected_tab: SelectedTab::DirectoryBrowser, + browser, }) } @@ -62,6 +66,7 @@ impl App { self.conn.update_state(); self.conn.update_progress(); self.update_queue(); + self.browser.update_directory(&mut self.conn).unwrap(); } pub fn quit(&mut self) { @@ -103,9 +108,9 @@ impl App { pub fn cycle_tabls(&mut self) { self.selected_tab = match self.selected_tab { - SelectedTab::SongList => SelectedTab::Queue, + SelectedTab::DirectoryBrowser => SelectedTab::Queue, SelectedTab::Queue => SelectedTab::Playlists, - SelectedTab::Playlists => SelectedTab::SongList, + SelectedTab::Playlists => SelectedTab::DirectoryBrowser, }; } } diff --git a/src/browser.rs b/src/browser.rs new file mode 100755 index 0000000..0ed94ff --- /dev/null +++ b/src/browser.rs @@ -0,0 +1,92 @@ +use mpd::Query; + +use crate::{app::AppResult, connection::Connection}; + +#[derive(Debug)] +pub struct FileBrowser { + pub filetree: Vec<(String, String)>, + pub selected: usize, + pub prev_selected: usize, + pub path: String, + pub prev_path: String, +} + +impl FileBrowser { + pub fn new() -> FileBrowser { + FileBrowser { + filetree: Vec::new(), + selected: 0, + prev_selected: 0, + path: ".".to_string(), + prev_path: ".".to_string(), + } + } + + pub fn update_directory(&mut self, conn: &mut Connection) -> AppResult<()> { + self.filetree.clear(); + self.filetree = conn + .conn + .listfiles(self.path.as_str())? + .into_iter() + .filter(|(f, _)| f == "directory" || f == "file") + .collect::>(); + + Ok(()) + } + + // Go to next item in filetree + pub fn next(&mut self) { + // if self.selected < self.filetree.len() - 1 { + // self.selected += 1; + // } + + if self.selected == self.filetree.len() - 1 { + self.selected = 0; + } else { + self.selected += 1; + } + } + + /// Go to previous item in filetree + pub fn prev(&mut self) { + if self.selected == 0 { + self.selected = self.filetree.len() - 1; + } else { + self.selected -= 1; + } + } + + pub fn handle_enter(&mut self, conn: &mut Connection) -> AppResult<()> { + let (t, path) = self.filetree.get(self.selected).unwrap(); + if t == "directory" { + if path != "." { + self.prev_path = self.path.clone(); + self.path = path.to_string(); + self.update_directory(conn)?; + self.prev_selected = self.selected; + self.selected = 0; + } + } else { + let list = conn + .songs_filenames + .iter() + .map(|f| f.as_str()) + .collect::>(); + let (filename, _) = rust_fuzzy_search::fuzzy_search_sorted(&path, &list) + .get(0) + .unwrap() + .clone(); + + let song = conn.get_song_with_only_filename(filename); + conn.push(&song)?; + } + Ok(()) + } + + pub fn handle_go_back(&mut self, conn: &mut Connection) -> AppResult<()> { + self.path = self.prev_path.clone(); + self.selected = self.prev_selected; + self.update_directory(conn)?; + Ok(()) + } +} diff --git a/src/handler.rs b/src/handler.rs index fce3aa8..81a8639 100755 --- a/src/handler.rs +++ b/src/handler.rs @@ -17,13 +17,13 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { } KeyCode::Char('j') | KeyCode::Down => match app.selected_tab { - SelectedTab::SongList => app.song_list.next(), + SelectedTab::DirectoryBrowser => app.browser.next(), SelectedTab::Queue => app.queue_list.next(), SelectedTab::Playlists => app.pl_list.next(), }, KeyCode::Char('k') | KeyCode::Up => match app.selected_tab { - SelectedTab::SongList => app.song_list.prev(), + SelectedTab::DirectoryBrowser => app.browser.prev(), SelectedTab::Queue => app.queue_list.prev(), SelectedTab::Playlists => app.pl_list.prev(), }, @@ -32,12 +32,10 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { // app.update_queue(); match app.selected_tab { - SelectedTab::SongList => { - let song = app.conn.get_song_with_only_filename( - app.conn.songs_filenames.get(app.song_list.index).unwrap(), - ); - app.conn.push(&song)?; + SelectedTab::DirectoryBrowser => { + app.browser.handle_enter(&mut app.conn)?; } + SelectedTab::Queue => { let song = app.conn.get_song_with_only_filename( app.queue_list.list.get(app.queue_list.index).unwrap(), @@ -51,6 +49,14 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { } } + KeyCode::Char('h') => match app.selected_tab { + SelectedTab::DirectoryBrowser => { + app.browser.handle_go_back(&mut app.conn)?; + } + SelectedTab::Queue => {} + SelectedTab::Playlists => {} + }, + // Playback controls // Toggle Pause KeyCode::Char('p') => { @@ -105,7 +111,7 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { } KeyCode::Char('1') => { - app.selected_tab = SelectedTab::SongList; + app.selected_tab = SelectedTab::DirectoryBrowser; } KeyCode::Char('2') => { @@ -116,6 +122,7 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { app.selected_tab = SelectedTab::Playlists; } + KeyCode::Char('n') => { app.conn.conn.next()?; } diff --git a/src/lib.rs b/src/lib.rs index 60df19e..71a2e93 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,9 @@ pub mod tui; /// Content list pub mod list; +/// File Browser +pub mod browser; + /// Event Handler pub mod event; diff --git a/src/ui.rs b/src/ui.rs index d9ef39b..76a4c09 100755 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,4 +1,7 @@ -use crate::app::{App, AppResult, SelectedTab}; +use crate::{ + app::{App, AppResult, SelectedTab}, + browser::FileBrowser, +}; use ratatui::{ prelude::*, widgets::{block::Title, *}, @@ -48,9 +51,10 @@ pub fn render(app: &mut App, frame: &mut Frame) { // frame.render_widget(tab, layout[0]); match app.selected_tab { - SelectedTab::SongList => draw_song_list(frame, app, layout[1]), + // 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::DirectoryBrowser => draw_directory_browser(frame, app, layout[1]), } } @@ -147,3 +151,33 @@ fn draw_progress_bar(frame: &mut Frame, app: &mut App, size: Rect) { frame.render_widget(progress_bar, size); } + +fn draw_directory_browser(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 mut list: Vec = vec![]; + for (t, s) in app.browser.filetree.iter() { + if t == "file" { + list.push(s.to_string()); + } else { + list.push(format!("[{}]", *s)); + } + } + let list = List::new(list) + .block( + Block::default() + .title(format!("File Browser: {}", app.browser.path.clone()).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)) + .highlight_symbol(">>") + .repeat_highlight_symbol(true) + .scroll_padding(20); + + song_state.select(Some(app.browser.selected)); + frame.render_stateful_widget(list, size, &mut song_state); +}