This commit is contained in:
Mark 2023-10-26 16:18:52 +02:00
parent 5161b5577d
commit f61f52fdc7
5 changed files with 240 additions and 123 deletions

View File

@ -44,6 +44,35 @@ pub enum GuiEvent {
Exit, Exit,
} }
pub fn hotkey_deselect_all(modifiers: &ModifiersState, key: Option<VirtualKeyCode>) -> bool {
!modifiers.logo()
&& !modifiers.alt()
&& modifiers.ctrl()
&& !modifiers.shift()
&& matches!(key, Some(VirtualKeyCode::S))
}
pub fn hotkey_select_all(modifiers: &ModifiersState, key: Option<VirtualKeyCode>) -> bool {
!modifiers.logo()
&& !modifiers.alt()
&& modifiers.ctrl()
&& !modifiers.shift()
&& matches!(key, Some(VirtualKeyCode::A))
}
pub fn hotkey_select_albums(modifiers: &ModifiersState, key: Option<VirtualKeyCode>) -> bool {
!modifiers.logo()
&& !modifiers.alt()
&& modifiers.ctrl()
&& modifiers.shift()
&& matches!(key, Some(VirtualKeyCode::A))
}
pub fn hotkey_select_songs(modifiers: &ModifiersState, key: Option<VirtualKeyCode>) -> bool {
!modifiers.logo()
&& !modifiers.alt()
&& modifiers.ctrl()
&& modifiers.shift()
&& matches!(key, Some(VirtualKeyCode::S))
}
pub fn main( pub fn main(
database: Arc<Mutex<Database>>, database: Arc<Mutex<Database>>,
connection: TcpStream, connection: TcpStream,
@ -234,7 +263,6 @@ impl Gui {
} }
} }
Command::ErrorInfo(t, d) => { Command::ErrorInfo(t, d) => {
eprintln!("{t:?} | {d:?}");
let (t, d) = (t.clone(), d.clone()); let (t, d) = (t.clone(), d.clone());
notif_sender notif_sender
.send(Box::new(move |_| { .send(Box::new(move |_| {

View File

@ -31,6 +31,8 @@ use crate::{
gui_wrappers::WithFocusHotkey, gui_wrappers::WithFocusHotkey,
}; };
use self::selected::Selected;
/* /*
This is responsible for showing the library, This is responsible for showing the library,
@ -75,70 +77,140 @@ impl Clone for LibraryBrowser {
Self::new(self.config.clone()) Self::new(self.config.clone())
} }
} }
#[derive(Clone)] mod selected {
struct Selected(Arc<Mutex<(HashSet<ArtistId>, HashSet<AlbumId>, HashSet<SongId>)>>); use super::*;
impl Selected { #[derive(Clone)]
pub fn as_queue(&self, lb: &LibraryBrowser, db: &Database) -> Vec<Queue> { pub struct Selected(
let lock = self.0.lock().unwrap(); // artist, album, songs
let (sel_artists, sel_albums, sel_songs) = &*lock; Arc<Mutex<(HashSet<ArtistId>, HashSet<AlbumId>, HashSet<SongId>)>>,
let mut out = vec![]; Arc<AtomicBool>,
for (artist, singles, albums, _) in &lb.library_filtered { );
let artist_selected = sel_artists.contains(artist); impl Selected {
let mut local_artist_owned = vec![]; pub fn new(update: Arc<AtomicBool>) -> Self {
let mut local_artist = if artist_selected { Self(Default::default(), update)
&mut local_artist_owned }
} else { pub fn clear(&self) {
&mut out self.set_to(HashSet::new(), HashSet::new(), HashSet::new())
}; }
for (song, _) in singles { pub fn set_to(&self, artists: HashSet<u64>, albums: HashSet<u64>, songs: HashSet<u64>) {
let song_selected = sel_songs.contains(song); let mut s = self.0.lock().unwrap();
if song_selected { s.0 = artists;
local_artist.push(QueueContent::Song(*song).into()); s.1 = albums;
} s.2 = songs;
} self.changed();
for (album, songs, _) in albums { }
let album_selected = sel_albums.contains(album); pub fn contains_artist(&self, id: &ArtistId) -> bool {
let mut local_album_owned = vec![]; self.0.lock().unwrap().0.contains(id)
let local_album = if album_selected { }
&mut local_album_owned 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<T>(
&self,
f: impl FnOnce(&(HashSet<ArtistId>, HashSet<AlbumId>, HashSet<SongId>)) -> T,
) -> T {
f(&self.0.lock().unwrap())
}
pub fn view_mut<T>(
&self,
f: impl FnOnce(&mut (HashSet<ArtistId>, HashSet<AlbumId>, HashSet<SongId>)) -> 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<Queue> {
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 { } else {
&mut local_artist &mut out
}; };
for (song, _) in songs { for (song, _) in singles {
let song_selected = sel_songs.contains(song); let song_selected = sel_songs.contains(song);
if song_selected { if song_selected {
local_album.push(QueueContent::Song(*song).into()); local_artist.push(QueueContent::Song(*song).into());
} }
} }
if album_selected { for (album, songs, _) in albums {
local_artist.push( 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( QueueContent::Folder(
0, 0,
local_album_owned, local_artist_owned,
match db.albums().get(album) { match db.artists().get(artist) {
Some(v) => v.name.clone(), Some(v) => v.name.to_owned(),
None => "< unknown album >".to_owned(), None => "< unknown artist >".to_owned(),
}, },
) )
.into(), .into(),
); );
} }
} }
if artist_selected { out
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<Option<Regex>, regex::Error> { fn search_regex_new(pat: &str, case_insensitive: bool) -> Result<Option<Regex>, regex::Error> {
@ -221,13 +293,9 @@ impl LibraryBrowser {
and: true, and: true,
filters: vec![], filters: vec![],
})); }));
let selected = Selected(Arc::new(Mutex::new(( let selected = Selected::new(Arc::clone(&search_settings_changed));
HashSet::new(),
HashSet::new(),
HashSet::new(),
))));
Self { Self {
config, config: config.w_keyboard_watch(),
children: vec![ children: vec![
GuiElem::new(search_artist), GuiElem::new(search_artist),
GuiElem::new(search_album), GuiElem::new(search_album),
@ -270,6 +338,48 @@ impl LibraryBrowser {
do_something_receiver, do_something_receiver,
} }
} }
pub fn selected_add_all(&self) {
self.selected.view_mut(|sel| {
for (id, singles, albums, _) in &self.library_filtered {
sel.0.insert(*id);
for (s, _) in singles {
sel.2.insert(*s);
}
for (id, album, _) in albums {
sel.1.insert(*id);
for (s, _) in album {
sel.2.insert(*s);
}
}
}
})
}
pub fn selected_add_songs(&self) {
self.selected.view_mut(|sel| {
for (_, singles, albums, _) in &self.library_filtered {
for (s, _) in singles {
sel.2.insert(*s);
}
for (_, album, _) in albums {
for (s, _) in album {
sel.2.insert(*s);
}
}
}
})
}
pub fn selected_add_albums(&self) {
self.selected.view_mut(|sel| {
for (_, _, albums, _) in &self.library_filtered {
for (id, album, _) in albums {
sel.1.insert(*id);
for (s, _) in album {
sel.2.insert(*s);
}
}
}
})
}
} }
impl GuiElemTrait for LibraryBrowser { impl GuiElemTrait for LibraryBrowser {
fn config(&self) -> &GuiElemCfg { fn config(&self) -> &GuiElemCfg {
@ -521,6 +631,27 @@ impl GuiElemTrait for LibraryBrowser {
fn updated_library(&mut self) { fn updated_library(&mut self) {
self.library_updated = true; self.library_updated = true;
} }
fn key_watch(
&mut self,
modifiers: speedy2d::window::ModifiersState,
down: bool,
key: Option<VirtualKeyCode>,
scan: speedy2d::window::KeyScancode,
) -> Vec<GuiAction> {
if down && crate::gui::hotkey_deselect_all(&modifiers, key) {
self.selected.clear();
}
if down && crate::gui::hotkey_select_all(&modifiers, key) {
self.selected_add_all();
}
if down && crate::gui::hotkey_select_albums(&modifiers, key) {
self.selected_add_albums();
}
if down && crate::gui::hotkey_select_songs(&modifiers, key) {
self.selected_add_songs();
}
vec![]
}
} }
impl LibraryBrowser { impl LibraryBrowser {
/// Sets `self.library_sorted` based on the contents of the `Database`. /// Sets `self.library_sorted` based on the contents of the `Database`.
@ -757,7 +888,7 @@ impl GuiElemTrait for ListArtist {
fn draw(&mut self, info: &mut DrawInfo, _g: &mut speedy2d::Graphics2D) { fn draw(&mut self, info: &mut DrawInfo, _g: &mut speedy2d::Graphics2D) {
if self.config.redraw { if self.config.redraw {
self.config.redraw = false; self.config.redraw = false;
let sel = self.selected.0.lock().unwrap().0.contains(&self.id); let sel = self.selected.contains_artist(&self.id);
if sel != self.sel { if sel != self.sel {
self.sel = sel; self.sel = sel;
if sel { if sel {
@ -783,7 +914,7 @@ impl GuiElemTrait for ListArtist {
gui.gui gui.gui
.inner .inner
.children() .children()
.nth(2) .nth(3)
.unwrap() .unwrap()
.inner .inner
.children() .children()
@ -838,9 +969,9 @@ impl GuiElemTrait for ListArtist {
self.mouse = false; self.mouse = false;
self.config.redraw = true; self.config.redraw = true;
if !self.sel { if !self.sel {
self.selected.0.lock().unwrap().0.insert(self.id); self.selected.insert_artist(self.id);
} else { } else {
self.selected.0.lock().unwrap().0.remove(&self.id); self.selected.remove_artist(&self.id);
} }
} }
vec![] vec![]
@ -900,7 +1031,7 @@ impl GuiElemTrait for ListAlbum {
fn draw(&mut self, info: &mut DrawInfo, _g: &mut speedy2d::Graphics2D) { fn draw(&mut self, info: &mut DrawInfo, _g: &mut speedy2d::Graphics2D) {
if self.config.redraw { if self.config.redraw {
self.config.redraw = false; self.config.redraw = false;
let sel = self.selected.0.lock().unwrap().1.contains(&self.id); let sel = self.selected.contains_album(&self.id);
if sel != self.sel { if sel != self.sel {
self.sel = sel; self.sel = sel;
if sel { if sel {
@ -926,7 +1057,7 @@ impl GuiElemTrait for ListAlbum {
gui.gui gui.gui
.inner .inner
.children() .children()
.nth(2) .nth(3)
.unwrap() .unwrap()
.inner .inner
.children() .children()
@ -981,9 +1112,9 @@ impl GuiElemTrait for ListAlbum {
self.mouse = false; self.mouse = false;
self.config.redraw = true; self.config.redraw = true;
if !self.sel { if !self.sel {
self.selected.0.lock().unwrap().1.insert(self.id); self.selected.insert_album(self.id);
} else { } else {
self.selected.0.lock().unwrap().1.remove(&self.id); self.selected.remove_album(&self.id);
} }
} }
vec![] vec![]
@ -1043,7 +1174,7 @@ impl GuiElemTrait for ListSong {
fn draw(&mut self, info: &mut DrawInfo, _g: &mut speedy2d::Graphics2D) { fn draw(&mut self, info: &mut DrawInfo, _g: &mut speedy2d::Graphics2D) {
if self.config.redraw { if self.config.redraw {
self.config.redraw = false; self.config.redraw = false;
let sel = self.selected.0.lock().unwrap().2.contains(&self.id); let sel = self.selected.contains_song(&self.id);
if sel != self.sel { if sel != self.sel {
self.sel = sel; self.sel = sel;
if sel { if sel {
@ -1069,7 +1200,7 @@ impl GuiElemTrait for ListSong {
gui.gui gui.gui
.inner .inner
.children() .children()
.nth(2) .nth(3)
.unwrap() .unwrap()
.inner .inner
.children() .children()
@ -1124,9 +1255,9 @@ impl GuiElemTrait for ListSong {
self.mouse = false; self.mouse = false;
self.config.redraw = true; self.config.redraw = true;
if !self.sel { if !self.sel {
self.selected.0.lock().unwrap().2.insert(self.id); self.selected.insert_song(self.id);
} else { } else {
self.selected.0.lock().unwrap().2.remove(&self.id); self.selected.remove_song(&self.id);
} }
} }
vec![] vec![]
@ -1165,7 +1296,6 @@ impl FilterPanel {
search_prefer_start_matches.load(std::sync::atomic::Ordering::Relaxed); search_prefer_start_matches.load(std::sync::atomic::Ordering::Relaxed);
let ssc1 = Arc::clone(&search_settings_changed); let ssc1 = Arc::clone(&search_settings_changed);
let ssc2 = Arc::clone(&search_settings_changed); let ssc2 = Arc::clone(&search_settings_changed);
let ssc3 = Arc::clone(&search_settings_changed);
let sel3 = selected.clone(); let sel3 = selected.clone();
const VSPLIT: f32 = 0.4; const VSPLIT: f32 = 0.4;
let tab_main = GuiElem::new(ScrollBox::new( let tab_main = GuiElem::new(ScrollBox::new(
@ -1249,11 +1379,7 @@ impl FilterPanel {
GuiElem::new(Button::new( GuiElem::new(Button::new(
GuiElemCfg::default(), GuiElemCfg::default(),
move |_| { move |_| {
ssc3.store(true, std::sync::atomic::Ordering::Relaxed); let mut sel = sel3.clear();
let mut sel = sel3.0.lock().unwrap();
sel.2 = HashSet::new();
sel.1 = HashSet::new();
sel.0 = HashSet::new();
vec![] vec![]
}, },
vec![GuiElem::new(Label::new( vec![GuiElem::new(Label::new(
@ -1275,24 +1401,7 @@ impl FilterPanel {
{ {
let dss = do_something_sender.clone(); let dss = do_something_sender.clone();
move |_| { move |_| {
dss.send(Box::new(|s| { dss.send(Box::new(|s| s.selected_add_all())).unwrap();
s.search_settings_changed
.store(true, std::sync::atomic::Ordering::Relaxed);
let mut sel = s.selected.0.lock().unwrap();
for (id, singles, albums, _) in &s.library_filtered {
sel.0.insert(*id);
for (s, _) in singles {
sel.2.insert(*s);
}
for (id, album, _) in albums {
sel.1.insert(*id);
for (s, _) in album {
sel.2.insert(*s);
}
}
}
}))
.unwrap();
vec![] vec![]
} }
}, },
@ -1309,22 +1418,7 @@ impl FilterPanel {
{ {
let dss = do_something_sender.clone(); let dss = do_something_sender.clone();
move |_| { move |_| {
dss.send(Box::new(|s| { dss.send(Box::new(|s| s.selected_add_songs())).unwrap();
s.search_settings_changed
.store(true, std::sync::atomic::Ordering::Relaxed);
let mut sel = s.selected.0.lock().unwrap();
for (_, singles, albums, _) in &s.library_filtered {
for (s, _) in singles {
sel.2.insert(*s);
}
for (_, album, _) in albums {
for (s, _) in album {
sel.2.insert(*s);
}
}
}
}))
.unwrap();
vec![] vec![]
} }
}, },
@ -1341,20 +1435,7 @@ impl FilterPanel {
{ {
let dss = do_something_sender.clone(); let dss = do_something_sender.clone();
move |_| { move |_| {
dss.send(Box::new(|s| { dss.send(Box::new(|s| s.selected_add_albums())).unwrap();
s.search_settings_changed
.store(true, std::sync::atomic::Ordering::Relaxed);
let mut sel = s.selected.0.lock().unwrap();
for (_, _, albums, _) in &s.library_filtered {
for (id, album, _) in albums {
sel.1.insert(*id);
for (s, _) in album {
sel.2.insert(*s);
}
}
}
}))
.unwrap();
vec![] vec![]
} }
}, },

View File

@ -185,7 +185,7 @@ impl GuiElemTrait for NotifOverlay {
} }
} }
fn draw_rev(&self) -> bool { fn draw_rev(&self) -> bool {
true false
} }
fn config(&self) -> &GuiElemCfg { fn config(&self) -> &GuiElemCfg {

View File

@ -23,6 +23,9 @@ This file could probably have a better name.
#[derive(Clone)] #[derive(Clone)]
pub struct CurrentSong { pub struct CurrentSong {
config: GuiElemCfg, config: GuiElemCfg,
/// 0: AdvancedLabel for small mode
/// 1: AdvancedLabel for big mode heading
/// 2: AdvancedLabel for big mode info text
children: Vec<GuiElem>, children: Vec<GuiElem>,
prev_song: Option<SongId>, prev_song: Option<SongId>,
cover_pos: Rectangle, cover_pos: Rectangle,

View File

@ -341,6 +341,11 @@ impl GuiElemTrait for GuiScreen {
Command::Resume Command::Resume
})] })]
}))] }))]
} else if down && matches!(key, Some(VirtualKeyCode::F8)) {
vec![GuiAction::SendToServer(Command::ErrorInfo(
"".to_owned(),
"tEsT".to_owned(),
))]
} else { } else {
vec![] vec![]
} }