use filetree view instead of all song list
This commit is contained in:
parent
f665c4e9f3
commit
00c5c12d82
19
src/app.rs
19
src/app.rs
|
|
@ -1,5 +1,6 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use crate::browser::FileBrowser;
|
||||||
use crate::connection::Connection;
|
use crate::connection::Connection;
|
||||||
use crate::list::ContentList;
|
use crate::list::ContentList;
|
||||||
use mpd::Client;
|
use mpd::Client;
|
||||||
|
|
@ -17,11 +18,12 @@ pub struct App {
|
||||||
pub queue_list: ContentList<String>,
|
pub queue_list: ContentList<String>,
|
||||||
pub pl_list: ContentList<String>,
|
pub pl_list: ContentList<String>,
|
||||||
pub selected_tab: SelectedTab,
|
pub selected_tab: SelectedTab,
|
||||||
|
pub browser: FileBrowser,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum SelectedTab {
|
pub enum SelectedTab {
|
||||||
SongList,
|
DirectoryBrowser,
|
||||||
Queue,
|
Queue,
|
||||||
Playlists,
|
Playlists,
|
||||||
}
|
}
|
||||||
|
|
@ -29,9 +31,9 @@ pub enum SelectedTab {
|
||||||
impl SelectedTab {
|
impl SelectedTab {
|
||||||
fn as_usize(&self) {
|
fn as_usize(&self) {
|
||||||
match self {
|
match self {
|
||||||
SelectedTab::SongList => 0,
|
SelectedTab::Queue => 0,
|
||||||
SelectedTab::Queue => 1,
|
SelectedTab::Playlists => 1,
|
||||||
SelectedTab::Playlists => 2,
|
SelectedTab::DirectoryBrowser => 2,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -48,13 +50,15 @@ impl App {
|
||||||
let mut song_list = ContentList::new();
|
let mut song_list = ContentList::new();
|
||||||
song_list.list = conn.songs_filenames.clone();
|
song_list.list = conn.songs_filenames.clone();
|
||||||
|
|
||||||
|
let browser = FileBrowser::new();
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
running: true,
|
running: true,
|
||||||
conn,
|
conn,
|
||||||
song_list,
|
song_list,
|
||||||
queue_list,
|
queue_list,
|
||||||
pl_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_state();
|
||||||
self.conn.update_progress();
|
self.conn.update_progress();
|
||||||
self.update_queue();
|
self.update_queue();
|
||||||
|
self.browser.update_directory(&mut self.conn).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn quit(&mut self) {
|
pub fn quit(&mut self) {
|
||||||
|
|
@ -103,9 +108,9 @@ impl App {
|
||||||
|
|
||||||
pub fn cycle_tabls(&mut self) {
|
pub fn cycle_tabls(&mut self) {
|
||||||
self.selected_tab = match self.selected_tab {
|
self.selected_tab = match self.selected_tab {
|
||||||
SelectedTab::SongList => SelectedTab::Queue,
|
SelectedTab::DirectoryBrowser => SelectedTab::Queue,
|
||||||
SelectedTab::Queue => SelectedTab::Playlists,
|
SelectedTab::Queue => SelectedTab::Playlists,
|
||||||
SelectedTab::Playlists => SelectedTab::SongList,
|
SelectedTab::Playlists => SelectedTab::DirectoryBrowser,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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::<Vec<(String, String)>>();
|
||||||
|
|
||||||
|
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::<Vec<&str>>();
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 {
|
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::Queue => app.queue_list.next(),
|
||||||
SelectedTab::Playlists => app.pl_list.next(),
|
SelectedTab::Playlists => app.pl_list.next(),
|
||||||
},
|
},
|
||||||
|
|
||||||
KeyCode::Char('k') | KeyCode::Up => match app.selected_tab {
|
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::Queue => app.queue_list.prev(),
|
||||||
SelectedTab::Playlists => app.pl_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();
|
// app.update_queue();
|
||||||
|
|
||||||
match app.selected_tab {
|
match app.selected_tab {
|
||||||
SelectedTab::SongList => {
|
SelectedTab::DirectoryBrowser => {
|
||||||
let song = app.conn.get_song_with_only_filename(
|
app.browser.handle_enter(&mut app.conn)?;
|
||||||
app.conn.songs_filenames.get(app.song_list.index).unwrap(),
|
|
||||||
);
|
|
||||||
app.conn.push(&song)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectedTab::Queue => {
|
SelectedTab::Queue => {
|
||||||
let song = app.conn.get_song_with_only_filename(
|
let song = app.conn.get_song_with_only_filename(
|
||||||
app.queue_list.list.get(app.queue_list.index).unwrap(),
|
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
|
// Playback controls
|
||||||
// Toggle Pause
|
// Toggle Pause
|
||||||
KeyCode::Char('p') => {
|
KeyCode::Char('p') => {
|
||||||
|
|
@ -105,7 +111,7 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyCode::Char('1') => {
|
KeyCode::Char('1') => {
|
||||||
app.selected_tab = SelectedTab::SongList;
|
app.selected_tab = SelectedTab::DirectoryBrowser;
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyCode::Char('2') => {
|
KeyCode::Char('2') => {
|
||||||
|
|
@ -116,6 +122,7 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
|
||||||
app.selected_tab = SelectedTab::Playlists;
|
app.selected_tab = SelectedTab::Playlists;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
KeyCode::Char('n') => {
|
KeyCode::Char('n') => {
|
||||||
app.conn.conn.next()?;
|
app.conn.conn.next()?;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,9 @@ pub mod tui;
|
||||||
/// Content list
|
/// Content list
|
||||||
pub mod list;
|
pub mod list;
|
||||||
|
|
||||||
|
/// File Browser
|
||||||
|
pub mod browser;
|
||||||
|
|
||||||
/// Event Handler
|
/// Event Handler
|
||||||
pub mod event;
|
pub mod event;
|
||||||
|
|
||||||
|
|
|
||||||
38
src/ui.rs
38
src/ui.rs
|
|
@ -1,4 +1,7 @@
|
||||||
use crate::app::{App, AppResult, SelectedTab};
|
use crate::{
|
||||||
|
app::{App, AppResult, SelectedTab},
|
||||||
|
browser::FileBrowser,
|
||||||
|
};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
widgets::{block::Title, *},
|
widgets::{block::Title, *},
|
||||||
|
|
@ -48,9 +51,10 @@ pub fn render(app: &mut App, frame: &mut Frame) {
|
||||||
// frame.render_widget(tab, layout[0]);
|
// frame.render_widget(tab, layout[0]);
|
||||||
|
|
||||||
match app.selected_tab {
|
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::Queue => draw_queue(frame, app, layout[1]),
|
||||||
SelectedTab::Playlists => draw_playlists(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);
|
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<String> = 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);
|
||||||
|
}
|
||||||
|
|
|
||||||
Reference in New Issue