Krolyxon
Commit 6da9e0c

rmptui6da9e0c

new feature: add to new playlist

Author
krolxon
Date
2024-06-12 18:13 +0530
Parent
5db16b3
Commit
6da9e0c8515dbe6860c03a9f38f5759c451705d0

+136 −18

8 files changed

  1. M README.md +2 −0
  2. M src/app.rs +42 −13
  3. M src/event/handler.rs +7 −3
  4. M src/event/mod.rs +1 −0
  5. A src/event/new_pl_keys.rs +45 −0
  6. M src/event/pl_append_keys.rs +15 −0
  7. M src/ui.rs +24 −0
  8. D todo.md +0 −2
M README.md View
@@ -38,6 +38,7 @@ rmptui is a minimal tui mpd client made with rust.
3838 | `r` | Toggle repeat |
3939 | `z` | Toggle random |
4040 | `/` | Search |
41+| `R` | Rename Playlist |
4142 | `g` | Go to top of list |
4243 | `G` | Go to bottom of list |
4344
@@ -54,4 +55,5 @@ rmptui is a minimal tui mpd client made with rust.
5455 - [x] metadata based tree view
5556 - [x] view playlist
5657 - [x] change playlist name
58+- [x] add to new playlist
5759 - [ ] add lyrics fetcher
M src/app.rs View
@@ -25,9 +25,14 @@ pub struct App {
2525 pub inputmode: InputMode, // Defines input mode, Normal or Search
2626 pub search_input: String, // Stores the userinput to be searched
2727 pub search_cursor_pos: usize, // Stores the cursor position for searching
28+
2829 pub pl_newname_input: String, // Stores the new name of the playlist
2930 pub pl_cursor_pos: usize, // Stores the cursor position for renaming playlist
3031
32+ pub pl_new_pl_input: String, // Stores the name of new playlist to be created
33+ pub pl_new_pl_cursor_pos: usize, // Stores the cursor position of new playlist to be created
34+ pub pl_new_pl_songs_buffer: Vec<Song>, // Buffer for songs that need to be added to the newly created playlist
35+
3136 // playlist variables
3237 // used to show playlist popup
3338 pub playlist_popup: bool,
@@ -80,6 +85,9 @@ impl App {
8085 search_cursor_pos: 0,
8186 pl_cursor_pos: 0,
8287 playlist_popup: false,
88+ pl_new_pl_input: String::new(),
89+ pl_new_pl_cursor_pos: 0,
90+ pl_new_pl_songs_buffer: Vec::new(),
8391 append_list,
8492 should_update_song_list: false,
8593 queue_state,
@@ -136,6 +144,7 @@ impl App {
136144
137145 pub fn get_append_list(conn: &mut Client) -> AppResult<ContentList<String>> {
138146 let mut list = ContentList::new();
147+ list.list.push("New Playlist".to_string());
139148 list.list.push("Current Playlist".to_string());
140149 for item in Self::get_playlist(conn)? {
141150 list.list.push(item.to_string());
@@ -286,6 +295,10 @@ impl App {
286295 let cursor_moved_left = self.search_cursor_pos.saturating_sub(1);
287296 self.search_cursor_pos = self.clamp_cursor(cursor_moved_left);
288297 }
298+ InputMode::NewPlaylist => {
299+ let cursor_moved_left = self.pl_new_pl_cursor_pos.saturating_sub(1);
300+ self.pl_new_pl_cursor_pos = self.clamp_cursor(cursor_moved_left);
301+ }
289302 _ => {}
290303 }
291304 }
@@ -300,6 +313,12 @@ impl App {
300313 let cursor_moved_right = self.search_cursor_pos.saturating_add(1);
301314 self.search_cursor_pos = self.clamp_cursor(cursor_moved_right);
302315 }
316+
317+ InputMode::NewPlaylist => {
318+ let cursor_moved_right = self.pl_new_pl_cursor_pos.saturating_add(1);
319+ self.pl_new_pl_cursor_pos = self.clamp_cursor(cursor_moved_right);
320+ }
321+
303322 _ => {}
304323 }
305324 }
@@ -308,19 +327,24 @@ impl App {
308327 match self.inputmode {
309328 InputMode::PlaylistRename => {
310329 self.pl_newname_input.insert(self.pl_cursor_pos, new_char);
311− self.move_cursor_right();
330+ }
331+ InputMode::NewPlaylist => {
332+ self.pl_new_pl_input
333+ .insert(self.pl_new_pl_cursor_pos, new_char);
312334 }
313335 InputMode::Editing => {
314336 self.search_input.insert(self.search_cursor_pos, new_char);
315− self.move_cursor_right();
316337 }
317338 _ => {}
318339 }
340+
341+ self.move_cursor_right();
319342 }
320343
321344 pub fn delete_char(&mut self) {
322345 let is_not_cursor_leftmost = match self.inputmode {
323346 InputMode::PlaylistRename => self.pl_cursor_pos != 0,
347+ InputMode::NewPlaylist => self.pl_new_pl_cursor_pos != 0,
324348 InputMode::Editing => self.search_cursor_pos != 0,
325349 _ => false,
326350 };
@@ -333,27 +357,35 @@ impl App {
333357 let current_index = match self.inputmode {
334358 InputMode::Editing => self.search_cursor_pos,
335359 InputMode::PlaylistRename => self.pl_cursor_pos,
360+ InputMode::NewPlaylist => self.pl_new_pl_cursor_pos,
336361 _ => 0,
337362 };
338363
339364 let from_left_to_current_index = current_index - 1;
340365
341366 if self.inputmode == InputMode::PlaylistRename {
342− // Getting all characters before the selected character.
343367 let before_char_to_delete = self
344368 .pl_newname_input
345369 .chars()
346370 .take(from_left_to_current_index);
347− // Getting all characters after selected character.
348371 let after_char_to_delete = self.pl_newname_input.chars().skip(current_index);
349− // Put all characters together except the selected one.
350− // By leaving the selected one out, it is forgotten and therefore deleted.
372+
351373 self.pl_newname_input = before_char_to_delete.chain(after_char_to_delete).collect();
352374 self.move_cursor_left();
375+ } else if self.inputmode == InputMode::NewPlaylist {
376+ let before_char_to_delete = self
377+ .pl_new_pl_input
378+ .chars()
379+ .take(from_left_to_current_index);
380+ let after_char_to_delete = self.pl_new_pl_input.chars().skip(current_index);
381+
382+ self.pl_new_pl_input = before_char_to_delete.chain(after_char_to_delete).collect();
383+ self.move_cursor_left();
353384 } else if self.inputmode == InputMode::Editing {
354385 let before_char_to_delete =
355386 self.search_input.chars().take(from_left_to_current_index);
356387 let after_char_to_delete = self.search_input.chars().skip(current_index);
388+
357389 self.search_input = before_char_to_delete.chain(after_char_to_delete).collect();
358390 self.move_cursor_left();
359391 }
@@ -363,6 +395,7 @@ impl App {
363395 pub fn clamp_cursor(&self, new_cursor_pos: usize) -> usize {
364396 match self.inputmode {
365397 InputMode::PlaylistRename => new_cursor_pos.clamp(0, self.pl_newname_input.len()),
398+ InputMode::NewPlaylist => new_cursor_pos.clamp(0, self.pl_new_pl_input.len()),
366399 InputMode::Editing => new_cursor_pos.clamp(0, self.search_input.len()),
367400 _ => 0,
368401 }
@@ -376,6 +409,9 @@ impl App {
376409 InputMode::PlaylistRename => {
377410 self.pl_cursor_pos = 0;
378411 }
412+ InputMode::NewPlaylist => {
413+ self.pl_new_pl_cursor_pos = 0;
414+ }
379415 _ => {}
380416 }
381417 }
@@ -393,13 +429,6 @@ impl App {
393429 }
394430 }
395431
396− pub fn change_playlist_name(&mut self) -> AppResult<()> {
397− if self.selected_tab == SelectedTab::Playlists {
398− self.inputmode = InputMode::PlaylistRename;
399− }
400− Ok(())
401− }
402−
403432 // Mouse event handlers
404433 pub fn handle_scroll_up(&mut self) {
405434 match self.selected_tab {
M src/event/handler.rs View
@@ -6,7 +6,7 @@ use crate::{
66 use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseEvent, MouseEventKind};
77 use std::time::Duration;
88
9−use super::{pl_append_keys, pl_rename_keys, search_keys};
9+use super::{pl_append_keys, pl_rename_keys, new_pl_keys, search_keys};
1010
1111 pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
1212 // searching, playlist renaming, playlist appending
@@ -14,6 +14,8 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
1414 search_keys::handle_search_keys(key_event, app)?;
1515 } else if app.inputmode == InputMode::PlaylistRename {
1616 pl_rename_keys::handle_pl_rename_keys(key_event, app)?;
17+ } else if app.inputmode == InputMode::NewPlaylist {
18+ new_pl_keys::handle_new_pl_keys(key_event, app)?;
1719 } else if app.playlist_popup {
1820 pl_append_keys::hande_pl_append_keys(key_event, app)?;
1921 } else {
@@ -295,8 +297,10 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
295297 // go to bottom of list
296298 KeyCode::Char('G') => app.pl_list.index = app.pl_list.list.len() - 1,
297299
298− // Change playlist name
299− KeyCode::Char('e') => app.change_playlist_name()?,
300+ // Playlist Rename
301+ KeyCode::Char('R') => {
302+ app.inputmode = InputMode::PlaylistRename;
303+ }
300304
301305 // add to current playlist
302306 KeyCode::Enter | KeyCode::Char('l') | KeyCode::Right | KeyCode::Char(' ') => {
M src/event/mod.rs View
@@ -3,3 +3,4 @@ pub mod handler;
33 pub mod search_keys;
44 pub mod pl_rename_keys;
55 pub mod pl_append_keys;
6+pub mod new_pl_keys;
A src/event/new_pl_keys.rs View
@@ -0,0 +1,45 @@
1+use crate::{
2+ app::{App, AppResult},
3+ ui::InputMode,
4+};
5+use crossterm::event::{KeyCode, KeyEvent};
6+
7+pub fn handle_new_pl_keys(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
8+ match key_event.code {
9+ KeyCode::Esc => {
10+ app.pl_new_pl_input.clear();
11+ app.reset_cursor();
12+ app.inputmode = InputMode::Normal;
13+ }
14+ KeyCode::Char(to_insert) => {
15+ app.enter_char(to_insert);
16+ }
17+ KeyCode::Enter => {
18+ let pl_name = &app.pl_new_pl_input;
19+
20+ for song in app.pl_new_pl_songs_buffer.iter() {
21+ app.conn.conn.pl_push(pl_name, song)?;
22+ }
23+ app.pl_new_pl_input.clear();
24+
25+ app.pl_list.list = App::get_playlist(&mut app.conn.conn)?;
26+ app.reset_cursor();
27+ app.inputmode = InputMode::Normal;
28+ }
29+
30+ KeyCode::Backspace => {
31+ app.delete_char();
32+ }
33+
34+ KeyCode::Left => {
35+ app.move_cursor_left();
36+ }
37+
38+ KeyCode::Right => {
39+ app.move_cursor_right();
40+ }
41+
42+ _ => {}
43+ }
44+ Ok(())
45+}
M src/event/pl_append_keys.rs View
@@ -1,4 +1,5 @@
11 use crate::app::{App, AppResult, SelectedTab};
2+use crate::ui::InputMode;
23 use crate::utils::FileExtension;
34 use crossterm::event::{KeyCode, KeyEvent};
45 use std::path::Path;
@@ -30,6 +31,10 @@ pub fn hande_pl_append_keys(key_event: KeyEvent, app: &mut App) -> AppResult<()>
3031 if *pl_name == "Current Playlist" {
3132 app.conn.conn.push(&song)?;
3233 app.update_queue();
34+ } else if *pl_name == "New Playlist" {
35+ app.pl_new_pl_songs_buffer.clear();
36+ app.pl_new_pl_songs_buffer.push(song.clone());
37+ app.inputmode = InputMode::NewPlaylist;
3338 } else {
3439 app.conn.add_to_playlist(pl_name, &song)?;
3540 }
@@ -47,12 +52,17 @@ pub fn hande_pl_append_keys(key_event: KeyEvent, app: &mut App) -> AppResult<()>
4752 if *pl_name == "Current Playlist" {
4853 app.conn.conn.push(&song)?;
4954 app.update_queue();
55+ } else if *pl_name == "New Playlist" {
56+ app.pl_new_pl_songs_buffer.clear();
57+ app.pl_new_pl_songs_buffer.push(song.clone());
58+ app.inputmode = InputMode::NewPlaylist;
5059 } else {
5160 app.conn.add_to_playlist(pl_name, &song)?;
5261 }
5362 }
5463 } else if t == "directory" {
5564 let file = format!("{}/{}", app.browser.path, f);
65+ app.pl_new_pl_songs_buffer.clear();
5666 for (t, f) in app.conn.conn.listfiles(&file)?.iter() {
5767 // dir_vec.push((t, f));
5868 if t == "file"
@@ -65,6 +75,9 @@ pub fn hande_pl_append_keys(key_event: KeyEvent, app: &mut App) -> AppResult<()>
6575 let song = app.conn.get_song_with_only_filename(&full_path);
6676 if *pl_name == "Current Playlist" {
6777 app.conn.conn.push(&song)?;
78+ } else if *pl_name == "New Playlist" {
79+ app.pl_new_pl_songs_buffer.push(song.clone());
80+ app.inputmode = InputMode::NewPlaylist;
6881 } else {
6982 app.conn.add_to_playlist(pl_name, &song)?;
7083 }
@@ -78,6 +91,8 @@ pub fn hande_pl_append_keys(key_event: KeyEvent, app: &mut App) -> AppResult<()>
7891 if *pl_name == "Current Playlist" {
7992 app.conn.load_playlist(playlist_name)?;
8093 app.update_queue();
94+ } else if *pl_name == "New Playlist" {
95+ app.inputmode = InputMode::NewPlaylist;
8196 } else {
8297 let songs = app.conn.conn.playlist(playlist_name)?;
8398 for song in songs {
M src/ui.rs View
@@ -14,6 +14,7 @@ pub enum InputMode {
1414 Editing,
1515 Normal,
1616 PlaylistRename,
17+ NewPlaylist,
1718 }
1819
1920 /// Renders the user interface widgets
@@ -40,6 +41,9 @@ pub fn render(app: &mut App, frame: &mut Frame) {
4041 InputMode::PlaylistRename => {
4142 draw_rename_playlist(frame, app, layout[1]);
4243 }
44+ InputMode::NewPlaylist => {
45+ draw_new_playlist(frame, app, layout[1]);
46+ }
4347 }
4448
4549 if app.playlist_popup {
@@ -453,6 +457,26 @@ fn draw_rename_playlist(frame: &mut Frame, app: &mut App, area: Rect) {
453457 frame.render_widget(input, area);
454458 }
455459
460+fn draw_new_playlist(frame: &mut Frame, app: &mut App, area: Rect) {
461+ #[allow(clippy::cast_possible_truncation)]
462+ frame.set_cursor(
463+ // Draw the cursor at the current position in the input field.
464+ // This position is can be controlled via the left and right arrow key
465+ area.x + app.pl_new_pl_cursor_pos as u16 + 2,
466+ // Move one line down, from the border to the input line
467+ area.y + 1,
468+ );
469+
470+ let input = Paragraph::new("/".to_string() + &app.pl_new_pl_input)
471+ .style(Style::default())
472+ .block(
473+ Block::default()
474+ .borders(Borders::ALL)
475+ .title("Enter New Playlist's Name: ".bold().green()),
476+ );
477+ frame.render_widget(input, area);
478+}
479+
456480 fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
457481 let popup_layout = Layout::vertical([
458482 Constraint::Percentage((100 - percent_y) / 2),
D todo.md View
@@ -1,2 +0,0 @@
1−- [ ] create new playlist
2−- [ ] add to new playlist