diff --git a/README.md b/README.md index d886d91..fe69453 100755 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ rmptui is a minimal tui mpd client made with rust. | `r` | Toggle repeat | | `z` | Toggle random | | `/` | Search | +| `R` | Rename Playlist | | `g` | Go to top of list | | `G` | Go to bottom of list | @@ -54,4 +55,5 @@ rmptui is a minimal tui mpd client made with rust. - [x] metadata based tree view - [x] view playlist - [x] change playlist name +- [x] add to new playlist - [ ] add lyrics fetcher diff --git a/src/app.rs b/src/app.rs index 9b2d2fd..4b7b73e 100755 --- a/src/app.rs +++ b/src/app.rs @@ -25,9 +25,14 @@ pub struct App { pub inputmode: InputMode, // Defines input mode, Normal or Search pub search_input: String, // Stores the userinput to be searched pub search_cursor_pos: usize, // Stores the cursor position for searching + pub pl_newname_input: String, // Stores the new name of the playlist pub pl_cursor_pos: usize, // Stores the cursor position for renaming playlist + pub pl_new_pl_input: String, // Stores the name of new playlist to be created + pub pl_new_pl_cursor_pos: usize, // Stores the cursor position of new playlist to be created + pub pl_new_pl_songs_buffer: Vec, // Buffer for songs that need to be added to the newly created playlist + // playlist variables // used to show playlist popup pub playlist_popup: bool, @@ -80,6 +85,9 @@ impl App { search_cursor_pos: 0, pl_cursor_pos: 0, playlist_popup: false, + pl_new_pl_input: String::new(), + pl_new_pl_cursor_pos: 0, + pl_new_pl_songs_buffer: Vec::new(), append_list, should_update_song_list: false, queue_state, @@ -136,6 +144,7 @@ impl App { pub fn get_append_list(conn: &mut Client) -> AppResult> { let mut list = ContentList::new(); + list.list.push("New Playlist".to_string()); list.list.push("Current Playlist".to_string()); for item in Self::get_playlist(conn)? { list.list.push(item.to_string()); @@ -286,6 +295,10 @@ impl App { let cursor_moved_left = self.search_cursor_pos.saturating_sub(1); self.search_cursor_pos = self.clamp_cursor(cursor_moved_left); } + InputMode::NewPlaylist => { + let cursor_moved_left = self.pl_new_pl_cursor_pos.saturating_sub(1); + self.pl_new_pl_cursor_pos = self.clamp_cursor(cursor_moved_left); + } _ => {} } } @@ -300,6 +313,12 @@ impl App { let cursor_moved_right = self.search_cursor_pos.saturating_add(1); self.search_cursor_pos = self.clamp_cursor(cursor_moved_right); } + + InputMode::NewPlaylist => { + let cursor_moved_right = self.pl_new_pl_cursor_pos.saturating_add(1); + self.pl_new_pl_cursor_pos = self.clamp_cursor(cursor_moved_right); + } + _ => {} } } @@ -308,19 +327,24 @@ impl App { match self.inputmode { InputMode::PlaylistRename => { self.pl_newname_input.insert(self.pl_cursor_pos, new_char); - self.move_cursor_right(); + } + InputMode::NewPlaylist => { + self.pl_new_pl_input + .insert(self.pl_new_pl_cursor_pos, new_char); } InputMode::Editing => { self.search_input.insert(self.search_cursor_pos, new_char); - self.move_cursor_right(); } _ => {} } + + self.move_cursor_right(); } pub fn delete_char(&mut self) { let is_not_cursor_leftmost = match self.inputmode { InputMode::PlaylistRename => self.pl_cursor_pos != 0, + InputMode::NewPlaylist => self.pl_new_pl_cursor_pos != 0, InputMode::Editing => self.search_cursor_pos != 0, _ => false, }; @@ -333,27 +357,35 @@ impl App { let current_index = match self.inputmode { InputMode::Editing => self.search_cursor_pos, InputMode::PlaylistRename => self.pl_cursor_pos, + InputMode::NewPlaylist => self.pl_new_pl_cursor_pos, _ => 0, }; let from_left_to_current_index = current_index - 1; if self.inputmode == InputMode::PlaylistRename { - // Getting all characters before the selected character. let before_char_to_delete = self .pl_newname_input .chars() .take(from_left_to_current_index); - // Getting all characters after selected character. let after_char_to_delete = self.pl_newname_input.chars().skip(current_index); - // Put all characters together except the selected one. - // By leaving the selected one out, it is forgotten and therefore deleted. + self.pl_newname_input = before_char_to_delete.chain(after_char_to_delete).collect(); self.move_cursor_left(); + } else if self.inputmode == InputMode::NewPlaylist { + let before_char_to_delete = self + .pl_new_pl_input + .chars() + .take(from_left_to_current_index); + let after_char_to_delete = self.pl_new_pl_input.chars().skip(current_index); + + self.pl_new_pl_input = before_char_to_delete.chain(after_char_to_delete).collect(); + self.move_cursor_left(); } else if self.inputmode == InputMode::Editing { let before_char_to_delete = self.search_input.chars().take(from_left_to_current_index); let after_char_to_delete = self.search_input.chars().skip(current_index); + self.search_input = before_char_to_delete.chain(after_char_to_delete).collect(); self.move_cursor_left(); } @@ -363,6 +395,7 @@ impl App { pub fn clamp_cursor(&self, new_cursor_pos: usize) -> usize { match self.inputmode { InputMode::PlaylistRename => new_cursor_pos.clamp(0, self.pl_newname_input.len()), + InputMode::NewPlaylist => new_cursor_pos.clamp(0, self.pl_new_pl_input.len()), InputMode::Editing => new_cursor_pos.clamp(0, self.search_input.len()), _ => 0, } @@ -376,6 +409,9 @@ impl App { InputMode::PlaylistRename => { self.pl_cursor_pos = 0; } + InputMode::NewPlaylist => { + self.pl_new_pl_cursor_pos = 0; + } _ => {} } } @@ -393,13 +429,6 @@ impl App { } } - pub fn change_playlist_name(&mut self) -> AppResult<()> { - if self.selected_tab == SelectedTab::Playlists { - self.inputmode = InputMode::PlaylistRename; - } - Ok(()) - } - // Mouse event handlers pub fn handle_scroll_up(&mut self) { match self.selected_tab { diff --git a/src/event/handler.rs b/src/event/handler.rs index 02bdb14..8fd4286 100755 --- a/src/event/handler.rs +++ b/src/event/handler.rs @@ -6,7 +6,7 @@ use crate::{ use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseEvent, MouseEventKind}; use std::time::Duration; -use super::{pl_append_keys, pl_rename_keys, search_keys}; +use super::{pl_append_keys, pl_rename_keys, new_pl_keys, search_keys}; pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { // searching, playlist renaming, playlist appending @@ -14,6 +14,8 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { search_keys::handle_search_keys(key_event, app)?; } else if app.inputmode == InputMode::PlaylistRename { pl_rename_keys::handle_pl_rename_keys(key_event, app)?; + } else if app.inputmode == InputMode::NewPlaylist { + new_pl_keys::handle_new_pl_keys(key_event, app)?; } else if app.playlist_popup { pl_append_keys::hande_pl_append_keys(key_event, app)?; } else { @@ -295,8 +297,10 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { // go to bottom of list KeyCode::Char('G') => app.pl_list.index = app.pl_list.list.len() - 1, - // Change playlist name - KeyCode::Char('e') => app.change_playlist_name()?, + // Playlist Rename + KeyCode::Char('R') => { + app.inputmode = InputMode::PlaylistRename; + } // add to current playlist KeyCode::Enter | KeyCode::Char('l') | KeyCode::Right | KeyCode::Char(' ') => { diff --git a/src/event/mod.rs b/src/event/mod.rs index e0f3b13..efc9b9e 100755 --- a/src/event/mod.rs +++ b/src/event/mod.rs @@ -3,3 +3,4 @@ pub mod handler; pub mod search_keys; pub mod pl_rename_keys; pub mod pl_append_keys; +pub mod new_pl_keys; diff --git a/src/event/new_pl_keys.rs b/src/event/new_pl_keys.rs new file mode 100644 index 0000000..75c9e97 --- /dev/null +++ b/src/event/new_pl_keys.rs @@ -0,0 +1,45 @@ +use crate::{ + app::{App, AppResult}, + ui::InputMode, +}; +use crossterm::event::{KeyCode, KeyEvent}; + +pub fn handle_new_pl_keys(key_event: KeyEvent, app: &mut App) -> AppResult<()> { + match key_event.code { + KeyCode::Esc => { + app.pl_new_pl_input.clear(); + app.reset_cursor(); + app.inputmode = InputMode::Normal; + } + KeyCode::Char(to_insert) => { + app.enter_char(to_insert); + } + KeyCode::Enter => { + let pl_name = &app.pl_new_pl_input; + + for song in app.pl_new_pl_songs_buffer.iter() { + app.conn.conn.pl_push(pl_name, song)?; + } + app.pl_new_pl_input.clear(); + + app.pl_list.list = App::get_playlist(&mut app.conn.conn)?; + app.reset_cursor(); + app.inputmode = InputMode::Normal; + } + + KeyCode::Backspace => { + app.delete_char(); + } + + KeyCode::Left => { + app.move_cursor_left(); + } + + KeyCode::Right => { + app.move_cursor_right(); + } + + _ => {} + } + Ok(()) +} diff --git a/src/event/pl_append_keys.rs b/src/event/pl_append_keys.rs index f56d597..0f7df81 100755 --- a/src/event/pl_append_keys.rs +++ b/src/event/pl_append_keys.rs @@ -1,4 +1,5 @@ use crate::app::{App, AppResult, SelectedTab}; +use crate::ui::InputMode; use crate::utils::FileExtension; use crossterm::event::{KeyCode, KeyEvent}; use std::path::Path; @@ -30,6 +31,10 @@ pub fn hande_pl_append_keys(key_event: KeyEvent, app: &mut App) -> AppResult<()> if *pl_name == "Current Playlist" { app.conn.conn.push(&song)?; app.update_queue(); + } else if *pl_name == "New Playlist" { + app.pl_new_pl_songs_buffer.clear(); + app.pl_new_pl_songs_buffer.push(song.clone()); + app.inputmode = InputMode::NewPlaylist; } else { app.conn.add_to_playlist(pl_name, &song)?; } @@ -47,12 +52,17 @@ pub fn hande_pl_append_keys(key_event: KeyEvent, app: &mut App) -> AppResult<()> if *pl_name == "Current Playlist" { app.conn.conn.push(&song)?; app.update_queue(); + } else if *pl_name == "New Playlist" { + app.pl_new_pl_songs_buffer.clear(); + app.pl_new_pl_songs_buffer.push(song.clone()); + app.inputmode = InputMode::NewPlaylist; } else { app.conn.add_to_playlist(pl_name, &song)?; } } } else if t == "directory" { let file = format!("{}/{}", app.browser.path, f); + app.pl_new_pl_songs_buffer.clear(); for (t, f) in app.conn.conn.listfiles(&file)?.iter() { // dir_vec.push((t, f)); if t == "file" @@ -65,6 +75,9 @@ pub fn hande_pl_append_keys(key_event: KeyEvent, app: &mut App) -> AppResult<()> let song = app.conn.get_song_with_only_filename(&full_path); if *pl_name == "Current Playlist" { app.conn.conn.push(&song)?; + } else if *pl_name == "New Playlist" { + app.pl_new_pl_songs_buffer.push(song.clone()); + app.inputmode = InputMode::NewPlaylist; } else { app.conn.add_to_playlist(pl_name, &song)?; } @@ -78,6 +91,8 @@ pub fn hande_pl_append_keys(key_event: KeyEvent, app: &mut App) -> AppResult<()> if *pl_name == "Current Playlist" { app.conn.load_playlist(playlist_name)?; app.update_queue(); + } else if *pl_name == "New Playlist" { + app.inputmode = InputMode::NewPlaylist; } else { let songs = app.conn.conn.playlist(playlist_name)?; for song in songs { diff --git a/src/ui.rs b/src/ui.rs index adf9d43..bd21e08 100755 --- a/src/ui.rs +++ b/src/ui.rs @@ -14,6 +14,7 @@ pub enum InputMode { Editing, Normal, PlaylistRename, + NewPlaylist, } /// Renders the user interface widgets @@ -40,6 +41,9 @@ pub fn render(app: &mut App, frame: &mut Frame) { InputMode::PlaylistRename => { draw_rename_playlist(frame, app, layout[1]); } + InputMode::NewPlaylist => { + draw_new_playlist(frame, app, layout[1]); + } } if app.playlist_popup { @@ -453,6 +457,26 @@ fn draw_rename_playlist(frame: &mut Frame, app: &mut App, area: Rect) { frame.render_widget(input, area); } +fn draw_new_playlist(frame: &mut Frame, app: &mut App, area: Rect) { + #[allow(clippy::cast_possible_truncation)] + frame.set_cursor( + // Draw the cursor at the current position in the input field. + // This position is can be controlled via the left and right arrow key + area.x + app.pl_new_pl_cursor_pos as u16 + 2, + // Move one line down, from the border to the input line + area.y + 1, + ); + + let input = Paragraph::new("/".to_string() + &app.pl_new_pl_input) + .style(Style::default()) + .block( + Block::default() + .borders(Borders::ALL) + .title("Enter New Playlist's Name: ".bold().green()), + ); + frame.render_widget(input, area); +} + fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect { let popup_layout = Layout::vertical([ Constraint::Percentage((100 - percent_y) / 2), diff --git a/todo.md b/todo.md deleted file mode 100644 index 390c3a2..0000000 --- a/todo.md +++ /dev/null @@ -1,2 +0,0 @@ -- [ ] create new playlist -- [ ] add to new playlist