mirror of
https://github.com/Dummi26/musicdb.git
synced 2025-03-10 05:43:53 +01:00
client
This commit is contained in:
parent
5161b5577d
commit
f61f52fdc7
@ -44,6 +44,35 @@ pub enum GuiEvent {
|
||||
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(
|
||||
database: Arc<Mutex<Database>>,
|
||||
connection: TcpStream,
|
||||
@ -234,7 +263,6 @@ impl Gui {
|
||||
}
|
||||
}
|
||||
Command::ErrorInfo(t, d) => {
|
||||
eprintln!("{t:?} | {d:?}");
|
||||
let (t, d) = (t.clone(), d.clone());
|
||||
notif_sender
|
||||
.send(Box::new(move |_| {
|
||||
|
@ -31,6 +31,8 @@ use crate::{
|
||||
gui_wrappers::WithFocusHotkey,
|
||||
};
|
||||
|
||||
use self::selected::Selected;
|
||||
|
||||
/*
|
||||
|
||||
This is responsible for showing the library,
|
||||
@ -75,70 +77,140 @@ impl Clone for LibraryBrowser {
|
||||
Self::new(self.config.clone())
|
||||
}
|
||||
}
|
||||
#[derive(Clone)]
|
||||
struct Selected(Arc<Mutex<(HashSet<ArtistId>, HashSet<AlbumId>, HashSet<SongId>)>>);
|
||||
impl Selected {
|
||||
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 {
|
||||
&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
|
||||
mod selected {
|
||||
use super::*;
|
||||
#[derive(Clone)]
|
||||
pub struct Selected(
|
||||
// artist, album, songs
|
||||
Arc<Mutex<(HashSet<ArtistId>, HashSet<AlbumId>, HashSet<SongId>)>>,
|
||||
Arc<AtomicBool>,
|
||||
);
|
||||
impl Selected {
|
||||
pub fn new(update: Arc<AtomicBool>) -> 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<u64>, albums: HashSet<u64>, songs: HashSet<u64>) {
|
||||
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<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 {
|
||||
&mut local_artist
|
||||
&mut out
|
||||
};
|
||||
for (song, _) in songs {
|
||||
for (song, _) in singles {
|
||||
let song_selected = sel_songs.contains(song);
|
||||
if song_selected {
|
||||
local_album.push(QueueContent::Song(*song).into());
|
||||
local_artist.push(QueueContent::Song(*song).into());
|
||||
}
|
||||
}
|
||||
if album_selected {
|
||||
local_artist.push(
|
||||
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_album_owned,
|
||||
match db.albums().get(album) {
|
||||
Some(v) => v.name.clone(),
|
||||
None => "< unknown album >".to_owned(),
|
||||
local_artist_owned,
|
||||
match db.artists().get(artist) {
|
||||
Some(v) => v.name.to_owned(),
|
||||
None => "< unknown artist >".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
|
||||
}
|
||||
out
|
||||
}
|
||||
}
|
||||
fn search_regex_new(pat: &str, case_insensitive: bool) -> Result<Option<Regex>, regex::Error> {
|
||||
@ -221,13 +293,9 @@ impl LibraryBrowser {
|
||||
and: true,
|
||||
filters: vec![],
|
||||
}));
|
||||
let selected = Selected(Arc::new(Mutex::new((
|
||||
HashSet::new(),
|
||||
HashSet::new(),
|
||||
HashSet::new(),
|
||||
))));
|
||||
let selected = Selected::new(Arc::clone(&search_settings_changed));
|
||||
Self {
|
||||
config,
|
||||
config: config.w_keyboard_watch(),
|
||||
children: vec![
|
||||
GuiElem::new(search_artist),
|
||||
GuiElem::new(search_album),
|
||||
@ -270,6 +338,48 @@ impl LibraryBrowser {
|
||||
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 {
|
||||
fn config(&self) -> &GuiElemCfg {
|
||||
@ -521,6 +631,27 @@ impl GuiElemTrait for LibraryBrowser {
|
||||
fn updated_library(&mut self) {
|
||||
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 {
|
||||
/// 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) {
|
||||
if self.config.redraw {
|
||||
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 {
|
||||
self.sel = sel;
|
||||
if sel {
|
||||
@ -783,7 +914,7 @@ impl GuiElemTrait for ListArtist {
|
||||
gui.gui
|
||||
.inner
|
||||
.children()
|
||||
.nth(2)
|
||||
.nth(3)
|
||||
.unwrap()
|
||||
.inner
|
||||
.children()
|
||||
@ -838,9 +969,9 @@ impl GuiElemTrait for ListArtist {
|
||||
self.mouse = false;
|
||||
self.config.redraw = true;
|
||||
if !self.sel {
|
||||
self.selected.0.lock().unwrap().0.insert(self.id);
|
||||
self.selected.insert_artist(self.id);
|
||||
} else {
|
||||
self.selected.0.lock().unwrap().0.remove(&self.id);
|
||||
self.selected.remove_artist(&self.id);
|
||||
}
|
||||
}
|
||||
vec![]
|
||||
@ -900,7 +1031,7 @@ impl GuiElemTrait for ListAlbum {
|
||||
fn draw(&mut self, info: &mut DrawInfo, _g: &mut speedy2d::Graphics2D) {
|
||||
if self.config.redraw {
|
||||
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 {
|
||||
self.sel = sel;
|
||||
if sel {
|
||||
@ -926,7 +1057,7 @@ impl GuiElemTrait for ListAlbum {
|
||||
gui.gui
|
||||
.inner
|
||||
.children()
|
||||
.nth(2)
|
||||
.nth(3)
|
||||
.unwrap()
|
||||
.inner
|
||||
.children()
|
||||
@ -981,9 +1112,9 @@ impl GuiElemTrait for ListAlbum {
|
||||
self.mouse = false;
|
||||
self.config.redraw = true;
|
||||
if !self.sel {
|
||||
self.selected.0.lock().unwrap().1.insert(self.id);
|
||||
self.selected.insert_album(self.id);
|
||||
} else {
|
||||
self.selected.0.lock().unwrap().1.remove(&self.id);
|
||||
self.selected.remove_album(&self.id);
|
||||
}
|
||||
}
|
||||
vec![]
|
||||
@ -1043,7 +1174,7 @@ impl GuiElemTrait for ListSong {
|
||||
fn draw(&mut self, info: &mut DrawInfo, _g: &mut speedy2d::Graphics2D) {
|
||||
if self.config.redraw {
|
||||
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 {
|
||||
self.sel = sel;
|
||||
if sel {
|
||||
@ -1069,7 +1200,7 @@ impl GuiElemTrait for ListSong {
|
||||
gui.gui
|
||||
.inner
|
||||
.children()
|
||||
.nth(2)
|
||||
.nth(3)
|
||||
.unwrap()
|
||||
.inner
|
||||
.children()
|
||||
@ -1124,9 +1255,9 @@ impl GuiElemTrait for ListSong {
|
||||
self.mouse = false;
|
||||
self.config.redraw = true;
|
||||
if !self.sel {
|
||||
self.selected.0.lock().unwrap().2.insert(self.id);
|
||||
self.selected.insert_song(self.id);
|
||||
} else {
|
||||
self.selected.0.lock().unwrap().2.remove(&self.id);
|
||||
self.selected.remove_song(&self.id);
|
||||
}
|
||||
}
|
||||
vec![]
|
||||
@ -1165,7 +1296,6 @@ impl FilterPanel {
|
||||
search_prefer_start_matches.load(std::sync::atomic::Ordering::Relaxed);
|
||||
let ssc1 = Arc::clone(&search_settings_changed);
|
||||
let ssc2 = Arc::clone(&search_settings_changed);
|
||||
let ssc3 = Arc::clone(&search_settings_changed);
|
||||
let sel3 = selected.clone();
|
||||
const VSPLIT: f32 = 0.4;
|
||||
let tab_main = GuiElem::new(ScrollBox::new(
|
||||
@ -1249,11 +1379,7 @@ impl FilterPanel {
|
||||
GuiElem::new(Button::new(
|
||||
GuiElemCfg::default(),
|
||||
move |_| {
|
||||
ssc3.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
let mut sel = sel3.0.lock().unwrap();
|
||||
sel.2 = HashSet::new();
|
||||
sel.1 = HashSet::new();
|
||||
sel.0 = HashSet::new();
|
||||
let mut sel = sel3.clear();
|
||||
vec![]
|
||||
},
|
||||
vec![GuiElem::new(Label::new(
|
||||
@ -1275,24 +1401,7 @@ impl FilterPanel {
|
||||
{
|
||||
let dss = do_something_sender.clone();
|
||||
move |_| {
|
||||
dss.send(Box::new(|s| {
|
||||
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();
|
||||
dss.send(Box::new(|s| s.selected_add_all())).unwrap();
|
||||
vec![]
|
||||
}
|
||||
},
|
||||
@ -1309,22 +1418,7 @@ impl FilterPanel {
|
||||
{
|
||||
let dss = do_something_sender.clone();
|
||||
move |_| {
|
||||
dss.send(Box::new(|s| {
|
||||
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();
|
||||
dss.send(Box::new(|s| s.selected_add_songs())).unwrap();
|
||||
vec![]
|
||||
}
|
||||
},
|
||||
@ -1341,20 +1435,7 @@ impl FilterPanel {
|
||||
{
|
||||
let dss = do_something_sender.clone();
|
||||
move |_| {
|
||||
dss.send(Box::new(|s| {
|
||||
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();
|
||||
dss.send(Box::new(|s| s.selected_add_albums())).unwrap();
|
||||
vec![]
|
||||
}
|
||||
},
|
||||
|
@ -185,7 +185,7 @@ impl GuiElemTrait for NotifOverlay {
|
||||
}
|
||||
}
|
||||
fn draw_rev(&self) -> bool {
|
||||
true
|
||||
false
|
||||
}
|
||||
|
||||
fn config(&self) -> &GuiElemCfg {
|
||||
|
@ -23,6 +23,9 @@ This file could probably have a better name.
|
||||
#[derive(Clone)]
|
||||
pub struct CurrentSong {
|
||||
config: GuiElemCfg,
|
||||
/// 0: AdvancedLabel for small mode
|
||||
/// 1: AdvancedLabel for big mode heading
|
||||
/// 2: AdvancedLabel for big mode info text
|
||||
children: Vec<GuiElem>,
|
||||
prev_song: Option<SongId>,
|
||||
cover_pos: Rectangle,
|
||||
|
@ -341,6 +341,11 @@ impl GuiElemTrait for GuiScreen {
|
||||
Command::Resume
|
||||
})]
|
||||
}))]
|
||||
} else if down && matches!(key, Some(VirtualKeyCode::F8)) {
|
||||
vec![GuiAction::SendToServer(Command::ErrorInfo(
|
||||
"".to_owned(),
|
||||
"tEsT".to_owned(),
|
||||
))]
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user