Did #0 (added context menu to side_nav), fixed some spelling errors, implemented deleting from manifest for both side_nav and song_list
This commit is contained in:
parent
84ab965b9d
commit
14c53d96c0
16
DEV.md
16
DEV.md
|
@ -6,11 +6,11 @@ Todo types:
|
||||||
[BUG] \[loc\](/src/...) - Bugfix, mandatory location
|
[BUG] \[loc\](/src/...) - Bugfix, mandatory location
|
||||||
[GIT] \[loc\](/src/...) - Git related feature, optional location
|
[GIT] \[loc\](/src/...) - Git related feature, optional location
|
||||||
|
|
||||||
Fixed todos have to add `**DONE**` prefix to the type
|
Todos that have been merged have to add `**DONE**` prefix to the type
|
||||||
|
|
||||||
### #0
|
### #0
|
||||||
[FEAT] - [side_nav](/src/ui/gui/components/side_nav.rs)
|
**DONE** ~~[FEAT] - [side_nav](/src/ui/gui/components/mod.rs)
|
||||||
Add dropdown menu for `side_nav` playlist
|
Add dropdown menu for `side_nav` playlist~~
|
||||||
|
|
||||||
### #1
|
### #1
|
||||||
[FEAT] - [gui](/src/ui/gui/)
|
[FEAT] - [gui](/src/ui/gui/)
|
||||||
|
@ -25,7 +25,7 @@ Better styling
|
||||||
Add music player footer
|
Add music player footer
|
||||||
|
|
||||||
### #4
|
### #4
|
||||||
[FEAT] - [gui](/src/ui/gui/components/song_list.rs)
|
[FEAT] - [gui](/src/ui/gui/components/song_list/mod.rs)
|
||||||
Add numbers to `song_list` table
|
Add numbers to `song_list` table
|
||||||
|
|
||||||
### #5
|
### #5
|
||||||
|
@ -33,7 +33,7 @@ Add numbers to `song_list` table
|
||||||
Add music player logic
|
Add music player logic
|
||||||
|
|
||||||
### #6
|
### #6
|
||||||
[FEAT] - [manifest](/src/manifest/)
|
[FEAT] - [manifest](/src/manifest/mod.rs)
|
||||||
Add support for images by possibly storing the images in json or custom format
|
Add support for images by possibly storing the images in json or custom format
|
||||||
|
|
||||||
### #7
|
### #7
|
||||||
|
@ -47,7 +47,7 @@ standalone one, moving default paths and using [#10](#10):
|
||||||
| music-output | `~/Music/mcmg/*` | `%userprofile%/Music/mcmg/*` |
|
| music-output | `~/Music/mcmg/*` | `%userprofile%/Music/mcmg/*` |
|
||||||
|
|
||||||
### #8
|
### #8
|
||||||
[FEAT] - [cli](/src/ui/cli/)
|
[FEAT] - [cli](/src/ui/cli/mod.rs)
|
||||||
add missing commands that are available via gui
|
add missing commands that are available via gui
|
||||||
- Downloading single songs, from the manifest and standalone as an utility
|
- Downloading single songs, from the manifest and standalone as an utility
|
||||||
|
|
||||||
|
@ -78,3 +78,7 @@ Add custom type for downloading, one for simple http downloads, and archived one
|
||||||
### #15
|
### #15
|
||||||
[FEAT] - [dependencies](/Cargo.toml)
|
[FEAT] - [dependencies](/Cargo.toml)
|
||||||
Clean up dependencies, remove unneeded features for executable size
|
Clean up dependencies, remove unneeded features for executable size
|
||||||
|
|
||||||
|
### #16
|
||||||
|
[FEAT] - [song_list](/src/ui/gui/components/song_list/mod.rs)
|
||||||
|
Add a checkmark or an X depending on if the song is downloaded to disk
|
||||||
|
|
|
@ -82,10 +82,10 @@ impl Downloader {
|
||||||
for (name, playlist) in manifest.get_playlists() {
|
for (name, playlist) in manifest.get_playlists() {
|
||||||
for (song_name, song) in playlist.get_songs() {
|
for (song_name, song) in playlist.get_songs() {
|
||||||
self.download_song(cfg, song_name, song, name, format)?;
|
self.download_song(cfg, song_name, song, name, format)?;
|
||||||
self.count += crate::process_manager::wait_for_procs_untill(10)?;
|
self.count += crate::process_manager::wait_for_procs_until(10)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.count += crate::process_manager::wait_for_procs_untill(0)?;
|
self.count += crate::process_manager::wait_for_procs_until(0)?;
|
||||||
Ok(self.count)
|
Ok(self.count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,12 @@ impl Manifest {
|
||||||
pub fn get_playlists_mut(&mut self) -> &mut HashMap<String, playlist::Playlist> {
|
pub fn get_playlists_mut(&mut self) -> &mut HashMap<String, playlist::Playlist> {
|
||||||
&mut self.playlists
|
&mut self.playlists
|
||||||
}
|
}
|
||||||
|
pub fn remove_playlist(&mut self, playlist_name: &String) -> Option<playlist::Playlist> {
|
||||||
|
self.playlists.remove(playlist_name)
|
||||||
|
}
|
||||||
|
pub fn remove_song(&mut self, playlist_name: &String, song_name: &String) -> Option<Song> {
|
||||||
|
self.get_playlist_mut(playlist_name)?.remove_song(song_name)
|
||||||
|
}
|
||||||
pub fn get_song_count(&self) -> usize {
|
pub fn get_song_count(&self) -> usize {
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
for v in self.playlists.values() {
|
for v in self.playlists.values() {
|
||||||
|
|
|
@ -65,7 +65,7 @@ pub fn purge_done_procs() -> usize {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Waits for processes to finish until the proc count is lower or equal to `max`
|
/// Waits for processes to finish until the proc count is lower or equal to `max`
|
||||||
pub fn wait_for_procs_untill(max: usize) -> anyhow::Result<usize> {
|
pub fn wait_for_procs_until(max: usize) -> anyhow::Result<usize> {
|
||||||
// NOTE: This looks really fucked because i dont want to deadlock the processes so i lock PROCESSES for as little as possible
|
// NOTE: This looks really fucked because i dont want to deadlock the processes so i lock PROCESSES for as little as possible
|
||||||
// NOTE: So its also kinda really slow
|
// NOTE: So its also kinda really slow
|
||||||
let mut finish_count = 0;
|
let mut finish_count = 0;
|
||||||
|
|
|
@ -27,7 +27,7 @@ pub fn song(cfg: &ConfigWrapper, manifest: &mut Manifest, downloader: &mut Downl
|
||||||
|
|
||||||
if should_download {
|
if should_download {
|
||||||
downloader.download_song(cfg, name, &song, playlist, manifest.get_format())?;
|
downloader.download_song(cfg, name, &song, playlist, manifest.get_format())?;
|
||||||
crate::process_manager::wait_for_procs_untill(0)?;
|
crate::process_manager::wait_for_procs_until(0)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
use egui::{Color32, RichText};
|
|
||||||
use crate::{manifest::song::Song, ui::gui::windows::{song_edit::GuiSongEditor, WindowIndex}};
|
|
||||||
|
|
||||||
pub struct ContextMenu;
|
|
||||||
|
|
||||||
// NOTE: This should be a component but theres no easy way to do that, so we just make it folow the
|
|
||||||
// trait manually, ish, more like a convention
|
|
||||||
impl /* ComponentUi for */ ContextMenu {
|
|
||||||
pub fn ui(gui: &mut crate::ui::gui::Gui, ui: &mut egui::Ui, pname: &String, sname: &String, song: &Song) {
|
|
||||||
if ui.button("Edit").clicked() {
|
|
||||||
let w = gui.windows.get_window::<GuiSongEditor>(WindowIndex::SongEdit);
|
|
||||||
w.set_active_song(pname, sname, song.get_url_str(), song.get_type());
|
|
||||||
gui.windows.open(WindowIndex::SongEdit, true);
|
|
||||||
ui.close_menu();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ui.button("Download").clicked() {
|
|
||||||
if let Err(e) = gui.downloader.download_song_nb(&gui.cfg, pname, sname, song, gui.manifest.get_format()) {
|
|
||||||
log::error!("{e}");
|
|
||||||
gui.throw_error(format!("Failed to download song {sname}: {e}"));
|
|
||||||
}
|
|
||||||
ui.close_menu();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ui.button("Open Source").clicked() {
|
|
||||||
if let Err(e) = open::that(song.get_url_str()) {
|
|
||||||
log::error!("{e}");
|
|
||||||
gui.throw_error(format!("Failed to open song source: {e}"));
|
|
||||||
}
|
|
||||||
ui.close_menu();
|
|
||||||
}
|
|
||||||
if ui.button("Play").clicked() {
|
|
||||||
let p = crate::util::get_song_path(pname, sname, gui.manifest.get_format());
|
|
||||||
|
|
||||||
if !p.exists() {
|
|
||||||
gui.throw_error("Song does not exist on disk".to_string());
|
|
||||||
} else if let Err(e) = open::that(p) {
|
|
||||||
log::error!("{e}");
|
|
||||||
gui.throw_error(format!("Failed to play song: {e}"));
|
|
||||||
}
|
|
||||||
ui.close_menu();
|
|
||||||
}
|
|
||||||
if ui.button("Delete from disk").clicked() {
|
|
||||||
let p = crate::util::get_song_path(pname, sname, gui.manifest.get_format());
|
|
||||||
if p.exists() {
|
|
||||||
if let Err(e) = std::fs::remove_file(p) {
|
|
||||||
gui.throw_error(format!("Failed to delete file: {e}"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ui.close_menu();
|
|
||||||
}
|
|
||||||
if ui.button(RichText::new("Delete").color(Color32::RED)).clicked() {
|
|
||||||
gui.throw_error("TODO");
|
|
||||||
ui.close_menu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,7 +3,6 @@ use super::Gui;
|
||||||
|
|
||||||
pub mod nav;
|
pub mod nav;
|
||||||
pub mod song_list;
|
pub mod song_list;
|
||||||
pub mod context_menu;
|
|
||||||
pub mod side_nav;
|
pub mod side_nav;
|
||||||
pub mod search_bar;
|
pub mod search_bar;
|
||||||
|
|
||||||
|
@ -18,3 +17,8 @@ pub trait ComponentUi {
|
||||||
pub trait ComponentUiMut {
|
pub trait ComponentUiMut {
|
||||||
fn ui(&mut self, gui: &mut Gui, ui: &mut egui::Ui);
|
fn ui(&mut self, gui: &mut Gui, ui: &mut egui::Ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait ComponentContextMenu {
|
||||||
|
type Data;
|
||||||
|
fn ui(gui: &mut Gui, ui: &mut egui::Ui, data: &Self::Data);
|
||||||
|
}
|
||||||
|
|
51
src/ui/gui/components/side_nav/context_menu.rs
Normal file
51
src/ui/gui/components/side_nav/context_menu.rs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
use egui::{Color32, RichText, TextBuffer};
|
||||||
|
|
||||||
|
use crate::ui::gui::{components::ComponentContextMenu, windows::{self, WindowIndex}};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ContextMenu;
|
||||||
|
|
||||||
|
impl ComponentContextMenu for ContextMenu {
|
||||||
|
type Data = String; // Playlist name
|
||||||
|
fn ui(gui: &mut crate::ui::gui::Gui, ui: &mut egui::Ui, playlist_name: &Self::Data) {
|
||||||
|
if ui.button("Edit").clicked() {
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ui.button("Download all").clicked() {
|
||||||
|
let Some(playlist) = gui.manifest.get_playlist(playlist_name) else {
|
||||||
|
gui.throw_error(&format!("Playlist not found: {}", playlist_name));
|
||||||
|
ui.close_menu();
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (song_name, song) in playlist.get_songs() {
|
||||||
|
if let Err(e) = gui.downloader.download_song_nb(&gui.cfg, &playlist_name, song_name, song, gui.manifest.get_format()) {
|
||||||
|
gui.throw_error(&format!("Could not download song: {e}"));
|
||||||
|
ui.close_menu();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ui.button("Delete from disk").clicked() {
|
||||||
|
let p = crate::util::get_playlist_path(playlist_name);
|
||||||
|
if p.exists() {
|
||||||
|
if let Err(e) = std::fs::remove_dir_all(p) {
|
||||||
|
gui.throw_error(format!("Failed to delete directory: {e}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
|
if ui.button(RichText::new("Delete").color(Color32::RED)).clicked() {
|
||||||
|
let w = gui.windows.get_window::<windows::confirm::ConfirmW>(WindowIndex::Confirm);
|
||||||
|
w.set_message(&"side_nav_playlist_manifest_delete", &"This will delete the playlist from the manifest file. This is NOT reversible", &vec![playlist_name.clone()]);
|
||||||
|
gui.windows.open(WindowIndex::Confirm, true);
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,9 @@
|
||||||
use std::borrow::BorrowMut;
|
use egui::{Color32, Label, RichText, Sense};
|
||||||
|
use crate::ui::gui::windows::{self, WindowIndex};
|
||||||
|
|
||||||
use egui::{Button, Color32, Label, RichText, Sense};
|
use super::{ComponentContextMenu, ComponentUi};
|
||||||
|
|
||||||
use super::ComponentUi;
|
mod context_menu;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,9 +23,10 @@ impl ComponentUi for SideNav {
|
||||||
gui.current_playlist = pname.to_string();
|
gui.current_playlist = pname.to_string();
|
||||||
}
|
}
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
|
|
||||||
let tint = Color32::from_hex("#333377").unwrap();
|
let tint = Color32::from_hex("#333377").unwrap();
|
||||||
ui.add(egui::Image::new(crate::data::NOTE_ICON).tint(tint));
|
ui.add(egui::Image::new(crate::data::NOTE_ICON).tint(tint))
|
||||||
|
.context_menu(|ui| context_menu::ContextMenu::ui(gui, ui, &pname));
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
let text;
|
let text;
|
||||||
if gui.current_playlist == *pname {
|
if gui.current_playlist == *pname {
|
||||||
|
@ -34,13 +36,38 @@ impl ComponentUi for SideNav {
|
||||||
}
|
}
|
||||||
|
|
||||||
let button = Label::new(text).sense(Sense::click()).selectable(false);
|
let button = Label::new(text).sense(Sense::click()).selectable(false);
|
||||||
if ui.add(button).clicked() {
|
let button = ui.add(button);
|
||||||
|
if button.clicked() {
|
||||||
gui.current_playlist = pname.to_string();
|
gui.current_playlist = pname.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.context_menu(|ui| context_menu::ContextMenu::ui(gui, ui, &pname));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
check_if_needs_delete(gui);
|
||||||
}
|
}
|
||||||
// #333377
|
// #333377
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn check_if_needs_delete(gui: &mut crate::ui::gui::Gui) {
|
||||||
|
// Check for items that need to be deleted
|
||||||
|
let (id, resp, data) = gui.windows.get_window::<windows::confirm::ConfirmW>(WindowIndex::Confirm).get_response();
|
||||||
|
match (id.as_str(), resp) {
|
||||||
|
("side_nav_playlist_manifest_delete", Some(true)) => {
|
||||||
|
gui.manifest.remove_playlist(&data[0]);
|
||||||
|
let _ = gui.manifest.save(None);
|
||||||
|
gui.windows.get_window::<windows::confirm::ConfirmW>(WindowIndex::Confirm).reset();
|
||||||
|
}
|
||||||
|
("side_nav_playlist_manifest_delete", Some(false)) => {
|
||||||
|
log::debug!("FALSE");
|
||||||
|
gui.windows.get_window::<windows::confirm::ConfirmW>(WindowIndex::Confirm).reset();
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
92
src/ui/gui/components/song_list/context_menu.rs
Normal file
92
src/ui/gui/components/song_list/context_menu.rs
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
use egui::{Color32, RichText};
|
||||||
|
use crate::{manifest::song::{Song, SongType}, ui::gui::windows::{self, song_edit::GuiSongEditor, WindowIndex}};
|
||||||
|
|
||||||
|
use super::ComponentContextMenu;
|
||||||
|
|
||||||
|
pub struct ContextMenu;
|
||||||
|
|
||||||
|
pub struct SongInfo {
|
||||||
|
pname: String,
|
||||||
|
sname: String,
|
||||||
|
song: Song,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SongInfo {
|
||||||
|
pub fn new(pname: &String, sname: &String, song: &Song) -> Self {
|
||||||
|
Self {
|
||||||
|
pname: pname.clone(),
|
||||||
|
sname: sname.clone(),
|
||||||
|
song: song.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn playlist_name(&self) -> &String {
|
||||||
|
&self.pname
|
||||||
|
}
|
||||||
|
pub fn song_name(&self) -> &String {
|
||||||
|
&self.sname
|
||||||
|
}
|
||||||
|
pub fn song_url(&self) -> &String {
|
||||||
|
self.song.get_url_str()
|
||||||
|
}
|
||||||
|
pub fn song_type(&self) -> &SongType {
|
||||||
|
self.song.get_type()
|
||||||
|
}
|
||||||
|
pub fn song(&self) -> &Song {
|
||||||
|
&self.song
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentContextMenu for ContextMenu {
|
||||||
|
type Data = SongInfo;
|
||||||
|
fn ui(gui: &mut crate::ui::gui::Gui, ui: &mut egui::Ui, data: &Self::Data) {
|
||||||
|
if ui.button("Edit").clicked() {
|
||||||
|
let w = gui.windows.get_window::<GuiSongEditor>(WindowIndex::SongEdit);
|
||||||
|
w.set_active_song(data.playlist_name(), data.song_name(), data.song_url(), data.song_type());
|
||||||
|
gui.windows.open(WindowIndex::SongEdit, true);
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ui.button("Download").clicked() {
|
||||||
|
if let Err(e) = gui.downloader.download_song_nb(&gui.cfg, data.playlist_name(), data.song_name(), data.song(), gui.manifest.get_format()) {
|
||||||
|
log::error!("{e}");
|
||||||
|
gui.throw_error(format!("Failed to download song {}: {e}", data.song_name()));
|
||||||
|
}
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ui.button("Open Source").clicked() {
|
||||||
|
if let Err(e) = open::that(data.song_url()) {
|
||||||
|
log::error!("{e}");
|
||||||
|
gui.throw_error(format!("Failed to open song source: {e}"));
|
||||||
|
}
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
|
if ui.button("Play").clicked() {
|
||||||
|
let p = crate::util::get_song_path(data.playlist_name(), data.song_name(), gui.manifest.get_format());
|
||||||
|
|
||||||
|
if !p.exists() {
|
||||||
|
gui.throw_error("Song does not exist on disk".to_string());
|
||||||
|
} else if let Err(e) = open::that(p) {
|
||||||
|
log::error!("{e}");
|
||||||
|
gui.throw_error(format!("Failed to play song: {e}"));
|
||||||
|
}
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
|
if ui.button("Delete from disk").clicked() {
|
||||||
|
let p = crate::util::get_song_path(data.playlist_name(), data.song_name(), gui.manifest.get_format());
|
||||||
|
if p.exists() {
|
||||||
|
if let Err(e) = std::fs::remove_file(p) {
|
||||||
|
gui.throw_error(format!("Failed to delete file: {e}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
|
if ui.button(RichText::new("Delete").color(Color32::RED)).clicked() {
|
||||||
|
let w = gui.windows.get_window::<windows::confirm::ConfirmW>(WindowIndex::Confirm);
|
||||||
|
w.set_message(&"song_list_song_manifest_delete", &"This will delete the song from the manifest file. This is NOT reversible", &vec![data.playlist_name().clone(), data.song_name().clone()]);
|
||||||
|
gui.windows.open(WindowIndex::Confirm, true);
|
||||||
|
ui.close_menu();
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,14 @@
|
||||||
use egui::Color32;
|
use egui::Color32;
|
||||||
use egui_extras::{Column, TableBuilder};
|
use egui_extras::{Column, TableBuilder};
|
||||||
|
|
||||||
use crate::manifest::song::SongType;
|
use crate::{manifest::song::SongType, ui::gui::windows::{self, WindowIndex}};
|
||||||
|
|
||||||
use super::{context_menu::ContextMenu, search_bar::SearchType, ComponentUi};
|
use super::{search_bar::SearchType, ComponentContextMenu, ComponentUi};
|
||||||
|
|
||||||
|
mod context_menu;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct SongList {
|
pub struct SongList;
|
||||||
}
|
|
||||||
|
|
||||||
impl ComponentUi for SongList {
|
impl ComponentUi for SongList {
|
||||||
fn ui(gui: &mut crate::ui::gui::Gui, ui: &mut egui::Ui) {
|
fn ui(gui: &mut crate::ui::gui::Gui, ui: &mut egui::Ui) {
|
||||||
|
@ -25,17 +26,8 @@ impl ComponentUi for SongList {
|
||||||
.striped(true)
|
.striped(true)
|
||||||
.cell_layout(egui::Layout::left_to_right(egui::Align::Center))
|
.cell_layout(egui::Layout::left_to_right(egui::Align::Center))
|
||||||
.resizable(true)
|
.resizable(true)
|
||||||
//.column(Column::auto())
|
|
||||||
//.column(Column::auto())
|
|
||||||
//.column(
|
|
||||||
// Column::remainder()
|
|
||||||
// .at_least(40.0)
|
|
||||||
// .clip(true)
|
|
||||||
// .resizable(true),
|
|
||||||
//)
|
|
||||||
.column(Column::auto())
|
.column(Column::auto())
|
||||||
.column(Column::remainder())
|
.column(Column::remainder())
|
||||||
//.column(Column::remainder())
|
|
||||||
.min_scrolled_height(0.0)
|
.min_scrolled_height(0.0)
|
||||||
.max_scroll_height(available_height)
|
.max_scroll_height(available_height)
|
||||||
.sense(egui::Sense::click());
|
.sense(egui::Sense::click());
|
||||||
|
@ -55,10 +47,6 @@ impl ComponentUi for SongList {
|
||||||
};
|
};
|
||||||
|
|
||||||
table.header(20.0, |mut header| {
|
table.header(20.0, |mut header| {
|
||||||
// header.col(|_|{});
|
|
||||||
//header.col(|ui| {
|
|
||||||
// ui.strong("Playlist");vec.sort_by_key(|name| name.to_lowercase());
|
|
||||||
//});
|
|
||||||
header.col(|ui| {
|
header.col(|ui| {
|
||||||
ui.strong("Source");
|
ui.strong("Source");
|
||||||
});
|
});
|
||||||
|
@ -97,11 +85,8 @@ impl ComponentUi for SongList {
|
||||||
(SearchType::Url, _) => (),
|
(SearchType::Url, _) => (),
|
||||||
}
|
}
|
||||||
body.row(18.0, |mut row| {
|
body.row(18.0, |mut row| {
|
||||||
|
let song_info = context_menu::SongInfo::new(&pname, &sname, &s);
|
||||||
//row.col(|ui| {
|
|
||||||
// ui.label(pname.clone())
|
|
||||||
// .context_menu(|ui| ContextMenu::ui(gui, ui, &pname, &sname, &s));
|
|
||||||
//});
|
|
||||||
row.col(|ui| {
|
row.col(|ui| {
|
||||||
let color =
|
let color =
|
||||||
match s.get_type() {
|
match s.get_type() {
|
||||||
|
@ -111,20 +96,41 @@ impl ComponentUi for SongList {
|
||||||
};
|
};
|
||||||
|
|
||||||
ui.colored_label(color, s.get_type().to_string())
|
ui.colored_label(color, s.get_type().to_string())
|
||||||
.context_menu(|ui| ContextMenu::ui(gui, ui, &pname, &sname, &s));
|
.context_menu(|ui| context_menu::ContextMenu::ui(gui, ui, &song_info));
|
||||||
});
|
});
|
||||||
row.col(|ui| {
|
row.col(|ui| {
|
||||||
ui.hyperlink_to(sname.clone(), s.get_url_str())
|
ui.hyperlink_to(sname.clone(), s.get_url_str())
|
||||||
.context_menu(|ui| ContextMenu::ui(gui, ui, &pname, &sname, &s));
|
.context_menu(|ui| context_menu::ContextMenu::ui(gui, ui, &song_info));
|
||||||
});
|
});
|
||||||
|
|
||||||
row.response()
|
row.response()
|
||||||
.context_menu(|ui| ContextMenu::ui(gui, ui, &pname, &sname, &s));
|
.context_menu(|ui| context_menu::ContextMenu::ui(gui, ui, &song_info));
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
check_if_needs_delete(gui);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_if_needs_delete(gui: &mut crate::ui::gui::Gui) {
|
||||||
|
// Check for items that need to be deleted
|
||||||
|
let (id, resp, data) = gui.windows.get_window::<windows::confirm::ConfirmW>(WindowIndex::Confirm).get_response();
|
||||||
|
match (id.as_str(), resp) {
|
||||||
|
("song_list_song_manifest_delete", Some(true)) => {
|
||||||
|
gui.manifest.remove_song(&data[0], &data[1]);
|
||||||
|
let _ = gui.manifest.save(None);
|
||||||
|
gui.windows.get_window::<windows::confirm::ConfirmW>(WindowIndex::Confirm).reset();
|
||||||
|
}
|
||||||
|
("song_list_song_manifest_delete", Some(false)) => {
|
||||||
|
log::debug!("FALSE");
|
||||||
|
gui.windows.get_window::<windows::confirm::ConfirmW>(WindowIndex::Confirm).reset();
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -51,7 +51,6 @@ impl Gui {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::pedantic)]
|
|
||||||
pub fn throw_error<S: ToString>(&mut self, text: S) {
|
pub fn throw_error<S: ToString>(&mut self, text: S) {
|
||||||
let w = self.windows.get_window::<windows::error::GuiError>(WindowIndex::Error);
|
let w = self.windows.get_window::<windows::error::GuiError>(WindowIndex::Error);
|
||||||
w.set_error_message(&text);
|
w.set_error_message(&text);
|
||||||
|
@ -94,5 +93,7 @@ impl eframe::App for Gui {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
// Make sure we dont wait for any updates cause we depend on the gui code for downloads
|
||||||
|
ctx.request_repaint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
57
src/ui/gui/windows/confirm.rs
Normal file
57
src/ui/gui/windows/confirm.rs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
use egui::{Color32, Label, RichText, TextBuffer};
|
||||||
|
|
||||||
|
use super::{State, Window};
|
||||||
|
|
||||||
|
|
||||||
|
#[allow(clippy::pedantic)]
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct ConfirmW {
|
||||||
|
id: String,
|
||||||
|
text: String,
|
||||||
|
response: Option<bool>,
|
||||||
|
data: Vec<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Window for ConfirmW {
|
||||||
|
fn ui(&mut self, _: &mut State, ctx: &egui::Context, open: &mut bool) -> anyhow::Result<()> {
|
||||||
|
let mut should_close = false;
|
||||||
|
egui::Window::new("Are you sure?").open(open).show(ctx, |ui| {
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
ui.label(RichText::new("Are you sure you want to do this?").size(15.0).color(Color32::BLUE));
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.add(Label::new(self.text.clone()).wrap(true));
|
||||||
|
});
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
if ui.button("Cancel").clicked() {
|
||||||
|
self.response = Some(false);
|
||||||
|
should_close = true;
|
||||||
|
} else if ui.button("Continue").clicked() {
|
||||||
|
self.response = Some(true);
|
||||||
|
should_close = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
if should_close {
|
||||||
|
*open = false;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConfirmW {
|
||||||
|
pub fn set_message<S: ToString>(&mut self, new_id: &S, text: &S, data: &Vec<String>) {
|
||||||
|
self.text = text.to_string();
|
||||||
|
self.id = new_id.to_string();
|
||||||
|
self.data = data.clone();
|
||||||
|
}
|
||||||
|
pub fn get_response(&self) -> (&String, &Option<bool>, &Vec<String>) {
|
||||||
|
(&self.id, &self.response, &self.data)
|
||||||
|
}
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
self.id.clear();
|
||||||
|
self.text.clear();
|
||||||
|
self.response = None;
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,7 +16,7 @@ impl Window for GuiError {
|
||||||
.open(open)
|
.open(open)
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
ui.label(RichText::new("Error:").size(30.0).color(Color32::RED));
|
ui.label(RichText::new("Error:").size(15.0).color(Color32::RED));
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.add(Label::new(self.text.clone()).wrap(true));
|
ui.add(Label::new(self.text.clone()).wrap(true));
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,6 +5,7 @@ pub mod song_edit;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod import_playlist;
|
pub mod import_playlist;
|
||||||
pub mod song_new;
|
pub mod song_new;
|
||||||
|
pub mod confirm;
|
||||||
|
|
||||||
pub trait Window: std::fmt::Debug {
|
pub trait Window: std::fmt::Debug {
|
||||||
fn ui(&mut self, state: &mut State, ctx: &egui::Context, open: &mut bool) -> anyhow::Result<()>;
|
fn ui(&mut self, state: &mut State, ctx: &egui::Context, open: &mut bool) -> anyhow::Result<()>;
|
||||||
|
@ -16,6 +17,7 @@ pub enum WindowIndex {
|
||||||
ImportPlaylist,
|
ImportPlaylist,
|
||||||
SongEdit,
|
SongEdit,
|
||||||
SongNew,
|
SongNew,
|
||||||
|
Confirm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,6 +40,7 @@ impl WindowManager {
|
||||||
windows.insert(WindowIndex::ImportPlaylist, Box::<import_playlist::GuiImportPlaylist>::default());
|
windows.insert(WindowIndex::ImportPlaylist, Box::<import_playlist::GuiImportPlaylist>::default());
|
||||||
windows.insert(WindowIndex::SongEdit, Box::<song_edit::GuiSongEditor>::default());
|
windows.insert(WindowIndex::SongEdit, Box::<song_edit::GuiSongEditor>::default());
|
||||||
windows.insert(WindowIndex::SongNew, Box::<song_new::GuiNewSong>::default());
|
windows.insert(WindowIndex::SongNew, Box::<song_new::GuiNewSong>::default());
|
||||||
|
windows.insert(WindowIndex::Confirm, Box::<confirm::ConfirmW>::default());
|
||||||
Self {
|
Self {
|
||||||
windows,
|
windows,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
|
@ -70,4 +70,12 @@ pub fn get_song_path/*<P: TryInto<PathBuf>>*/(/*basepath: Option<P>,*/ pname: &S
|
||||||
path.push(sname);
|
path.push(sname);
|
||||||
path.set_extension(format.to_string());
|
path.set_extension(format.to_string());
|
||||||
path
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_playlist_path(pname: &String) -> PathBuf {
|
||||||
|
let mut path = std::env::current_dir().unwrap_or_default();
|
||||||
|
// TODO: Get this from cfg
|
||||||
|
path.push("out");
|
||||||
|
path.push(pname);
|
||||||
|
path
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user