diff --git a/musicdb-client/src/config_gui.toml b/musicdb-client/src/config_gui.toml old mode 100644 new mode 100755 index 70250d2..13ed205 --- a/musicdb-client/src/config_gui.toml +++ b/musicdb-client/src/config_gui.toml @@ -29,4 +29,4 @@ font = '' # If we know the title, write it. If not, write "(no title found)" instead. status_bar = '''\t -\s0.5;?\A#\c505050by \c593D6E\A##?\a#?\A# ##\c505050on \c264524\a##?%>Year=%#\c808080 (%>Year=%)##''' +\s0.5;?\A#\c505050by \c593D6E\A##?\a#?\A# ##\c505050on \c264524\a##\c808080?%>Year=%# (%>Year=%)## | \d''' diff --git a/musicdb-client/src/gui.rs b/musicdb-client/src/gui.rs index 6dbf7c5..0886b03 100755 --- a/musicdb-client/src/gui.rs +++ b/musicdb-client/src/gui.rs @@ -257,7 +257,8 @@ impl Gui { | Command::ModifyArtist(_) | Command::RemoveSong(_) | Command::RemoveAlbum(_) - | Command::RemoveArtist(_) => { + | Command::RemoveArtist(_) + | Command::SetSongDuration(..) => { if let Some(s) = &*event_sender_arc.lock().unwrap() { _ = s.send_event(GuiEvent::UpdatedLibrary); } diff --git a/musicdb-client/src/gui_library.rs b/musicdb-client/src/gui_library.rs index 0824678..e85616b 100755 --- a/musicdb-client/src/gui_library.rs +++ b/musicdb-client/src/gui_library.rs @@ -27,7 +27,7 @@ use speedy2d::{ use crate::{ gui::{Dragging, DrawInfo, GuiAction, GuiElem, GuiElemCfg, GuiElemTrait}, gui_base::{Button, Panel, ScrollBox}, - gui_text::{Label, TextField}, + gui_text::{self, AdvancedLabel, Label, TextField}, gui_wrappers::WithFocusHotkey, }; @@ -71,148 +71,13 @@ pub struct LibraryBrowser { filter_albums: Arc>, filter_artists: Arc>, do_something_receiver: mpsc::Receiver>, + selected_popup_state: (f32, usize, usize, usize), } impl Clone for LibraryBrowser { fn clone(&self) -> Self { Self::new(self.config.clone()) } } -mod selected { - use super::*; - #[derive(Clone)] - pub struct Selected( - // artist, album, songs - Arc, HashSet, HashSet)>>, - Arc, - ); - impl Selected { - pub fn new(update: Arc) -> Self { - Self(Default::default(), update) - } - pub fn clear(&self) { - self.set_to(HashSet::new(), HashSet::new(), HashSet::new()) - } - pub fn set_to(&self, artists: HashSet, albums: HashSet, songs: HashSet) { - let mut s = self.0.lock().unwrap(); - s.0 = artists; - s.1 = albums; - s.2 = songs; - self.changed(); - } - pub fn contains_artist(&self, id: &ArtistId) -> bool { - self.0.lock().unwrap().0.contains(id) - } - pub fn contains_album(&self, id: &AlbumId) -> bool { - self.0.lock().unwrap().1.contains(id) - } - pub fn contains_song(&self, id: &SongId) -> bool { - self.0.lock().unwrap().2.contains(id) - } - pub fn insert_artist(&self, id: ArtistId) -> bool { - self.changed(); - self.0.lock().unwrap().0.insert(id) - } - pub fn insert_album(&self, id: AlbumId) -> bool { - self.changed(); - self.0.lock().unwrap().1.insert(id) - } - pub fn insert_song(&self, id: SongId) -> bool { - self.changed(); - self.0.lock().unwrap().2.insert(id) - } - pub fn remove_artist(&self, id: &ArtistId) -> bool { - self.changed(); - self.0.lock().unwrap().0.remove(id) - } - pub fn remove_album(&self, id: &AlbumId) -> bool { - self.changed(); - self.0.lock().unwrap().1.remove(id) - } - pub fn remove_song(&self, id: &SongId) -> bool { - self.changed(); - self.0.lock().unwrap().2.remove(id) - } - pub fn view( - &self, - f: impl FnOnce(&(HashSet, HashSet, HashSet)) -> T, - ) -> T { - f(&self.0.lock().unwrap()) - } - pub fn view_mut( - &self, - f: impl FnOnce(&mut (HashSet, HashSet, HashSet)) -> T, - ) -> T { - let v = f(&mut self.0.lock().unwrap()); - self.changed(); - v - } - fn changed(&self) { - self.1.store(true, std::sync::atomic::Ordering::Relaxed); - } - pub fn as_queue(&self, lb: &LibraryBrowser, db: &Database) -> Vec { - let lock = self.0.lock().unwrap(); - let (sel_artists, sel_albums, sel_songs) = &*lock; - let mut out = vec![]; - for (artist, singles, albums, _) in &lb.library_filtered { - let artist_selected = sel_artists.contains(artist); - let mut local_artist_owned = vec![]; - let mut local_artist = if artist_selected { - &mut local_artist_owned - } else { - &mut out - }; - for (song, _) in singles { - let song_selected = sel_songs.contains(song); - if song_selected { - local_artist.push(QueueContent::Song(*song).into()); - } - } - for (album, songs, _) in albums { - let album_selected = sel_albums.contains(album); - let mut local_album_owned = vec![]; - let local_album = if album_selected { - &mut local_album_owned - } else { - &mut local_artist - }; - for (song, _) in songs { - let song_selected = sel_songs.contains(song); - if song_selected { - local_album.push(QueueContent::Song(*song).into()); - } - } - if album_selected { - local_artist.push( - QueueContent::Folder( - 0, - local_album_owned, - match db.albums().get(album) { - Some(v) => v.name.clone(), - None => "< unknown album >".to_owned(), - }, - ) - .into(), - ); - } - } - if artist_selected { - out.push( - QueueContent::Folder( - 0, - local_artist_owned, - match db.artists().get(artist) { - Some(v) => v.name.to_owned(), - None => "< unknown artist >".to_owned(), - }, - ) - .into(), - ); - } - } - out - } - } -} fn search_regex_new(pat: &str, case_insensitive: bool) -> Result, regex::Error> { if pat.is_empty() { Ok(None) @@ -312,6 +177,17 @@ impl LibraryBrowser { selected.clone(), do_something_sender.clone(), )), + GuiElem::new(Panel::with_background( + GuiElemCfg::default().disabled(), + vec![GuiElem::new(Label::new( + GuiElemCfg::default(), + String::new(), + Color::LIGHT_GRAY, + None, + Vec2::new(0.5, 0.5), + ))], + Color::from_rgba(0.0, 0.0, 0.0, 0.8), + )), ], // - - - library_sorted: vec![], @@ -336,6 +212,7 @@ impl LibraryBrowser { filter_albums, filter_artists, do_something_receiver, + selected_popup_state: (0.0, 0, 0, 0), } } pub fn selected_add_all(&self) { @@ -400,6 +277,9 @@ impl GuiElemTrait for LibraryBrowser { fn clone_gui(&self) -> Box { Box::new(self.clone()) } + fn draw_rev(&self) -> bool { + false + } fn draw(&mut self, info: &mut DrawInfo, _g: &mut speedy2d::Graphics2D) { loop { if let Ok(action) = self.do_something_receiver.try_recv() { @@ -621,8 +501,121 @@ impl GuiElemTrait for LibraryBrowser { } }, ); + // selected + { + let (artists, albums, songs) = self + .selected + .view(|sel| (sel.0.len(), sel.1.len(), sel.2.len())); + if self.selected_popup_state.1 != artists + || self.selected_popup_state.2 != albums + || self.selected_popup_state.3 != songs + { + self.selected_popup_state.1 = artists; + self.selected_popup_state.2 = albums; + self.selected_popup_state.3 = songs; + if artists > 0 || albums > 0 || songs > 0 { + if let Some(text) = match (artists, albums, songs) { + (0, 0, 0) => None, + + (0, 0, 1) => Some(format!("1 song selected")), + (0, 0, s) => Some(format!("{s} songs selected")), + + (0, 1, 0) => Some(format!("1 album selected")), + (0, al, 0) => Some(format!("{al} albums selected")), + + (1, 0, 0) => Some(format!("1 artist selected")), + (ar, 0, 0) => Some(format!("{ar} artists selected")), + + (0, 1, 1) => Some(format!("1 song and 1 album selected")), + (0, 1, s) => Some(format!("{s} songs and 1 album selected")), + (0, al, 1) => Some(format!("1 song and {al} albums selected")), + (0, al, s) => Some(format!("{s} songs and {al} albums selected")), + + (1, 0, 1) => Some(format!("1 song and 1 artist selected")), + (1, 0, s) => Some(format!("{s} songs and 1 artist selected")), + (ar, 0, 1) => Some(format!("1 song and {ar} artists selected")), + (ar, 0, s) => Some(format!("{s} songs and {ar} artists selected")), + + (1, 1, 0) => Some(format!("1 album and 1 artist selected")), + (1, al, 0) => Some(format!("{al} albums and 1 artist selected")), + (ar, 1, 0) => Some(format!("1 album and {ar} artists selected")), + (ar, al, 0) => Some(format!("{al} albums and {ar} artists selected")), + + (1, 1, 1) => Some(format!("1 song, 1 album and 1 artist selected")), + (1, 1, s) => Some(format!("{s} songs, 1 album and 1 artist selected")), + (1, al, 1) => { + Some(format!("1 song, {al} albums and 1 artist selected")) + } + (ar, 1, 1) => { + Some(format!("1 song, 1 album and {ar} artists selected")) + } + (1, al, s) => { + Some(format!("{s} songs, {al} albums and 1 artist selected")) + } + (ar, 1, s) => { + Some(format!("{s} songs, 1 album and {ar} artists selected")) + } + (ar, al, 1) => { + Some(format!("1 song, {al} albums and {ar} artist selected")) + } + (ar, al, s) => { + Some(format!("{s} songs, {al} albums and {ar} artists selected")) + } + } { + *self.children[6] + .inner + .any_mut() + .downcast_mut::() + .unwrap() + .children[0] + .inner + .any_mut() + .downcast_mut::