Remade the window adding machanism, added playlist add window
This commit is contained in:
parent
86cf5542be
commit
c2c18154b3
41
xmpd-gui/src/components/left_nav/header.rs
Normal file
41
xmpd-gui/src/components/left_nav/header.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
use uuid::Uuid;
|
||||
use crate::{components::{CompGetter, CompUi}, windows::WindowId};
|
||||
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Header {
|
||||
pub search_text: String,
|
||||
}
|
||||
|
||||
component_register!(Header);
|
||||
|
||||
impl CompUi for Header {
|
||||
fn draw(ui: &mut egui::Ui, state: &mut crate::GuiState) -> crate::Result<()> {
|
||||
let theme = xmpd_settings::Settings::get()?.theme.clone();
|
||||
|
||||
ui.vertical(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
let search_icon = egui::Image::new(crate::data::SEARCH_ICON)
|
||||
.fit_to_exact_size(egui::Vec2::new(16.0, 16.0))
|
||||
.tint(theme.accent_color);
|
||||
ui.add(search_icon);
|
||||
{
|
||||
ui.text_edit_singleline(&mut handle_error_ui!(Header::get()).search_text);
|
||||
}
|
||||
});
|
||||
//ui.with_layout(egui::Layout::top_down(egui::Align::), add_contents)
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Min), |ui| {
|
||||
let add_song = ui.add(
|
||||
egui::Image::new(crate::data::PLUS_ICON)
|
||||
.tint(theme.accent_color)
|
||||
.sense(egui::Sense::click())
|
||||
.fit_to_exact_size(egui::Vec2::new(16.0, 16.0))
|
||||
);
|
||||
if add_song.clicked() {
|
||||
state.windows.toggle(&WindowId::NewPlaylist, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
use egui::{CursorIcon, RichText, Sense};
|
||||
use egui::{CursorIcon, RichText, Sense, TextBuffer};
|
||||
use xmpd_manifest::store::BaseStore;
|
||||
use crate::utils::SearchType;
|
||||
|
||||
use super::{CompGetter, CompUi};
|
||||
|
||||
pub mod header;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct LeftNav {
|
||||
pub selected_playlist_id: Option<uuid::Uuid>,
|
||||
|
@ -26,7 +29,15 @@ impl CompUi for LeftNav {
|
|||
let b = b.1.name().to_lowercase();
|
||||
a.cmp(&b)
|
||||
});
|
||||
let search_text = handle_error_ui!(header::Header::get()).search_text.clone();
|
||||
let (qtyp, qtxt) = crate::utils::SearchType::from_str(&search_text);
|
||||
for (pid, playlist) in playlists.iter() {
|
||||
match qtyp {
|
||||
_ if qtxt.is_empty() => (),
|
||||
SearchType::Normal if playlist.name().to_lowercase().contains(&qtxt) => (),
|
||||
SearchType::Author if playlist.author().to_lowercase().contains(&qtxt) => (),
|
||||
_ => continue
|
||||
}
|
||||
add_playlist_tab(ui,
|
||||
&Some(**pid),
|
||||
playlist.name(),
|
||||
|
@ -43,6 +54,9 @@ impl CompUi for LeftNav {
|
|||
}
|
||||
|
||||
fn add_playlist_tab(ui: &mut egui::Ui, pid: &Option<uuid::Uuid>, title: &str, author: Option<&str>, song_count: usize, width: f32) {
|
||||
if pid.is_some() {
|
||||
ui.separator();
|
||||
}
|
||||
let theme = &handle_error_ui!(xmpd_settings::Settings::get()).theme;
|
||||
let wdg_rect = ui.horizontal(|ui| {
|
||||
ui.set_width(width);
|
||||
|
@ -91,7 +105,6 @@ fn add_playlist_tab(ui: &mut egui::Ui, pid: &Option<uuid::Uuid>, title: &str, au
|
|||
if blob.hovered() {
|
||||
ui.output_mut(|o| o.cursor_icon = CursorIcon::PointingHand);
|
||||
}
|
||||
ui.separator();
|
||||
}
|
||||
|
||||
|
|
@ -2,25 +2,20 @@ use uuid::Uuid;
|
|||
use xmpd_cache::DlStatus;
|
||||
use xmpd_manifest::{song::Song, store::BaseStore};
|
||||
|
||||
use crate::components::{left_nav::LeftNav, toast::ToastType, CompGetter, CompUi};
|
||||
use crate::{components::{left_nav::LeftNav, toast::ToastType, CompGetter, CompUi}, windows::WindowId};
|
||||
|
||||
use super::SongList;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SearchType {
|
||||
Name(String),
|
||||
Author(String),
|
||||
Source(String),
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SongListNav {
|
||||
text: String,
|
||||
pub struct Header {
|
||||
pub search_text: String,
|
||||
}
|
||||
|
||||
component_register!(SongListNav);
|
||||
component_register!(Header);
|
||||
|
||||
impl CompUi for SongListNav {
|
||||
impl CompUi for Header {
|
||||
fn draw(ui: &mut egui::Ui, state: &mut crate::GuiState) -> crate::Result<()> {
|
||||
let theme = xmpd_settings::Settings::get()?.theme.clone();
|
||||
let pid = {LeftNav::get()?.selected_playlist_id.clone()};
|
||||
|
@ -31,7 +26,7 @@ impl CompUi for SongListNav {
|
|||
.tint(theme.accent_color);
|
||||
ui.add(search_icon);
|
||||
{
|
||||
ui.text_edit_singleline(&mut handle_error_ui!(SongListNav::get()).text);
|
||||
ui.text_edit_singleline(&mut handle_error_ui!(Header::get()).search_text);
|
||||
}
|
||||
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::RIGHT), |ui| {
|
||||
|
@ -72,7 +67,7 @@ impl CompUi for SongListNav {
|
|||
}
|
||||
|
||||
if add_song.clicked() {
|
||||
todo!()
|
||||
state.windows.toggle(&WindowId::AddSongToPl, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -80,16 +75,8 @@ impl CompUi for SongListNav {
|
|||
}
|
||||
}
|
||||
|
||||
impl SongListNav {
|
||||
pub fn parse_search(&self) -> SearchType {
|
||||
match &self.text {
|
||||
i @ _ if i.starts_with("source:") =>
|
||||
SearchType::Source(i.strip_prefix("source:").unwrap_or("").to_string().to_lowercase()),
|
||||
i @ _ if i.starts_with("author:") =>
|
||||
SearchType::Author(i.strip_prefix("author:").unwrap_or("").to_string().to_lowercase()),
|
||||
i @ _ => SearchType::Name(i.to_string().to_lowercase())
|
||||
}
|
||||
}
|
||||
impl Header {
|
||||
|
||||
fn get_songs_to_download(songs: &Vec<uuid::Uuid>) -> crate::Result<Vec<uuid::Uuid>> {
|
||||
let mut songs2 = Vec::new();
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use egui::{Color32, CursorIcon, ImageSource, RichText, Sense, Vec2};
|
||||
use song_list_nav::SearchType;
|
||||
use xmpd_cache::DlStatus;
|
||||
use xmpd_manifest::{song::Song, store::BaseStore};
|
||||
use xmpd_manifest::{query, song::Song, store::BaseStore};
|
||||
use crate::utils::SearchType;
|
||||
|
||||
use super::{CompGetter, CompUi};
|
||||
|
||||
pub mod song_list_nav;
|
||||
pub mod header;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SongList {
|
||||
|
@ -80,27 +79,28 @@ impl SongList {
|
|||
|
||||
fn get_songs_to_display(songs: &Vec<(uuid::Uuid, Song)>) -> crate::Result<Vec<(uuid::Uuid, Song)>>{
|
||||
let mut to_display = Vec::new();
|
||||
let query = {song_list_nav::SongListNav::get()?.parse_search()}.clone();
|
||||
let query = {header::Header::get()?.search_text.clone()};
|
||||
let (query_type, query_text) = crate::utils::SearchType::from_str(&query);
|
||||
for (sid, song) in songs {
|
||||
let should_display = match &query {
|
||||
SearchType::Name(s) |
|
||||
SearchType::Author(s) |
|
||||
SearchType::Source(s) if s.is_empty() => true,
|
||||
let should_display = match &query_type {
|
||||
SearchType::Normal |
|
||||
SearchType::Author |
|
||||
SearchType::Source if query_text.is_empty() => true,
|
||||
|
||||
SearchType::Source(s) => {
|
||||
SearchType::Source => {
|
||||
song.source_type().to_string()
|
||||
.to_lowercase()
|
||||
.contains(s)
|
||||
.contains(&query_text)
|
||||
},
|
||||
SearchType::Author(s) => {
|
||||
SearchType::Author => {
|
||||
song.author()
|
||||
.to_lowercase()
|
||||
.contains(s)
|
||||
.contains(&query_text)
|
||||
},
|
||||
SearchType::Name(s) => {
|
||||
SearchType::Normal => {
|
||||
song.name()
|
||||
.to_lowercase()
|
||||
.contains(s)
|
||||
.contains(&query_text)
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -29,15 +29,22 @@ pub fn draw(ctx: &egui::Context, state: &mut GuiState, cache_rx: &Receiver<Messa
|
|||
let song_list_width = avail.x - left_nav_width - 35.0;
|
||||
ui.horizontal(|ui| {
|
||||
ui.set_height(main_height);
|
||||
ui.group(|ui| {
|
||||
ui.set_height(main_height);
|
||||
ui.set_max_width(left_nav_width);
|
||||
handle_error_ui!(crate::components::left_nav::LeftNav::draw(ui, state));
|
||||
ui.vertical(|ui| {
|
||||
ui.group(|ui| {
|
||||
//ui.set_height(main_height * 0.1);
|
||||
ui.set_max_width(left_nav_width);
|
||||
handle_error_ui!(crate::components::left_nav::header::Header::draw(ui, state));
|
||||
});
|
||||
ui.group(|ui| {
|
||||
// ui.set_height(main_height * 0.9);
|
||||
ui.set_max_width(left_nav_width);
|
||||
handle_error_ui!(crate::components::left_nav::LeftNav::draw(ui, state));
|
||||
});
|
||||
});
|
||||
ui.vertical(|ui| {
|
||||
ui.group(|ui| {
|
||||
ui.set_width(song_list_width);
|
||||
handle_error_ui!(crate::components::song_list::song_list_nav::SongListNav::draw(ui, state));
|
||||
handle_error_ui!(crate::components::song_list::header::Header::draw(ui, state));
|
||||
});
|
||||
ui.group(|ui| {
|
||||
ui.set_width(song_list_width);
|
||||
|
|
|
@ -1,4 +1,23 @@
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SearchType {
|
||||
Normal,
|
||||
Author,
|
||||
Source,
|
||||
}
|
||||
|
||||
impl SearchType {
|
||||
pub fn from_str(s: &str) -> (Self, String) {
|
||||
match s {
|
||||
i @ _ if i.starts_with("source:") =>
|
||||
(Self::Source, i.strip_prefix("source:").unwrap_or("").to_string().to_lowercase()),
|
||||
i @ _ if i.starts_with("author:") =>
|
||||
(Self::Author, i.strip_prefix("author:").unwrap_or("").to_string().to_lowercase()),
|
||||
i @ _ => (Self::Normal, i.to_string().to_lowercase())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn super_separator(ui: &mut egui::Ui, color: egui::Color32, width: f32, height: f32) {
|
||||
egui::Frame::none()
|
||||
.fill(color)
|
||||
|
|
20
xmpd-gui/src/windows/add_song.rs
Normal file
20
xmpd-gui/src/windows/add_song.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
use super::Window;
|
||||
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct AddSongW {
|
||||
|
||||
}
|
||||
|
||||
impl Window for AddSongW {
|
||||
fn id() -> super::WindowId where Self: Sized {
|
||||
super::WindowId::AddSongToPl
|
||||
}
|
||||
fn default_title() -> &'static str where Self: Sized {
|
||||
"Add Song to Playlist"
|
||||
}
|
||||
fn draw(&mut self, ui: &mut egui::Ui, _: &mut crate::GuiState) -> crate::Result<()> {
|
||||
ui.label("Hello from other window!");
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -13,6 +13,12 @@ pub struct DebugW {
|
|||
}
|
||||
|
||||
impl Window for DebugW {
|
||||
fn id() -> super::WindowId where Self: Sized {
|
||||
super::WindowId::Debug
|
||||
}
|
||||
fn default_title() -> &'static str where Self: Sized {
|
||||
"DEBUG WINDOW"
|
||||
}
|
||||
fn draw(&mut self, ui: &mut egui::Ui, _: &mut crate::GuiState) -> crate::Result<()> {
|
||||
ui.group(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
|
|
|
@ -7,6 +7,12 @@ pub struct ErrorW {
|
|||
}
|
||||
|
||||
impl Window for ErrorW {
|
||||
fn id() -> super::WindowId where Self: Sized {
|
||||
super::WindowId::Error
|
||||
}
|
||||
fn default_title() -> &'static str where Self: Sized {
|
||||
"Error!"
|
||||
}
|
||||
fn draw(&mut self, ui: &mut egui::Ui, _: &mut crate::GuiState) -> crate::Result<()> {
|
||||
ui.label("Hello from other window!");
|
||||
Ok(())
|
||||
|
|
|
@ -6,15 +6,22 @@ use crate::GuiState;
|
|||
mod debug;
|
||||
mod error;
|
||||
mod settings;
|
||||
mod add_song;
|
||||
mod new_song;
|
||||
mod new_playlist;
|
||||
|
||||
lazy_static::lazy_static!(
|
||||
static ref WINDOWS: Arc<Mutex<HashMap<WindowId, Box<dyn Window>>>> = Arc::new(Mutex::new(HashMap::new()));
|
||||
static ref OPEN_WINDOWS: Arc<Mutex<HashSet<WindowId>>> = Arc::new(Mutex::new(HashSet::new()));
|
||||
|
||||
);
|
||||
|
||||
pub trait Window: std::fmt::Debug + Send {
|
||||
fn draw(&mut self, ui: &mut egui::Ui, state: &mut GuiState) -> crate::Result<()>;
|
||||
fn id() -> WindowId where Self: Sized;
|
||||
fn default_title() -> &'static str where Self: Sized;
|
||||
fn close(&self) where Self: Sized{
|
||||
OPEN_WINDOWS.lock().unwrap().remove(&Self::id());
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd, Ord, Eq)]
|
||||
|
@ -22,7 +29,10 @@ pub enum WindowId {
|
|||
Settings,
|
||||
Error,
|
||||
#[cfg(debug_assertions)]
|
||||
Debug
|
||||
Debug,
|
||||
NewPlaylist,
|
||||
NewSong,
|
||||
AddSongToPl,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -40,18 +50,21 @@ impl Windows {
|
|||
}
|
||||
|
||||
pub fn add_all_windows(&mut self) {
|
||||
self.add_new_window(WindowId::Error, "Error!", Box::<error::ErrorW>::default());
|
||||
self.add_new_window(WindowId::Settings, "Settings", Box::<settings::SettingsW>::default());
|
||||
#[cfg(debug_assertions)]
|
||||
self.add_new_window(WindowId::Debug, "Debug", Box::<debug::DebugW>::default());
|
||||
self.add_new_window::<debug::DebugW>();
|
||||
self.add_new_window::<error::ErrorW>();
|
||||
self.add_new_window::<settings::SettingsW>();
|
||||
self.add_new_window::<add_song::AddSongW>();
|
||||
self.add_new_window::<new_song::NewSongW>();
|
||||
self.add_new_window::<new_playlist::NewPlaylistW>();
|
||||
}
|
||||
|
||||
pub fn add_new_window(&mut self, id: WindowId, title: &str, cb: Box<dyn Window>) {
|
||||
pub fn add_new_window<WT: Window + Default + 'static>(&mut self) {
|
||||
let builder = ViewportBuilder::default()
|
||||
.with_window_type(egui::X11WindowType::Dialog)
|
||||
.with_title(title);
|
||||
self.windows.insert(id.clone(), (ViewportId::from_hash_of(id.clone()), builder));
|
||||
WINDOWS.lock().unwrap().insert(id, cb);
|
||||
.with_title(WT::default_title());
|
||||
self.windows.insert(WT::id(), (ViewportId::from_hash_of(WT::id()), builder));
|
||||
WINDOWS.lock().unwrap().insert(WT::id(), Box::<WT>::default());
|
||||
}
|
||||
|
||||
pub fn draw_all(&mut self, ctx: &egui::Context, state: &mut GuiState) {
|
||||
|
|
102
xmpd-gui/src/windows/new_playlist.rs
Normal file
102
xmpd-gui/src/windows/new_playlist.rs
Normal file
|
@ -0,0 +1,102 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use egui::{Sense, Vec2};
|
||||
use xmpd_manifest::{playlist::{self, Playlist}, store::BaseStore};
|
||||
|
||||
use super::{Window, WindowId};
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NewPlaylistW {
|
||||
name: String,
|
||||
author: String,
|
||||
}
|
||||
|
||||
impl Default for NewPlaylistW {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: String::from("New Playlist"),
|
||||
author: String::from("Unknown"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Window for NewPlaylistW {
|
||||
fn id() -> WindowId where Self: Sized {
|
||||
WindowId::NewPlaylist
|
||||
}
|
||||
fn default_title() -> &'static str where Self: Sized {
|
||||
"New Playlist"
|
||||
}
|
||||
fn draw(&mut self, ui: &mut egui::Ui, state: &mut crate::GuiState) -> crate::Result<()> {
|
||||
let theme = xmpd_settings::Settings::get()?.theme.clone();
|
||||
let img_size = 64.0;
|
||||
let img_spacing = 10.0;
|
||||
ui.vertical(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
let mut rect = egui::Rect::ZERO;
|
||||
rect.set_width(img_size);
|
||||
rect.set_height(img_size);
|
||||
rect.set_top(img_spacing);
|
||||
rect.set_left(img_spacing);
|
||||
let rect_int = ui.interact(rect, "new_playlist_w".into(), Sense::click());
|
||||
if rect_int.hovered() {
|
||||
ui.allocate_ui_at_rect(rect, |ui| {
|
||||
ui.group(|ui| {
|
||||
let img = egui::Image::new(crate::data::PLUS_ICON)
|
||||
.tint(theme.accent_color)
|
||||
.fit_to_exact_size(Vec2::new(img_size, img_size));
|
||||
//.paint_at(ui, rect);
|
||||
ui.add(img);
|
||||
});
|
||||
});
|
||||
|
||||
} else {
|
||||
ui.allocate_ui_at_rect(rect, |ui| {
|
||||
ui.group(|ui| {
|
||||
let img = egui::Image::new(crate::data::NOTE_ICON)
|
||||
.tint(theme.accent_color)
|
||||
.fit_to_exact_size(Vec2::new(img_size, img_size));
|
||||
//.paint_at(ui, rect);
|
||||
ui.add(img);
|
||||
});
|
||||
});
|
||||
}
|
||||
if rect_int.clicked() {
|
||||
// TODO: Add a way to add custom icons
|
||||
}
|
||||
ui.vertical(|ui| {
|
||||
ui.add_space(img_spacing);
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Name: ");
|
||||
ui.text_edit_singleline(&mut self.name);
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Author: ");
|
||||
ui.text_edit_singleline(&mut self.author);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.with_layout(egui::Layout::bottom_up(egui::Align::Max), |ui| {
|
||||
ui.add_space(3.0);
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_space(3.0);
|
||||
if ui.button("Cancel").clicked() {
|
||||
self.author = String::from("New Playlist");
|
||||
self.name = String::from("Unknown");
|
||||
state.windows.toggle(&WindowId::NewPlaylist, false);
|
||||
}
|
||||
if ui.button("Add").clicked() {
|
||||
let mut playlist = Playlist::default();
|
||||
playlist.set_name(&self.name);
|
||||
playlist.set_author(&self.author);
|
||||
let playlists = state.manifest.store_mut().get_playlists_mut();
|
||||
playlists.insert(uuid::Uuid::new_v4(), playlist);
|
||||
state.windows.toggle(&WindowId::NewPlaylist, false);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
20
xmpd-gui/src/windows/new_song.rs
Normal file
20
xmpd-gui/src/windows/new_song.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
use super::Window;
|
||||
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct NewSongW {
|
||||
|
||||
}
|
||||
|
||||
impl Window for NewSongW {
|
||||
fn id() -> super::WindowId where Self: Sized {
|
||||
super::WindowId::NewSong
|
||||
}
|
||||
fn default_title() -> &'static str where Self: Sized {
|
||||
"New Song"
|
||||
}
|
||||
fn draw(&mut self, ui: &mut egui::Ui, _: &mut crate::GuiState) -> crate::Result<()> {
|
||||
ui.label("Hello from other window!");
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -25,6 +25,12 @@ impl Default for SettingsW {
|
|||
|
||||
|
||||
impl Window for SettingsW {
|
||||
fn id() -> super::WindowId where Self: Sized {
|
||||
super::WindowId::Settings
|
||||
}
|
||||
fn default_title() -> &'static str where Self: Sized {
|
||||
"Settings"
|
||||
}
|
||||
#[allow(irrefutable_let_patterns)]
|
||||
fn draw(&mut self, ui: &mut egui::Ui, _: &mut crate::GuiState) -> crate::Result<()> {
|
||||
ui.group(|ui| {
|
||||
|
|
Loading…
Reference in New Issue
Block a user