Compare commits
8 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
5e61e0fad5 | |
|
|
9941e1e42b | |
|
|
df10c2cf63 | |
|
|
f2b9051489 | |
|
|
5795d00831 | |
|
|
e1dc925ecc | |
|
|
3862d5d324 | |
|
|
65f76181d3 |
|
|
@ -0,0 +1,4 @@
|
||||||
|
## rmptui-v0.1.6
|
||||||
|
- Add songs to new playlist feature
|
||||||
|
- Code refactoring
|
||||||
|
- fix #9
|
||||||
|
|
@ -282,7 +282,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rmptui"
|
name = "rmptui"
|
||||||
version = "0.1.5"
|
version = "0.1.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"mpd",
|
"mpd",
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ description = """
|
||||||
a fast and minimal tui mpd client
|
a fast and minimal tui mpd client
|
||||||
"""
|
"""
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
version = "0.1.5"
|
version = "0.1.6"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
repository = "https://github.com/krolyxon/rmptui"
|
repository = "https://github.com/krolyxon/rmptui"
|
||||||
keywords = ["rmptui", "mpd", "music", "cli", "tui", "client"]
|
keywords = ["rmptui", "mpd", "music", "cli", "tui", "client"]
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ rmptui is a minimal tui mpd client made with rust.
|
||||||
| `r` | Toggle repeat |
|
| `r` | Toggle repeat |
|
||||||
| `z` | Toggle random |
|
| `z` | Toggle random |
|
||||||
| `/` | Search |
|
| `/` | Search |
|
||||||
|
| `R` | Rename Playlist |
|
||||||
| `g` | Go to top of list |
|
| `g` | Go to top of list |
|
||||||
| `G` | Go to bottom 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] metadata based tree view
|
||||||
- [x] view playlist
|
- [x] view playlist
|
||||||
- [x] change playlist name
|
- [x] change playlist name
|
||||||
|
- [x] add to new playlist
|
||||||
- [ ] add lyrics fetcher
|
- [ ] add lyrics fetcher
|
||||||
|
|
|
||||||
77
src/app.rs
77
src/app.rs
|
|
@ -25,9 +25,14 @@ pub struct App {
|
||||||
pub inputmode: InputMode, // Defines input mode, Normal or Search
|
pub inputmode: InputMode, // Defines input mode, Normal or Search
|
||||||
pub search_input: String, // Stores the userinput to be searched
|
pub search_input: String, // Stores the userinput to be searched
|
||||||
pub search_cursor_pos: usize, // Stores the cursor position for searching
|
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_newname_input: String, // Stores the new name of the playlist
|
||||||
pub pl_cursor_pos: usize, // Stores the cursor position for renaming 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<Song>, // Buffer for songs that need to be added to the newly created playlist
|
||||||
|
|
||||||
// playlist variables
|
// playlist variables
|
||||||
// used to show playlist popup
|
// used to show playlist popup
|
||||||
pub playlist_popup: bool,
|
pub playlist_popup: bool,
|
||||||
|
|
@ -80,6 +85,9 @@ impl App {
|
||||||
search_cursor_pos: 0,
|
search_cursor_pos: 0,
|
||||||
pl_cursor_pos: 0,
|
pl_cursor_pos: 0,
|
||||||
playlist_popup: false,
|
playlist_popup: false,
|
||||||
|
pl_new_pl_input: String::new(),
|
||||||
|
pl_new_pl_cursor_pos: 0,
|
||||||
|
pl_new_pl_songs_buffer: Vec::new(),
|
||||||
append_list,
|
append_list,
|
||||||
should_update_song_list: false,
|
should_update_song_list: false,
|
||||||
queue_state,
|
queue_state,
|
||||||
|
|
@ -93,8 +101,7 @@ impl App {
|
||||||
self.update_queue();
|
self.update_queue();
|
||||||
|
|
||||||
// Deals with database update
|
// Deals with database update
|
||||||
if self.should_update_song_list {
|
if self.should_update_song_list && self.conn.status.updating_db.is_none() {
|
||||||
if let None = self.conn.status.updating_db {
|
|
||||||
// Update the songs list
|
// Update the songs list
|
||||||
self.conn.songs_filenames = self
|
self.conn.songs_filenames = self
|
||||||
.conn
|
.conn
|
||||||
|
|
@ -108,7 +115,6 @@ impl App {
|
||||||
|
|
||||||
self.should_update_song_list = false;
|
self.should_update_song_list = false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -137,6 +143,7 @@ impl App {
|
||||||
pub fn get_append_list(conn: &mut Client) -> AppResult<ContentList<String>> {
|
pub fn get_append_list(conn: &mut Client) -> AppResult<ContentList<String>> {
|
||||||
let mut list = ContentList::new();
|
let mut list = ContentList::new();
|
||||||
list.list.push("Current Playlist".to_string());
|
list.list.push("Current Playlist".to_string());
|
||||||
|
list.list.push("New Playlist".to_string());
|
||||||
for item in Self::get_playlist(conn)? {
|
for item in Self::get_playlist(conn)? {
|
||||||
list.list.push(item.to_string());
|
list.list.push(item.to_string());
|
||||||
}
|
}
|
||||||
|
|
@ -154,22 +161,22 @@ impl App {
|
||||||
let file = format!("{}/{}", self.browser.path, content);
|
let file = format!("{}/{}", self.browser.path, content);
|
||||||
let songs = self.conn.conn.listfiles(&file).unwrap_or_default();
|
let songs = self.conn.conn.listfiles(&file).unwrap_or_default();
|
||||||
for (t, f) in songs.iter() {
|
for (t, f) in songs.iter() {
|
||||||
if t == "file" {
|
if t == "file"
|
||||||
if Path::new(&f).has_extension(&[
|
&& Path::new(&f).has_extension(&[
|
||||||
"mp3", "ogg", "flac", "m4a", "wav", "aac", "opus", "ape", "wma",
|
"mp3", "ogg", "flac", "m4a", "wav", "aac", "opus", "ape", "wma",
|
||||||
"mpc", "aiff", "dff", "mp2", "mka",
|
"mpc", "aiff", "dff", "mp2", "mka",
|
||||||
]) {
|
])
|
||||||
|
{
|
||||||
let path = file.clone() + "/" + f;
|
let path = file.clone() + "/" + f;
|
||||||
let full_path = path.strip_prefix("./").unwrap_or_else(|| "");
|
let full_path = path.strip_prefix("./").unwrap_or("");
|
||||||
let song = self.conn.get_song_with_only_filename(&full_path);
|
let song = self.conn.get_song_with_only_filename(full_path);
|
||||||
self.conn.conn.push(&song)?;
|
self.conn.conn.push(&song)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else if content_type == "file" {
|
} else if content_type == "file" {
|
||||||
let mut status = false;
|
let mut status = false;
|
||||||
for (i, song) in self.queue_list.list.clone().iter().enumerate() {
|
for (i, song) in self.queue_list.list.clone().iter().enumerate() {
|
||||||
let song_path = song.file.split("/").last().unwrap_or_default();
|
let song_path = song.file.split('/').last().unwrap_or_default();
|
||||||
if song_path.eq(content) {
|
if song_path.eq(content) {
|
||||||
self.conn.conn.delete(i as u32).unwrap();
|
self.conn.conn.delete(i as u32).unwrap();
|
||||||
status = true;
|
status = true;
|
||||||
|
|
@ -244,7 +251,7 @@ impl App {
|
||||||
let (t, path) = browser.filetree.get(browser.selected).unwrap();
|
let (t, path) = browser.filetree.get(browser.selected).unwrap();
|
||||||
if t == "directory" {
|
if t == "directory" {
|
||||||
if path != "." {
|
if path != "." {
|
||||||
browser.prev_path = browser.path.clone();
|
browser.prev_path.clone_from(&browser.path);
|
||||||
browser.path = browser.prev_path.clone() + "/" + path;
|
browser.path = browser.prev_path.clone() + "/" + path;
|
||||||
browser.update_directory(&mut self.conn)?;
|
browser.update_directory(&mut self.conn)?;
|
||||||
browser.prev_selected = browser.selected;
|
browser.prev_selected = browser.selected;
|
||||||
|
|
@ -252,7 +259,7 @@ impl App {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let index = self.queue_list.list.iter().position(|x| {
|
let index = self.queue_list.list.iter().position(|x| {
|
||||||
let file = x.file.split("/").last().unwrap_or_default();
|
let file = x.file.split('/').last().unwrap_or_default();
|
||||||
file.eq(path)
|
file.eq(path)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -286,6 +293,10 @@ impl App {
|
||||||
let cursor_moved_left = self.search_cursor_pos.saturating_sub(1);
|
let cursor_moved_left = self.search_cursor_pos.saturating_sub(1);
|
||||||
self.search_cursor_pos = self.clamp_cursor(cursor_moved_left);
|
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 +311,12 @@ impl App {
|
||||||
let cursor_moved_right = self.search_cursor_pos.saturating_add(1);
|
let cursor_moved_right = self.search_cursor_pos.saturating_add(1);
|
||||||
self.search_cursor_pos = self.clamp_cursor(cursor_moved_right);
|
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 +325,24 @@ impl App {
|
||||||
match self.inputmode {
|
match self.inputmode {
|
||||||
InputMode::PlaylistRename => {
|
InputMode::PlaylistRename => {
|
||||||
self.pl_newname_input.insert(self.pl_cursor_pos, new_char);
|
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 => {
|
InputMode::Editing => {
|
||||||
self.search_input.insert(self.search_cursor_pos, new_char);
|
self.search_input.insert(self.search_cursor_pos, new_char);
|
||||||
self.move_cursor_right();
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.move_cursor_right();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_char(&mut self) {
|
pub fn delete_char(&mut self) {
|
||||||
let is_not_cursor_leftmost = match self.inputmode {
|
let is_not_cursor_leftmost = match self.inputmode {
|
||||||
InputMode::PlaylistRename => self.pl_cursor_pos != 0,
|
InputMode::PlaylistRename => self.pl_cursor_pos != 0,
|
||||||
|
InputMode::NewPlaylist => self.pl_new_pl_cursor_pos != 0,
|
||||||
InputMode::Editing => self.search_cursor_pos != 0,
|
InputMode::Editing => self.search_cursor_pos != 0,
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
@ -333,27 +355,35 @@ impl App {
|
||||||
let current_index = match self.inputmode {
|
let current_index = match self.inputmode {
|
||||||
InputMode::Editing => self.search_cursor_pos,
|
InputMode::Editing => self.search_cursor_pos,
|
||||||
InputMode::PlaylistRename => self.pl_cursor_pos,
|
InputMode::PlaylistRename => self.pl_cursor_pos,
|
||||||
|
InputMode::NewPlaylist => self.pl_new_pl_cursor_pos,
|
||||||
_ => 0,
|
_ => 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let from_left_to_current_index = current_index - 1;
|
let from_left_to_current_index = current_index - 1;
|
||||||
|
|
||||||
if self.inputmode == InputMode::PlaylistRename {
|
if self.inputmode == InputMode::PlaylistRename {
|
||||||
// Getting all characters before the selected character.
|
|
||||||
let before_char_to_delete = self
|
let before_char_to_delete = self
|
||||||
.pl_newname_input
|
.pl_newname_input
|
||||||
.chars()
|
.chars()
|
||||||
.take(from_left_to_current_index);
|
.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);
|
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.pl_newname_input = before_char_to_delete.chain(after_char_to_delete).collect();
|
||||||
self.move_cursor_left();
|
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 {
|
} else if self.inputmode == InputMode::Editing {
|
||||||
let before_char_to_delete =
|
let before_char_to_delete =
|
||||||
self.search_input.chars().take(from_left_to_current_index);
|
self.search_input.chars().take(from_left_to_current_index);
|
||||||
let after_char_to_delete = self.search_input.chars().skip(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.search_input = before_char_to_delete.chain(after_char_to_delete).collect();
|
||||||
self.move_cursor_left();
|
self.move_cursor_left();
|
||||||
}
|
}
|
||||||
|
|
@ -363,6 +393,7 @@ impl App {
|
||||||
pub fn clamp_cursor(&self, new_cursor_pos: usize) -> usize {
|
pub fn clamp_cursor(&self, new_cursor_pos: usize) -> usize {
|
||||||
match self.inputmode {
|
match self.inputmode {
|
||||||
InputMode::PlaylistRename => new_cursor_pos.clamp(0, self.pl_newname_input.len()),
|
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()),
|
InputMode::Editing => new_cursor_pos.clamp(0, self.search_input.len()),
|
||||||
_ => 0,
|
_ => 0,
|
||||||
}
|
}
|
||||||
|
|
@ -376,6 +407,9 @@ impl App {
|
||||||
InputMode::PlaylistRename => {
|
InputMode::PlaylistRename => {
|
||||||
self.pl_cursor_pos = 0;
|
self.pl_cursor_pos = 0;
|
||||||
}
|
}
|
||||||
|
InputMode::NewPlaylist => {
|
||||||
|
self.pl_new_pl_cursor_pos = 0;
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -393,13 +427,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
|
// Mouse event handlers
|
||||||
pub fn handle_scroll_up(&mut self) {
|
pub fn handle_scroll_up(&mut self) {
|
||||||
match self.selected_tab {
|
match self.selected_tab {
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ impl FileBrowser {
|
||||||
let mut file_vec: Vec<(String, String)> = vec![];
|
let mut file_vec: Vec<(String, String)> = vec![];
|
||||||
let mut dir_vec: Vec<(String, String)> = vec![];
|
let mut dir_vec: Vec<(String, String)> = vec![];
|
||||||
for (t, f) in conn.conn.listfiles(self.path.as_str())?.into_iter() {
|
for (t, f) in conn.conn.listfiles(self.path.as_str())?.into_iter() {
|
||||||
if t == "directory" && !f.starts_with(".") {
|
if t == "directory" && !f.starts_with('.') {
|
||||||
dir_vec.push((t, f));
|
dir_vec.push((t, f));
|
||||||
} else if t == "file"
|
} else if t == "file"
|
||||||
&& Path::new(&f).has_extension(&[
|
&& Path::new(&f).has_extension(&[
|
||||||
|
|
@ -73,7 +73,7 @@ impl FileBrowser {
|
||||||
.lsinfo(Song {
|
.lsinfo(Song {
|
||||||
file: (self.path.clone() + "/" + song)
|
file: (self.path.clone() + "/" + song)
|
||||||
.strip_prefix("./")
|
.strip_prefix("./")
|
||||||
.unwrap_or_else(|| "")
|
.unwrap_or("")
|
||||||
.to_string(),
|
.to_string(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
|
|
@ -121,7 +121,7 @@ impl FileBrowser {
|
||||||
self.update_directory(conn)?;
|
self.update_directory(conn)?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.path = self.prev_path.clone();
|
self.path.clone_from(&self.prev_path);
|
||||||
self.update_directory(conn)?;
|
self.update_directory(conn)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,11 +50,7 @@ impl Connection {
|
||||||
.unwrap_or_else(|_| Some(empty_song.clone()))
|
.unwrap_or_else(|_| Some(empty_song.clone()))
|
||||||
.unwrap_or(empty_song);
|
.unwrap_or(empty_song);
|
||||||
|
|
||||||
let volume_status = if status.volume == 0 {
|
let volume_status = VolumeStatus::Unmuted;
|
||||||
VolumeStatus::Muted(status.volume)
|
|
||||||
} else {
|
|
||||||
VolumeStatus::Unmuted
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
conn,
|
conn,
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseEvent, MouseEventKind};
|
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseEvent, MouseEventKind};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use super::{pl_append_keys, pl_rename_keys, search_keys};
|
use super::{new_pl_keys, pl_append_keys, pl_rename_keys, search_keys};
|
||||||
|
|
||||||
pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
|
pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
|
||||||
// searching, playlist renaming, playlist appending
|
// 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)?;
|
search_keys::handle_search_keys(key_event, app)?;
|
||||||
} else if app.inputmode == InputMode::PlaylistRename {
|
} else if app.inputmode == InputMode::PlaylistRename {
|
||||||
pl_rename_keys::handle_pl_rename_keys(key_event, app)?;
|
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 {
|
} else if app.playlist_popup {
|
||||||
pl_append_keys::hande_pl_append_keys(key_event, app)?;
|
pl_append_keys::hande_pl_append_keys(key_event, app)?;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -295,8 +297,10 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
|
||||||
// go to bottom of list
|
// go to bottom of list
|
||||||
KeyCode::Char('G') => app.pl_list.index = app.pl_list.list.len() - 1,
|
KeyCode::Char('G') => app.pl_list.index = app.pl_list.list.len() - 1,
|
||||||
|
|
||||||
// Change playlist name
|
// Playlist Rename
|
||||||
KeyCode::Char('e') => app.change_playlist_name()?,
|
KeyCode::Char('R') => {
|
||||||
|
app.inputmode = InputMode::PlaylistRename;
|
||||||
|
}
|
||||||
|
|
||||||
// add to current playlist
|
// add to current playlist
|
||||||
KeyCode::Enter | KeyCode::Char('l') | KeyCode::Right | KeyCode::Char(' ') => {
|
KeyCode::Enter | KeyCode::Char('l') | KeyCode::Right | KeyCode::Char(' ') => {
|
||||||
|
|
@ -321,9 +325,8 @@ pub fn handle_mouse_events(mouse_event: MouseEvent, app: &mut App) -> AppResult<
|
||||||
MouseEventKind::ScrollDown => app.handle_scroll_down(),
|
MouseEventKind::ScrollDown => app.handle_scroll_down(),
|
||||||
MouseEventKind::Down(button) => {
|
MouseEventKind::Down(button) => {
|
||||||
let (x, y) = (mouse_event.column, mouse_event.row);
|
let (x, y) = (mouse_event.column, mouse_event.row);
|
||||||
match button {
|
if button == crossterm::event::MouseButton::Left {
|
||||||
crossterm::event::MouseButton::Left => app.handle_mouse_left_click(x, y)?,
|
app.handle_mouse_left_click(x, y)?;
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
@ -3,3 +3,4 @@ pub mod handler;
|
||||||
pub mod search_keys;
|
pub mod search_keys;
|
||||||
pub mod pl_rename_keys;
|
pub mod pl_rename_keys;
|
||||||
pub mod pl_append_keys;
|
pub mod pl_append_keys;
|
||||||
|
pub mod new_pl_keys;
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
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.append_list = App::get_append_list(&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(())
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::app::{App, AppResult, SelectedTab};
|
use crate::app::{App, AppResult, SelectedTab};
|
||||||
|
use crate::ui::InputMode;
|
||||||
use crate::utils::FileExtension;
|
use crate::utils::FileExtension;
|
||||||
use crossterm::event::{KeyCode, KeyEvent};
|
use crossterm::event::{KeyCode, KeyEvent};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
@ -28,10 +29,14 @@ pub fn hande_pl_append_keys(key_event: KeyEvent, app: &mut App) -> AppResult<()>
|
||||||
let option_song = songs.first();
|
let option_song = songs.first();
|
||||||
if let Some(song) = option_song {
|
if let Some(song) = option_song {
|
||||||
if *pl_name == "Current Playlist" {
|
if *pl_name == "Current Playlist" {
|
||||||
app.conn.conn.push(&song)?;
|
app.conn.conn.push(song)?;
|
||||||
app.update_queue();
|
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 {
|
} else {
|
||||||
app.conn.add_to_playlist(pl_name, &song)?;
|
app.conn.add_to_playlist(pl_name, song)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -42,17 +47,22 @@ pub fn hande_pl_append_keys(key_event: KeyEvent, app: &mut App) -> AppResult<()>
|
||||||
if t == "file" {
|
if t == "file" {
|
||||||
let short_path = f;
|
let short_path = f;
|
||||||
if let Some(full_path) = app.conn.get_full_path(short_path) {
|
if let Some(full_path) = app.conn.get_full_path(short_path) {
|
||||||
let song = app.conn.get_song_with_only_filename(&full_path);
|
let song = app.conn.get_song_with_only_filename(full_path);
|
||||||
|
|
||||||
if *pl_name == "Current Playlist" {
|
if *pl_name == "Current Playlist" {
|
||||||
app.conn.conn.push(&song)?;
|
app.conn.conn.push(&song)?;
|
||||||
app.update_queue();
|
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 {
|
} else {
|
||||||
app.conn.add_to_playlist(pl_name, &song)?;
|
app.conn.add_to_playlist(pl_name, &song)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if t == "directory" {
|
} else if t == "directory" {
|
||||||
let file = format!("{}/{}", app.browser.path, f);
|
let file = format!("{}/{}", app.browser.path, f);
|
||||||
|
app.pl_new_pl_songs_buffer.clear();
|
||||||
for (t, f) in app.conn.conn.listfiles(&file)?.iter() {
|
for (t, f) in app.conn.conn.listfiles(&file)?.iter() {
|
||||||
// dir_vec.push((t, f));
|
// dir_vec.push((t, f));
|
||||||
if t == "file"
|
if t == "file"
|
||||||
|
|
@ -62,9 +72,12 @@ pub fn hande_pl_append_keys(key_event: KeyEvent, app: &mut App) -> AppResult<()>
|
||||||
])
|
])
|
||||||
{
|
{
|
||||||
let full_path = app.conn.get_full_path(f).unwrap_or_default();
|
let full_path = app.conn.get_full_path(f).unwrap_or_default();
|
||||||
let song = app.conn.get_song_with_only_filename(&full_path);
|
let song = app.conn.get_song_with_only_filename(full_path);
|
||||||
if *pl_name == "Current Playlist" {
|
if *pl_name == "Current Playlist" {
|
||||||
app.conn.conn.push(&song)?;
|
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 {
|
} else {
|
||||||
app.conn.add_to_playlist(pl_name, &song)?;
|
app.conn.add_to_playlist(pl_name, &song)?;
|
||||||
}
|
}
|
||||||
|
|
@ -78,14 +91,14 @@ pub fn hande_pl_append_keys(key_event: KeyEvent, app: &mut App) -> AppResult<()>
|
||||||
if *pl_name == "Current Playlist" {
|
if *pl_name == "Current Playlist" {
|
||||||
app.conn.load_playlist(playlist_name)?;
|
app.conn.load_playlist(playlist_name)?;
|
||||||
app.update_queue();
|
app.update_queue();
|
||||||
|
} else if *pl_name == "New Playlist" {
|
||||||
|
app.inputmode = InputMode::NewPlaylist;
|
||||||
} else {
|
} else {
|
||||||
let songs = app.conn.conn.playlist(playlist_name)?;
|
let songs = app.conn.conn.playlist(playlist_name)?;
|
||||||
for song in songs {
|
for song in songs {
|
||||||
// We ignore the Err() since there could be songs in playlists, which do not exist in the db anymore.
|
// We ignore the Err() since there could be songs in playlists, which do not exist in the db anymore.
|
||||||
// So instead of panicking, we just ignore if the song does not exists
|
// So instead of panicking, we just ignore if the song does not exists
|
||||||
app.conn
|
app.conn.add_to_playlist(pl_name, &song).unwrap_or(());
|
||||||
.add_to_playlist(*pl_name, &song)
|
|
||||||
.unwrap_or_else(|_| {});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -14,7 +14,7 @@ pub mod list;
|
||||||
pub mod browser;
|
pub mod browser;
|
||||||
|
|
||||||
/// Event Handler/ keymaps
|
/// Event Handler/ keymaps
|
||||||
pub mod event;
|
pub mod event_handler;
|
||||||
|
|
||||||
/// Application
|
/// Application
|
||||||
pub mod app;
|
pub mod app;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use ratatui::prelude::*;
|
use ratatui::prelude::*;
|
||||||
use rmptui::app::App;
|
use rmptui::app::App;
|
||||||
use rmptui::app::AppResult;
|
use rmptui::app::AppResult;
|
||||||
use rmptui::event::event::Event;
|
use rmptui::event_handler::event::Event;
|
||||||
use rmptui::event::event::EventHandler;
|
use rmptui::event_handler::event::EventHandler;
|
||||||
use rmptui::event::handler;
|
use rmptui::event_handler::handler;
|
||||||
use rmptui::tui;
|
use rmptui::tui;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use crossterm::terminal::{self, *};
|
||||||
use std::panic;
|
use std::panic;
|
||||||
|
|
||||||
use crate::app::{App, AppResult};
|
use crate::app::{App, AppResult};
|
||||||
use crate::event::event::EventHandler;
|
use crate::event_handler::event::EventHandler;
|
||||||
|
|
||||||
pub type CrosstermTerminal = ratatui::Terminal<ratatui::backend::CrosstermBackend<std::io::Stderr>>;
|
pub type CrosstermTerminal = ratatui::Terminal<ratatui::backend::CrosstermBackend<std::io::Stderr>>;
|
||||||
|
|
||||||
|
|
|
||||||
32
src/ui.rs
32
src/ui.rs
|
|
@ -14,6 +14,7 @@ pub enum InputMode {
|
||||||
Editing,
|
Editing,
|
||||||
Normal,
|
Normal,
|
||||||
PlaylistRename,
|
PlaylistRename,
|
||||||
|
NewPlaylist,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders the user interface widgets
|
/// Renders the user interface widgets
|
||||||
|
|
@ -40,6 +41,9 @@ pub fn render(app: &mut App, frame: &mut Frame) {
|
||||||
InputMode::PlaylistRename => {
|
InputMode::PlaylistRename => {
|
||||||
draw_rename_playlist(frame, app, layout[1]);
|
draw_rename_playlist(frame, app, layout[1]);
|
||||||
}
|
}
|
||||||
|
InputMode::NewPlaylist => {
|
||||||
|
draw_new_playlist(frame, app, layout[1]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if app.playlist_popup {
|
if app.playlist_popup {
|
||||||
|
|
@ -79,7 +83,7 @@ fn draw_directory_browser(frame: &mut Frame, app: &mut App, size: Rect) {
|
||||||
|
|
||||||
let mut status: bool = false;
|
let mut status: bool = false;
|
||||||
for sn in app.queue_list.list.iter() {
|
for sn in app.queue_list.list.iter() {
|
||||||
let file = sn.file.split("/").last().unwrap_or_default();
|
let file = sn.file.split('/').last().unwrap_or_default();
|
||||||
if file.eq(s) {
|
if file.eq(s) {
|
||||||
status = true;
|
status = true;
|
||||||
}
|
}
|
||||||
|
|
@ -133,7 +137,7 @@ fn draw_directory_browser(frame: &mut Frame, app: &mut App, size: Rect) {
|
||||||
.alignment(Alignment::Right)
|
.alignment(Alignment::Right)
|
||||||
}
|
}
|
||||||
VolumeStatus::Muted(_v) => {
|
VolumeStatus::Muted(_v) => {
|
||||||
Title::from(format!("Muted").red()).alignment(Alignment::Right)
|
Title::from("Muted".red()).alignment(Alignment::Right)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.borders(Borders::ALL),
|
.borders(Borders::ALL),
|
||||||
|
|
@ -231,7 +235,7 @@ fn draw_queue(frame: &mut Frame, app: &mut App, size: Rect) {
|
||||||
.alignment(Alignment::Right)
|
.alignment(Alignment::Right)
|
||||||
}
|
}
|
||||||
VolumeStatus::Muted(_v) => {
|
VolumeStatus::Muted(_v) => {
|
||||||
Title::from(format!("Muted").red()).alignment(Alignment::Right)
|
Title::from("Muted".red()).alignment(Alignment::Right)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.borders(Borders::ALL),
|
.borders(Borders::ALL),
|
||||||
|
|
@ -284,7 +288,7 @@ fn draw_progress_bar(frame: &mut Frame, app: &mut App, size: Rect) {
|
||||||
// Get the current playing state
|
// Get the current playing state
|
||||||
let mut state: String = String::new();
|
let mut state: String = String::new();
|
||||||
if !app.queue_list.list.is_empty() {
|
if !app.queue_list.list.is_empty() {
|
||||||
state = app.conn.state.clone();
|
state.clone_from(&app.conn.state);
|
||||||
state.push(':');
|
state.push(':');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -453,6 +457,26 @@ fn draw_rename_playlist(frame: &mut Frame, app: &mut App, area: Rect) {
|
||||||
frame.render_widget(input, area);
|
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 {
|
fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
|
||||||
let popup_layout = Layout::vertical([
|
let popup_layout = Layout::vertical([
|
||||||
Constraint::Percentage((100 - percent_y) / 2),
|
Constraint::Percentage((100 - percent_y) / 2),
|
||||||
|
|
|
||||||
Reference in New Issue