Rewrite the structure of the gui and cli interfaces
Added a more modular way to add windows
This commit is contained in:
parent
33ca4502e4
commit
2cde24e7a8
31
\
Normal file
31
\
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use super::Gui;
|
||||||
|
|
||||||
|
mod song_edit;
|
||||||
|
|
||||||
|
trait Window {
|
||||||
|
fn show(&mut self, gui: &mut Gui, ctx: &egui::Context, open: &mut bool) -> anyhow::Result<()>;
|
||||||
|
fn name(&self) -> &'static str;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum WindowIndex {
|
||||||
|
SongEdit
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct WindowManager {
|
||||||
|
windows: HashMap<WindowIndex, Box<dyn Window>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowManager {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut windows = HashMap::new();
|
||||||
|
windows.instert(WindowIndex::SongEdit, Box::<song_edit::GuiSongEditor>::default());
|
||||||
|
Self {
|
||||||
|
windows: HashMap::from_iter([
|
||||||
|
(WindowIndex::SongEdit, Box::<song_edit::GuiSongEditor>::default())
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,223 +0,0 @@
|
||||||
use egui::{Color32, Label, RichText};
|
|
||||||
|
|
||||||
|
|
||||||
use crate::manifest::song::{Song, SongType};
|
|
||||||
|
|
||||||
use super::Gui;
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct GuiSongEditor {
|
|
||||||
pub is_open: bool,
|
|
||||||
pub song: (String, String),
|
|
||||||
pub ed_url: String,
|
|
||||||
pub ed_name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct GuiNewSong {
|
|
||||||
pub is_open: bool,
|
|
||||||
ed_type: SongType,
|
|
||||||
ed_name: String,
|
|
||||||
ed_playlist: Option<String>,
|
|
||||||
ed_url: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct GuiImportPlaylist {
|
|
||||||
pub is_open: bool,
|
|
||||||
ed_name: String,
|
|
||||||
ed_url: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct GuiError {
|
|
||||||
pub is_open: bool,
|
|
||||||
pub text: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Gui {
|
|
||||||
pub fn draw_song_edit_window(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
|
|
||||||
let mut save = false;
|
|
||||||
|
|
||||||
let (playlist, song_name) = self.song_edit_w.song.clone();
|
|
||||||
|
|
||||||
let Some(song) = self.manifest.get_song(&playlist, &song_name) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let song = song.clone();
|
|
||||||
|
|
||||||
egui::Window::new("Song editor")
|
|
||||||
.open(&mut self.song_edit_w.is_open)
|
|
||||||
.show(ctx,
|
|
||||||
|ui| {
|
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.spacing_mut().item_spacing.x = 0.0;
|
|
||||||
ui.label("[");
|
|
||||||
ui.hyperlink_to("link", song.get_url().unwrap());
|
|
||||||
ui.label("] ");
|
|
||||||
ui.colored_label(Color32::LIGHT_BLUE, &playlist);
|
|
||||||
ui.label(": ");
|
|
||||||
ui.label(&song_name)
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Type: ");
|
|
||||||
ui.label(&song.get_type().to_string());
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Name: ");
|
|
||||||
ui.text_edit_singleline(&mut self.song_edit_w.ed_name);
|
|
||||||
});
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Url: ");
|
|
||||||
ui.text_edit_singleline(&mut self.song_edit_w.ed_url);
|
|
||||||
});
|
|
||||||
|
|
||||||
if ui.button("Save").clicked() {
|
|
||||||
save = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if save {
|
|
||||||
{
|
|
||||||
let Some(song) = self.manifest.get_song_mut(&playlist, &song_name) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
*song.get_url_str_mut() = self.song_edit_w.ed_url.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(playlist) = self.manifest.get_playlist_mut(&playlist) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
playlist.remove_song(&song_name);
|
|
||||||
playlist.add_song(self.song_edit_w.ed_name.clone(), song);
|
|
||||||
self.song_edit_w.is_open = false;
|
|
||||||
let _ = self.manifest.save(None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_new_song_window(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
|
|
||||||
let mut save = false;
|
|
||||||
egui::Window::new("New song")
|
|
||||||
.open(&mut self.new_song_w.is_open)
|
|
||||||
.show(ctx, |ui| {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Type: ");
|
|
||||||
egui::ComboBox::from_id_source("new_song_window_type")
|
|
||||||
.selected_text(format!("{:?}", self.new_song_w.ed_type))
|
|
||||||
.show_ui(ui, |ui| {
|
|
||||||
ui.selectable_value(&mut self.new_song_w.ed_type, SongType::Youtube, "Youtube");
|
|
||||||
ui.selectable_value(&mut self.new_song_w.ed_type, SongType::Spotify, "Spotify");
|
|
||||||
ui.selectable_value(&mut self.new_song_w.ed_type, SongType::Soundcloud, "Soundcloud");
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Name: ");
|
|
||||||
ui.text_edit_singleline(&mut self.new_song_w.ed_name);
|
|
||||||
});
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Playlist: ");
|
|
||||||
egui::ComboBox::from_id_source("new_song_window_playlist")
|
|
||||||
.selected_text(format!("{}", self.new_song_w.ed_playlist.clone().unwrap_or("".to_string())))
|
|
||||||
.show_ui(ui, |ui| {
|
|
||||||
for p in self.manifest.get_playlists().keys() {
|
|
||||||
ui.selectable_value(&mut self.new_song_w.ed_playlist, Option::Some(p.clone()), p.as_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Url: ");
|
|
||||||
ui.text_edit_singleline(&mut self.new_song_w.ed_url);
|
|
||||||
});
|
|
||||||
|
|
||||||
if ui.button("Save").clicked() {
|
|
||||||
save = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if save {
|
|
||||||
let Some(playlist) = self.manifest.get_playlist_mut(&self.new_song_w.ed_playlist.clone().unwrap()) else {
|
|
||||||
panic!("couldnt find playlist from a preset playlist list????????????");
|
|
||||||
};
|
|
||||||
|
|
||||||
playlist.add_song(
|
|
||||||
self.new_song_w.ed_name.clone(),
|
|
||||||
Song::from_url_str(self.new_song_w.ed_url.clone()).unwrap().set_type(self.new_song_w.ed_type.clone()).clone()
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
let _ = self.manifest.save(None);
|
|
||||||
self.new_song_w.is_open = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_import_playlist_window(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
|
|
||||||
let mut save = false;
|
|
||||||
egui::Window::new("Import Playlist")
|
|
||||||
.open(&mut self.import_playlist_w.is_open)
|
|
||||||
.show(ctx, |ui| {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Type: Youtube");
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Name: ");
|
|
||||||
ui.text_edit_singleline(&mut self.import_playlist_w.ed_name);
|
|
||||||
});
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("Url: ");
|
|
||||||
ui.text_edit_singleline(&mut self.import_playlist_w.ed_url);
|
|
||||||
});
|
|
||||||
|
|
||||||
if ui.button("Import").clicked() {
|
|
||||||
save = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if save {
|
|
||||||
let name = self.import_playlist_w.ed_name.clone();
|
|
||||||
let url = self.import_playlist_w.ed_url.clone();
|
|
||||||
|
|
||||||
if self.manifest.get_playlist(&name).is_some() {
|
|
||||||
log::error!("Playlist {name} already exists");
|
|
||||||
self.throw_error(format!("Playlist {name} already exists"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let songs = self.downloader.download_playlist_nb(&self.cfg, &url, &name, &self.manifest.get_format()).unwrap();
|
|
||||||
self.manifest.add_playlist(name.clone());
|
|
||||||
|
|
||||||
let playlist = self.manifest.get_playlist_mut(&name).expect("Unreachable");
|
|
||||||
|
|
||||||
for (sname, song) in songs {
|
|
||||||
log::info!("Added: {sname}");
|
|
||||||
playlist.add_song(sname, song);
|
|
||||||
}
|
|
||||||
let _ = self.manifest.save(None);
|
|
||||||
self.import_playlist_w.is_open = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_error_window(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
|
|
||||||
egui::Window::new("ERROR!!!! D:")
|
|
||||||
.open(&mut self.error_w.is_open)
|
|
||||||
.show(ctx, |ui| {
|
|
||||||
ui.vertical(|ui| {
|
|
||||||
ui.label(RichText::new("Error:").size(30.0).color(Color32::RED));
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.add(Label::new(self.error_w.text.clone()).wrap(true));
|
|
||||||
})
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -18,7 +18,7 @@ lazy_static!(
|
||||||
static ref PROCESSES: Mutex<RwLock<HashMap<usize, Proc>>> = Mutex::new(RwLock::new(HashMap::new()));
|
static ref PROCESSES: Mutex<RwLock<HashMap<usize, Proc>>> = Mutex::new(RwLock::new(HashMap::new()));
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct Downloader {
|
pub struct Downloader {
|
||||||
count: usize,
|
count: usize,
|
||||||
nb_initial_song_count: usize,
|
nb_initial_song_count: usize,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![feature(downcast_unchecked)]
|
||||||
|
|
||||||
use config::ConfigWrapper;
|
use config::ConfigWrapper;
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,11 +8,11 @@ mod manifest;
|
||||||
mod logger;
|
mod logger;
|
||||||
mod downloader;
|
mod downloader;
|
||||||
mod util;
|
mod util;
|
||||||
mod commands;
|
|
||||||
mod prompt;
|
mod prompt;
|
||||||
mod config;
|
mod config;
|
||||||
mod constants;
|
mod constants;
|
||||||
mod process_manager;
|
mod process_manager;
|
||||||
|
mod ui;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
@ -27,5 +29,5 @@ async fn main() {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
let _ = commands::command_run(&cfg, &mut manifest).await;
|
let _ = ui::cli::command_run(&cfg, &mut manifest).await;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
mod add;
|
mod add;
|
||||||
pub mod gui;
|
|
||||||
|
|
||||||
use crate::{config::{cli::CliCommand, ConfigWrapper}, downloader::Downloader, manifest::Manifest};
|
use crate::{config::{cli::CliCommand, ConfigWrapper}, downloader::Downloader, manifest::Manifest, ui::gui};
|
||||||
|
|
||||||
|
|
||||||
|
|
0
src/ui/gui/components/mod.rs
Normal file
0
src/ui/gui/components/mod.rs
Normal file
|
@ -1,21 +1,18 @@
|
||||||
mod nav_bar;
|
mod nav_bar;
|
||||||
mod song_edit_window;
|
mod windows;
|
||||||
|
mod components;
|
||||||
|
|
||||||
use egui::{Color32, RichText};
|
use egui::{Color32, RichText};
|
||||||
use egui_extras::{Column, TableBuilder};
|
use egui_extras::{Column, TableBuilder};
|
||||||
use song_edit_window::{GuiError, GuiImportPlaylist, GuiNewSong};
|
use windows::{State, WindowIndex, WindowManager};
|
||||||
|
|
||||||
use crate::{config::ConfigWrapper, downloader::Downloader, manifest::{song::{Song, SongType}, Manifest}};
|
use crate::{config::ConfigWrapper, downloader::Downloader, manifest::{song::{Song, SongType}, Manifest}};
|
||||||
|
|
||||||
use self::song_edit_window::GuiSongEditor;
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Gui {
|
pub struct Gui {
|
||||||
|
windows: WindowManager,
|
||||||
manifest: Manifest,
|
manifest: Manifest,
|
||||||
song_edit_w: GuiSongEditor,
|
|
||||||
new_song_w: GuiNewSong,
|
|
||||||
import_playlist_w: GuiImportPlaylist,
|
|
||||||
error_w: GuiError,
|
|
||||||
filter: String,
|
filter: String,
|
||||||
downloader: Downloader,
|
downloader: Downloader,
|
||||||
cfg: ConfigWrapper,
|
cfg: ConfigWrapper,
|
||||||
|
@ -28,6 +25,7 @@ impl Gui {
|
||||||
manifest,
|
manifest,
|
||||||
downloader,
|
downloader,
|
||||||
cfg,
|
cfg,
|
||||||
|
windows: windows::WindowManager::new(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,18 +54,26 @@ impl Gui {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn throw_error(&mut self, text: impl ToString) {
|
pub fn throw_error(&mut self, text: impl ToString) {
|
||||||
self.error_w.is_open = true;
|
let w = self.windows.get_window::<windows::error::GuiError>(WindowIndex::Error);
|
||||||
self.error_w.text = text.to_string();
|
w.set_error_message(text);
|
||||||
|
self.windows.open(WindowIndex::Error, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl eframe::App for Gui {
|
impl eframe::App for Gui {
|
||||||
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
||||||
self.draw_nav(ctx, frame);
|
self.draw_nav(ctx, frame);
|
||||||
self.draw_song_edit_window(ctx, frame);
|
{
|
||||||
self.draw_new_song_window(ctx, frame);
|
let mut state = State {
|
||||||
self.draw_import_playlist_window(ctx, frame);
|
cfg: self.cfg.clone(),
|
||||||
self.draw_error_window(ctx, frame);
|
downloader: self.downloader.clone(),
|
||||||
|
manifest: self.manifest.clone(),
|
||||||
|
};
|
||||||
|
self.windows.ui(&mut state, ctx).unwrap();
|
||||||
|
self.cfg = state.cfg;
|
||||||
|
self.downloader = state.downloader;
|
||||||
|
self.manifest = state.manifest;
|
||||||
|
}
|
||||||
|
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
// The central panel the region left after adding TopPanel's and SidePanel's
|
// The central panel the region left after adding TopPanel's and SidePanel's
|
||||||
|
@ -186,13 +192,9 @@ impl eframe::App for Gui {
|
||||||
|
|
||||||
fn context_menu(this: &mut Gui, ui: &mut egui::Ui, pname: &String, sname: &String, song: &Song) {
|
fn context_menu(this: &mut Gui, ui: &mut egui::Ui, pname: &String, sname: &String, song: &Song) {
|
||||||
if ui.button("Edit").clicked() {
|
if ui.button("Edit").clicked() {
|
||||||
this.song_edit_w.song = (
|
let w = this.windows.get_window::<windows::song_edit::GuiSongEditor>(WindowIndex::SongEdit);
|
||||||
pname.clone(),
|
w.set_active_song(pname, sname, song.get_url_str());
|
||||||
sname.clone(),
|
this.windows.open(WindowIndex::SongEdit, true);
|
||||||
);
|
|
||||||
this.song_edit_w.is_open = true;
|
|
||||||
this.song_edit_w.ed_name = sname.clone();
|
|
||||||
this.song_edit_w.ed_url = song.get_url_str().clone();
|
|
||||||
ui.close_menu()
|
ui.close_menu()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use super::Gui;
|
use super::{windows::WindowIndex, Gui};
|
||||||
|
|
||||||
impl Gui {
|
impl Gui {
|
||||||
|
|
||||||
|
@ -22,13 +22,13 @@ impl Gui {
|
||||||
|
|
||||||
ui.menu_button("Song", |ui| {
|
ui.menu_button("Song", |ui| {
|
||||||
if ui.button("Add New").clicked() {
|
if ui.button("Add New").clicked() {
|
||||||
self.new_song_w.is_open = true;
|
self.windows.open(WindowIndex::SongNew, true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.menu_button("Playlist", |ui| {
|
ui.menu_button("Playlist", |ui| {
|
||||||
if ui.button("Import").clicked() {
|
if ui.button("Import").clicked() {
|
||||||
self.import_playlist_w.is_open = true;
|
self.windows.open(WindowIndex::ImportPlaylist, true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
32
src/ui/gui/windows/error.rs
Normal file
32
src/ui/gui/windows/error.rs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
use egui::{Color32, Label, RichText};
|
||||||
|
|
||||||
|
use super::{State, Window};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct GuiError {
|
||||||
|
text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Window for GuiError {
|
||||||
|
fn ui(&mut self, _: &mut State, ctx: &egui::Context, open: &mut bool) -> anyhow::Result<()> {
|
||||||
|
egui::Window::new("ERROR!!!! D:")
|
||||||
|
.open(open)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
ui.label(RichText::new("Error:").size(30.0).color(Color32::RED));
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.add(Label::new(self.text.clone()).wrap(true));
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GuiError {
|
||||||
|
pub fn set_error_message<S: ToString>(&mut self, text: S) {
|
||||||
|
self.text = text.to_string();
|
||||||
|
}
|
||||||
|
}
|
59
src/ui/gui/windows/import_playlist.rs
Normal file
59
src/ui/gui/windows/import_playlist.rs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
use super::{State, Window};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct GuiImportPlaylist {
|
||||||
|
ed_name: String,
|
||||||
|
ed_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Window for GuiImportPlaylist {
|
||||||
|
fn ui(&mut self, state: &mut State, ctx: &egui::Context, open: &mut bool) -> anyhow::Result<()> {
|
||||||
|
let mut save = false;
|
||||||
|
egui::Window::new("Import Playlist")
|
||||||
|
.open(open)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Type: Youtube");
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Name: ");
|
||||||
|
ui.text_edit_singleline(&mut self.ed_name);
|
||||||
|
});
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Url: ");
|
||||||
|
ui.text_edit_singleline(&mut self.ed_url);
|
||||||
|
});
|
||||||
|
|
||||||
|
if ui.button("Import").clicked() {
|
||||||
|
save = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if save {
|
||||||
|
let name = self.ed_name.clone();
|
||||||
|
let url = self.ed_url.clone();
|
||||||
|
|
||||||
|
if state.manifest.get_playlist(&name).is_some() {
|
||||||
|
log::error!("Playlist {name} already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
let songs = state.downloader.download_playlist_nb(&state.cfg, &url, &name, &state.manifest.get_format()).unwrap();
|
||||||
|
state.manifest.add_playlist(name.clone());
|
||||||
|
|
||||||
|
let playlist = state.manifest.get_playlist_mut(&name).expect("Unreachable");
|
||||||
|
|
||||||
|
for (sname, song) in songs {
|
||||||
|
log::info!("Added: {sname}");
|
||||||
|
playlist.add_song(sname, song);
|
||||||
|
}
|
||||||
|
let _ = state.manifest.save(None);
|
||||||
|
*open = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
76
src/ui/gui/windows/mod.rs
Normal file
76
src/ui/gui/windows/mod.rs
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use crate::{config::ConfigWrapper, downloader::Downloader, manifest::Manifest};
|
||||||
|
|
||||||
|
pub mod song_edit;
|
||||||
|
pub mod error;
|
||||||
|
pub mod import_playlist;
|
||||||
|
pub mod song_new;
|
||||||
|
|
||||||
|
pub trait Window: std::fmt::Debug {
|
||||||
|
fn ui(&mut self, state: &mut State, ctx: &egui::Context, open: &mut bool) -> anyhow::Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
|
||||||
|
pub enum WindowIndex {
|
||||||
|
Error,
|
||||||
|
ImportPlaylist,
|
||||||
|
SongEdit,
|
||||||
|
SongNew,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug,Default)]
|
||||||
|
pub struct WindowManager {
|
||||||
|
opened: HashMap<WindowIndex, bool>,
|
||||||
|
windows: HashMap<WindowIndex, Box<dyn Window>>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct State {
|
||||||
|
pub downloader: Downloader,
|
||||||
|
pub manifest: Manifest,
|
||||||
|
pub cfg: ConfigWrapper,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowManager {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut windows: HashMap<WindowIndex, Box<dyn Window>> = HashMap::new();
|
||||||
|
windows.insert(WindowIndex::Error, Box::<error::GuiError>::default());
|
||||||
|
windows.insert(WindowIndex::ImportPlaylist, Box::<import_playlist::GuiImportPlaylist>::default());
|
||||||
|
windows.insert(WindowIndex::SongEdit, Box::<song_edit::GuiSongEditor>::default());
|
||||||
|
windows.insert(WindowIndex::SongNew, Box::<song_new::GuiNewSong>::default());
|
||||||
|
Self {
|
||||||
|
windows,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn is_open(&self, id: &WindowIndex) -> bool {
|
||||||
|
*self.opened.get(id).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open(&mut self, id: WindowIndex, open: bool) {
|
||||||
|
self.opened.insert(id, open);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ui(&mut self, state: &mut State, ctx: &egui::Context) -> anyhow::Result<()> {
|
||||||
|
for (id, window) in &mut self.windows {
|
||||||
|
if !self.opened.contains_key(&id) {
|
||||||
|
self.opened.insert(*id, false);
|
||||||
|
}
|
||||||
|
let open = self.opened.get_mut(id).unwrap();
|
||||||
|
if let Err(e) = window.ui(state, ctx, open) {
|
||||||
|
log::error!("Window {id:?} errored: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_window<T: Window + 'static>(&mut self, id: WindowIndex) -> &mut Box<T> {
|
||||||
|
let w = self.windows.get_mut(&id).unwrap();
|
||||||
|
unsafe {
|
||||||
|
crate::util::as_any_mut(w).downcast_mut_unchecked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
95
src/ui/gui/windows/song_edit.rs
Normal file
95
src/ui/gui/windows/song_edit.rs
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
use anyhow::{Result, bail};
|
||||||
|
use egui::Color32;
|
||||||
|
use crate::ui::gui::Gui;
|
||||||
|
|
||||||
|
use super::{State, Window};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct GuiSongEditor {
|
||||||
|
song: (String, String),
|
||||||
|
ed_url: String,
|
||||||
|
ed_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Window for GuiSongEditor {
|
||||||
|
fn ui(&mut self, state: &mut State, ctx: &egui::Context, open: &mut bool) -> anyhow::Result<()> {
|
||||||
|
let mut save = false;
|
||||||
|
let (playlist, song_name) = self.song.clone();
|
||||||
|
|
||||||
|
if playlist.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(song) = state.manifest.get_song(&playlist, &song_name) else {
|
||||||
|
bail!("Failed to get song (1)");
|
||||||
|
};
|
||||||
|
let song = song.clone();
|
||||||
|
|
||||||
|
egui::Window::new("Song editor")
|
||||||
|
.open(open)
|
||||||
|
.show(ctx,
|
||||||
|
|ui| {
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.spacing_mut().item_spacing.x = 0.0;
|
||||||
|
ui.label("[");
|
||||||
|
ui.hyperlink_to("link", song.get_url().unwrap());
|
||||||
|
ui.label("] ");
|
||||||
|
ui.colored_label(Color32::LIGHT_BLUE, &playlist);
|
||||||
|
ui.label(": ");
|
||||||
|
ui.label(&song_name)
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Type: ");
|
||||||
|
ui.label(&song.get_type().to_string());
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Name: ");
|
||||||
|
ui.text_edit_singleline(&mut self.ed_name);
|
||||||
|
});
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Url: ");
|
||||||
|
ui.text_edit_singleline(&mut self.ed_url);
|
||||||
|
});
|
||||||
|
|
||||||
|
if ui.button("Save").clicked() {
|
||||||
|
save = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if save {
|
||||||
|
{
|
||||||
|
let Some(song) = state.manifest.get_song_mut(&playlist, &song_name) else {
|
||||||
|
bail!("Failed to get song (2)");
|
||||||
|
};
|
||||||
|
|
||||||
|
*song.get_url_str_mut() = self.ed_url.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(playlist) = state.manifest.get_playlist_mut(&playlist) else {
|
||||||
|
bail!("Failed to get playlist");
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
playlist.remove_song(&song_name);
|
||||||
|
playlist.add_song(self.ed_name.clone(), song);
|
||||||
|
*open = false;
|
||||||
|
let _ = state.manifest.save(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GuiSongEditor {
|
||||||
|
pub fn set_active_song(&mut self, pname: &String, sname: &String, url: &String) {
|
||||||
|
self.song.0 = pname.clone();
|
||||||
|
self.song.1 = sname.clone();
|
||||||
|
self.ed_name = sname.clone();
|
||||||
|
self.ed_url = url.clone();
|
||||||
|
}
|
||||||
|
}
|
72
src/ui/gui/windows/song_new.rs
Normal file
72
src/ui/gui/windows/song_new.rs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
use crate::manifest::song::{Song, SongType};
|
||||||
|
use super::{State, Window};
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct GuiNewSong {
|
||||||
|
ed_type: SongType,
|
||||||
|
ed_name: String,
|
||||||
|
ed_playlist: Option<String>,
|
||||||
|
ed_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Window for GuiNewSong {
|
||||||
|
fn ui(&mut self, state: &mut State, ctx: &egui::Context, open: &mut bool) -> anyhow::Result<()> {
|
||||||
|
let mut save = false;
|
||||||
|
egui::Window::new("New song")
|
||||||
|
.open(open)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Type: ");
|
||||||
|
egui::ComboBox::from_id_source("new_song_window_type")
|
||||||
|
.selected_text(format!("{:?}", self.ed_type))
|
||||||
|
.show_ui(ui, |ui| {
|
||||||
|
ui.selectable_value(&mut self.ed_type, SongType::Youtube, "Youtube");
|
||||||
|
ui.selectable_value(&mut self.ed_type, SongType::Spotify, "Spotify");
|
||||||
|
ui.selectable_value(&mut self.ed_type, SongType::Soundcloud, "Soundcloud");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Name: ");
|
||||||
|
ui.text_edit_singleline(&mut self.ed_name);
|
||||||
|
});
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Playlist: ");
|
||||||
|
egui::ComboBox::from_id_source("new_song_window_playlist")
|
||||||
|
.selected_text(format!("{}", self.ed_playlist.clone().unwrap_or("".to_string())))
|
||||||
|
.show_ui(ui, |ui| {
|
||||||
|
for p in state.manifest.get_playlists().keys() {
|
||||||
|
ui.selectable_value(&mut self.ed_playlist, Option::Some(p.clone()), p.as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Url: ");
|
||||||
|
ui.text_edit_singleline(&mut self.ed_url);
|
||||||
|
});
|
||||||
|
|
||||||
|
if ui.button("Save").clicked() {
|
||||||
|
save = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if save {
|
||||||
|
let Some(playlist) = state.manifest.get_playlist_mut(&self.ed_playlist.clone().unwrap()) else {
|
||||||
|
panic!("couldnt find playlist from a preset playlist list????????????");
|
||||||
|
};
|
||||||
|
|
||||||
|
playlist.add_song(
|
||||||
|
self.ed_name.clone(),
|
||||||
|
Song::from_url_str(self.ed_url.clone()).unwrap().set_type(self.ed_type.clone()).clone()
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
let _ = state.manifest.save(None);
|
||||||
|
*open = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
2
src/ui/mod.rs
Normal file
2
src/ui/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod gui;
|
||||||
|
pub mod cli;
|
Loading…
Reference in New Issue
Block a user