mirror of
https://github.com/Dummi26/musicdb.git
synced 2025-03-10 14:13:53 +01:00
added multiselect to the client's library
This commit is contained in:
parent
9c340aa913
commit
6bab2bc075
@ -436,6 +436,7 @@ pub enum Dragging {
|
|||||||
Album(AlbumId),
|
Album(AlbumId),
|
||||||
Song(SongId),
|
Song(SongId),
|
||||||
Queue(Queue),
|
Queue(Queue),
|
||||||
|
Queues(Vec<Queue>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GuiElems have access to this within draw.
|
/// GuiElems have access to this within draw.
|
||||||
@ -733,7 +734,7 @@ pub fn adjust_pos(outer: &Rectangle, rel_pos: &Vec2) -> Vec2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Gui {
|
impl Gui {
|
||||||
fn exec_gui_action(&mut self, action: GuiAction) {
|
pub fn exec_gui_action(&mut self, action: GuiAction) {
|
||||||
match action {
|
match action {
|
||||||
GuiAction::Build(f) => {
|
GuiAction::Build(f) => {
|
||||||
let actions = f(&mut *self.database.lock().unwrap());
|
let actions = f(&mut *self.database.lock().unwrap());
|
||||||
@ -891,6 +892,11 @@ impl WindowHandler<GuiEvent> for Gui {
|
|||||||
25.0,
|
25.0,
|
||||||
Color::from_int_rgba(100, 0, 255, 100),
|
Color::from_int_rgba(100, 0, 255, 100),
|
||||||
),
|
),
|
||||||
|
Dragging::Queues(_) => graphics.draw_circle(
|
||||||
|
self.mouse_pos,
|
||||||
|
25.0,
|
||||||
|
Color::from_int_rgba(100, 0, 255, 100),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -373,7 +373,9 @@ impl GuiElemTrait for GuiEdit {
|
|||||||
}
|
}
|
||||||
fn dragged(&mut self, dragged: Dragging) -> Vec<GuiAction> {
|
fn dragged(&mut self, dragged: Dragging) -> Vec<GuiAction> {
|
||||||
let dragged = match dragged {
|
let dragged = match dragged {
|
||||||
Dragging::Artist(_) | Dragging::Album(_) | Dragging::Song(_) => dragged,
|
Dragging::Artist(_) | Dragging::Album(_) | Dragging::Song(_) | Dragging::Queues(_) => {
|
||||||
|
dragged
|
||||||
|
}
|
||||||
Dragging::Queue(q) => match q.content() {
|
Dragging::Queue(q) => match q.content() {
|
||||||
QueueContent::Song(id) => Dragging::Song(*id),
|
QueueContent::Song(id) => Dragging::Song(*id),
|
||||||
_ => Dragging::Queue(q),
|
_ => Dragging::Queue(q),
|
||||||
@ -396,6 +398,7 @@ impl GuiElemTrait for GuiEdit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Dragging::Queue(_) => return vec![],
|
Dragging::Queue(_) => return vec![],
|
||||||
|
Dragging::Queues(_) => return vec![],
|
||||||
}
|
}
|
||||||
self.reload = true;
|
self.reload = true;
|
||||||
vec![]
|
vec![]
|
||||||
|
@ -1,15 +1,21 @@
|
|||||||
use std::{
|
use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
|
collections::HashSet,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicBool, AtomicUsize},
|
atomic::{AtomicBool, AtomicUsize},
|
||||||
Arc, Mutex,
|
mpsc, Mutex,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use clap::builder::StringValueParser;
|
||||||
use musicdb_lib::data::{
|
use musicdb_lib::data::{
|
||||||
album::Album, artist::Artist, database::Database, song::Song, AlbumId, ArtistId, GeneralData,
|
album::Album,
|
||||||
SongId,
|
artist::Artist,
|
||||||
|
database::Database,
|
||||||
|
queue::{Queue, QueueContent},
|
||||||
|
song::Song,
|
||||||
|
AlbumId, ArtistId, GeneralData, SongId,
|
||||||
};
|
};
|
||||||
use regex::{Regex, RegexBuilder};
|
use regex::{Regex, RegexBuilder};
|
||||||
use speedy2d::{
|
use speedy2d::{
|
||||||
@ -21,8 +27,7 @@ use speedy2d::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
gui::{Dragging, DrawInfo, GuiAction, GuiElem, GuiElemCfg, GuiElemTrait},
|
gui::{Dragging, DrawInfo, GuiAction, GuiElem, GuiElemCfg, GuiElemTrait},
|
||||||
gui_base::{Button, Panel, ScrollBox, Slider},
|
gui_base::{Button, Panel, ScrollBox},
|
||||||
gui_edit::GuiEdit,
|
|
||||||
gui_text::{Label, TextField},
|
gui_text::{Label, TextField},
|
||||||
gui_wrappers::WithFocusHotkey,
|
gui_wrappers::WithFocusHotkey,
|
||||||
};
|
};
|
||||||
@ -34,7 +39,6 @@ with Regex search and drag-n-drop.
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct LibraryBrowser {
|
pub struct LibraryBrowser {
|
||||||
config: GuiElemCfg,
|
config: GuiElemCfg,
|
||||||
pub children: Vec<GuiElem>,
|
pub children: Vec<GuiElem>,
|
||||||
@ -46,6 +50,7 @@ pub struct LibraryBrowser {
|
|||||||
Vec<(AlbumId, Vec<(SongId, f32)>, f32)>,
|
Vec<(AlbumId, Vec<(SongId, f32)>, f32)>,
|
||||||
f32,
|
f32,
|
||||||
)>,
|
)>,
|
||||||
|
selected: Selected,
|
||||||
// - - -
|
// - - -
|
||||||
search_artist: String,
|
search_artist: String,
|
||||||
search_artist_regex: Option<Regex>,
|
search_artist_regex: Option<Regex>,
|
||||||
@ -64,6 +69,79 @@ pub struct LibraryBrowser {
|
|||||||
filter_songs: Rc<Mutex<Filter>>,
|
filter_songs: Rc<Mutex<Filter>>,
|
||||||
filter_albums: Rc<Mutex<Filter>>,
|
filter_albums: Rc<Mutex<Filter>>,
|
||||||
filter_artists: Rc<Mutex<Filter>>,
|
filter_artists: Rc<Mutex<Filter>>,
|
||||||
|
do_something_sender: mpsc::Sender<Box<dyn FnOnce(&mut Self)>>,
|
||||||
|
do_something_receiver: mpsc::Receiver<Box<dyn FnOnce(&mut Self)>>,
|
||||||
|
}
|
||||||
|
impl Clone for LibraryBrowser {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self::new(self.config.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Selected(Rc<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
|
||||||
|
} 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<Option<Regex>, regex::Error> {
|
fn search_regex_new(pat: &str, case_insensitive: bool) -> Result<Option<Regex>, regex::Error> {
|
||||||
if pat.is_empty() {
|
if pat.is_empty() {
|
||||||
@ -108,6 +186,7 @@ impl LibraryBrowser {
|
|||||||
crate::gui_base::ScrollBoxSizeUnit::Pixels,
|
crate::gui_base::ScrollBoxSizeUnit::Pixels,
|
||||||
vec![],
|
vec![],
|
||||||
);
|
);
|
||||||
|
let (do_something_sender, do_something_receiver) = mpsc::channel();
|
||||||
let search_settings_changed = Rc::new(AtomicBool::new(false));
|
let search_settings_changed = Rc::new(AtomicBool::new(false));
|
||||||
let search_was_case_sensitive = false;
|
let search_was_case_sensitive = false;
|
||||||
let search_is_case_sensitive = Rc::new(AtomicBool::new(search_was_case_sensitive));
|
let search_is_case_sensitive = Rc::new(AtomicBool::new(search_was_case_sensitive));
|
||||||
@ -144,6 +223,11 @@ impl LibraryBrowser {
|
|||||||
and: true,
|
and: true,
|
||||||
filters: vec![],
|
filters: vec![],
|
||||||
}));
|
}));
|
||||||
|
let selected = Selected(Rc::new(Mutex::new((
|
||||||
|
HashSet::new(),
|
||||||
|
HashSet::new(),
|
||||||
|
HashSet::new(),
|
||||||
|
))));
|
||||||
Self {
|
Self {
|
||||||
config,
|
config,
|
||||||
children: vec![
|
children: vec![
|
||||||
@ -159,11 +243,14 @@ impl LibraryBrowser {
|
|||||||
Rc::clone(&filter_songs),
|
Rc::clone(&filter_songs),
|
||||||
Rc::clone(&filter_albums),
|
Rc::clone(&filter_albums),
|
||||||
Rc::clone(&filter_artists),
|
Rc::clone(&filter_artists),
|
||||||
|
selected.clone(),
|
||||||
|
do_something_sender.clone(),
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
// - - -
|
// - - -
|
||||||
library_sorted: vec![],
|
library_sorted: vec![],
|
||||||
library_filtered: vec![],
|
library_filtered: vec![],
|
||||||
|
selected,
|
||||||
// - - -
|
// - - -
|
||||||
search_artist: String::new(),
|
search_artist: String::new(),
|
||||||
search_artist_regex: None,
|
search_artist_regex: None,
|
||||||
@ -182,6 +269,8 @@ impl LibraryBrowser {
|
|||||||
filter_songs,
|
filter_songs,
|
||||||
filter_albums,
|
filter_albums,
|
||||||
filter_artists,
|
filter_artists,
|
||||||
|
do_something_sender,
|
||||||
|
do_something_receiver,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -205,6 +294,13 @@ impl GuiElemTrait for LibraryBrowser {
|
|||||||
Box::new(self.clone())
|
Box::new(self.clone())
|
||||||
}
|
}
|
||||||
fn draw(&mut self, info: &mut DrawInfo, _g: &mut speedy2d::Graphics2D) {
|
fn draw(&mut self, info: &mut DrawInfo, _g: &mut speedy2d::Graphics2D) {
|
||||||
|
loop {
|
||||||
|
if let Ok(action) = self.do_something_receiver.try_recv() {
|
||||||
|
action(self);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
// search
|
// search
|
||||||
let mut search_changed = false;
|
let mut search_changed = false;
|
||||||
let mut rebuild_regex = false;
|
let mut rebuild_regex = false;
|
||||||
@ -574,6 +670,7 @@ impl LibraryBrowser {
|
|||||||
} else {
|
} else {
|
||||||
format!("[ Artist #{id} ]")
|
format!("[ Artist #{id} ]")
|
||||||
},
|
},
|
||||||
|
self.selected.clone(),
|
||||||
)),
|
)),
|
||||||
h * 2.5,
|
h * 2.5,
|
||||||
)
|
)
|
||||||
@ -588,6 +685,7 @@ impl LibraryBrowser {
|
|||||||
} else {
|
} else {
|
||||||
format!("[ Album #{id} ]")
|
format!("[ Album #{id} ]")
|
||||||
},
|
},
|
||||||
|
self.selected.clone(),
|
||||||
)),
|
)),
|
||||||
h * 1.5,
|
h * 1.5,
|
||||||
)
|
)
|
||||||
@ -602,6 +700,7 @@ impl LibraryBrowser {
|
|||||||
} else {
|
} else {
|
||||||
format!("[ Song #{id} ]")
|
format!("[ Song #{id} ]")
|
||||||
},
|
},
|
||||||
|
self.selected.clone(),
|
||||||
)),
|
)),
|
||||||
h,
|
h,
|
||||||
)
|
)
|
||||||
@ -615,9 +714,11 @@ struct ListArtist {
|
|||||||
children: Vec<GuiElem>,
|
children: Vec<GuiElem>,
|
||||||
mouse: bool,
|
mouse: bool,
|
||||||
mouse_pos: Vec2,
|
mouse_pos: Vec2,
|
||||||
|
selected: Selected,
|
||||||
|
sel: bool,
|
||||||
}
|
}
|
||||||
impl ListArtist {
|
impl ListArtist {
|
||||||
pub fn new(config: GuiElemCfg, id: ArtistId, name: String) -> Self {
|
pub fn new(mut config: GuiElemCfg, id: ArtistId, name: String, selected: Selected) -> Self {
|
||||||
let label = Label::new(
|
let label = Label::new(
|
||||||
GuiElemCfg::default(),
|
GuiElemCfg::default(),
|
||||||
name,
|
name,
|
||||||
@ -625,12 +726,15 @@ impl ListArtist {
|
|||||||
None,
|
None,
|
||||||
Vec2::new(0.0, 0.5),
|
Vec2::new(0.0, 0.5),
|
||||||
);
|
);
|
||||||
|
config.redraw = true;
|
||||||
Self {
|
Self {
|
||||||
config: config.w_mouse(),
|
config: config.w_mouse(),
|
||||||
id,
|
id,
|
||||||
children: vec![GuiElem::new(label)],
|
children: vec![GuiElem::new(label)],
|
||||||
mouse: false,
|
mouse: false,
|
||||||
mouse_pos: Vec2::ZERO,
|
mouse_pos: Vec2::ZERO,
|
||||||
|
selected,
|
||||||
|
sel: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -654,11 +758,50 @@ impl GuiElemTrait for ListArtist {
|
|||||||
Box::new(self.clone())
|
Box::new(self.clone())
|
||||||
}
|
}
|
||||||
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 {
|
||||||
|
self.config.redraw = false;
|
||||||
|
let sel = self.selected.0.lock().unwrap().0.contains(&self.id);
|
||||||
|
if sel != self.sel {
|
||||||
|
self.sel = sel;
|
||||||
|
if sel {
|
||||||
|
self.children.push(GuiElem::new(Panel::with_background(
|
||||||
|
GuiElemCfg::default(),
|
||||||
|
vec![],
|
||||||
|
Color::from_rgba(1.0, 1.0, 1.0, 0.2),
|
||||||
|
)));
|
||||||
|
} else {
|
||||||
|
self.children.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if self.mouse {
|
if self.mouse {
|
||||||
if info.pos.contains(info.mouse_pos) {
|
if info.pos.contains(info.mouse_pos) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
self.mouse = false;
|
self.mouse = false;
|
||||||
|
if self.sel {
|
||||||
|
let selected = self.selected.clone();
|
||||||
|
info.actions.push(GuiAction::Do(Box::new(move |gui| {
|
||||||
|
let q = selected.as_queue(
|
||||||
|
gui.gui
|
||||||
|
.inner
|
||||||
|
.children()
|
||||||
|
.nth(2)
|
||||||
|
.unwrap()
|
||||||
|
.inner
|
||||||
|
.children()
|
||||||
|
.nth(2)
|
||||||
|
.unwrap()
|
||||||
|
.try_as()
|
||||||
|
.unwrap(),
|
||||||
|
&gui.database.lock().unwrap(),
|
||||||
|
);
|
||||||
|
gui.exec_gui_action(GuiAction::SetDragging(Some((
|
||||||
|
Dragging::Queues(q),
|
||||||
|
None,
|
||||||
|
))));
|
||||||
|
})));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.mouse_pos = Vec2::new(
|
self.mouse_pos = Vec2::new(
|
||||||
@ -673,6 +816,9 @@ impl GuiElemTrait for ListArtist {
|
|||||||
let w = self.config.pixel_pos.width();
|
let w = self.config.pixel_pos.width();
|
||||||
let h = self.config.pixel_pos.height();
|
let h = self.config.pixel_pos.height();
|
||||||
let mut el = GuiElem::new(self.clone());
|
let mut el = GuiElem::new(self.clone());
|
||||||
|
if self.sel {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
vec![GuiAction::SetDragging(Some((
|
vec![GuiAction::SetDragging(Some((
|
||||||
Dragging::Artist(self.id),
|
Dragging::Artist(self.id),
|
||||||
Some(Box::new(move |i, g| {
|
Some(Box::new(move |i, g| {
|
||||||
@ -685,6 +831,7 @@ impl GuiElemTrait for ListArtist {
|
|||||||
el.draw(i, g)
|
el.draw(i, g)
|
||||||
})),
|
})),
|
||||||
)))]
|
)))]
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
@ -692,14 +839,15 @@ impl GuiElemTrait for ListArtist {
|
|||||||
fn mouse_up(&mut self, button: MouseButton) -> Vec<GuiAction> {
|
fn mouse_up(&mut self, button: MouseButton) -> Vec<GuiAction> {
|
||||||
if self.mouse && button == MouseButton::Left {
|
if self.mouse && button == MouseButton::Left {
|
||||||
self.mouse = false;
|
self.mouse = false;
|
||||||
vec![GuiAction::OpenEditPanel(GuiElem::new(GuiEdit::new(
|
self.config.redraw = true;
|
||||||
GuiElemCfg::default(),
|
if !self.sel {
|
||||||
crate::gui_edit::Editable::Artist(vec![self.id]),
|
self.selected.0.lock().unwrap().0.insert(self.id);
|
||||||
)))]
|
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
self.selected.0.lock().unwrap().0.remove(&self.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -709,9 +857,11 @@ struct ListAlbum {
|
|||||||
children: Vec<GuiElem>,
|
children: Vec<GuiElem>,
|
||||||
mouse: bool,
|
mouse: bool,
|
||||||
mouse_pos: Vec2,
|
mouse_pos: Vec2,
|
||||||
|
selected: Selected,
|
||||||
|
sel: bool,
|
||||||
}
|
}
|
||||||
impl ListAlbum {
|
impl ListAlbum {
|
||||||
pub fn new(config: GuiElemCfg, id: AlbumId, name: String) -> Self {
|
pub fn new(mut config: GuiElemCfg, id: AlbumId, name: String, selected: Selected) -> Self {
|
||||||
let label = Label::new(
|
let label = Label::new(
|
||||||
GuiElemCfg::default(),
|
GuiElemCfg::default(),
|
||||||
name,
|
name,
|
||||||
@ -719,12 +869,15 @@ impl ListAlbum {
|
|||||||
None,
|
None,
|
||||||
Vec2::new(0.0, 0.5),
|
Vec2::new(0.0, 0.5),
|
||||||
);
|
);
|
||||||
|
config.redraw = true;
|
||||||
Self {
|
Self {
|
||||||
config: config.w_mouse(),
|
config: config.w_mouse(),
|
||||||
id,
|
id,
|
||||||
children: vec![GuiElem::new(label)],
|
children: vec![GuiElem::new(label)],
|
||||||
mouse: false,
|
mouse: false,
|
||||||
mouse_pos: Vec2::ZERO,
|
mouse_pos: Vec2::ZERO,
|
||||||
|
selected,
|
||||||
|
sel: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -748,11 +901,50 @@ impl GuiElemTrait for ListAlbum {
|
|||||||
Box::new(self.clone())
|
Box::new(self.clone())
|
||||||
}
|
}
|
||||||
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 {
|
||||||
|
self.config.redraw = false;
|
||||||
|
let sel = self.selected.0.lock().unwrap().1.contains(&self.id);
|
||||||
|
if sel != self.sel {
|
||||||
|
self.sel = sel;
|
||||||
|
if sel {
|
||||||
|
self.children.push(GuiElem::new(Panel::with_background(
|
||||||
|
GuiElemCfg::default(),
|
||||||
|
vec![],
|
||||||
|
Color::from_rgba(1.0, 1.0, 1.0, 0.2),
|
||||||
|
)));
|
||||||
|
} else {
|
||||||
|
self.children.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if self.mouse {
|
if self.mouse {
|
||||||
if info.pos.contains(info.mouse_pos) {
|
if info.pos.contains(info.mouse_pos) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
self.mouse = false;
|
self.mouse = false;
|
||||||
|
if self.sel {
|
||||||
|
let selected = self.selected.clone();
|
||||||
|
info.actions.push(GuiAction::Do(Box::new(move |gui| {
|
||||||
|
let q = selected.as_queue(
|
||||||
|
gui.gui
|
||||||
|
.inner
|
||||||
|
.children()
|
||||||
|
.nth(2)
|
||||||
|
.unwrap()
|
||||||
|
.inner
|
||||||
|
.children()
|
||||||
|
.nth(2)
|
||||||
|
.unwrap()
|
||||||
|
.try_as()
|
||||||
|
.unwrap(),
|
||||||
|
&gui.database.lock().unwrap(),
|
||||||
|
);
|
||||||
|
gui.exec_gui_action(GuiAction::SetDragging(Some((
|
||||||
|
Dragging::Queues(q),
|
||||||
|
None,
|
||||||
|
))));
|
||||||
|
})));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.mouse_pos = Vec2::new(
|
self.mouse_pos = Vec2::new(
|
||||||
@ -767,6 +959,9 @@ impl GuiElemTrait for ListAlbum {
|
|||||||
let w = self.config.pixel_pos.width();
|
let w = self.config.pixel_pos.width();
|
||||||
let h = self.config.pixel_pos.height();
|
let h = self.config.pixel_pos.height();
|
||||||
let mut el = GuiElem::new(self.clone());
|
let mut el = GuiElem::new(self.clone());
|
||||||
|
if self.sel {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
vec![GuiAction::SetDragging(Some((
|
vec![GuiAction::SetDragging(Some((
|
||||||
Dragging::Album(self.id),
|
Dragging::Album(self.id),
|
||||||
Some(Box::new(move |i, g| {
|
Some(Box::new(move |i, g| {
|
||||||
@ -779,6 +974,7 @@ impl GuiElemTrait for ListAlbum {
|
|||||||
el.draw(i, g)
|
el.draw(i, g)
|
||||||
})),
|
})),
|
||||||
)))]
|
)))]
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
@ -786,14 +982,15 @@ impl GuiElemTrait for ListAlbum {
|
|||||||
fn mouse_up(&mut self, button: MouseButton) -> Vec<GuiAction> {
|
fn mouse_up(&mut self, button: MouseButton) -> Vec<GuiAction> {
|
||||||
if self.mouse && button == MouseButton::Left {
|
if self.mouse && button == MouseButton::Left {
|
||||||
self.mouse = false;
|
self.mouse = false;
|
||||||
vec![GuiAction::OpenEditPanel(GuiElem::new(GuiEdit::new(
|
self.config.redraw = true;
|
||||||
GuiElemCfg::default(),
|
if !self.sel {
|
||||||
crate::gui_edit::Editable::Album(vec![self.id]),
|
self.selected.0.lock().unwrap().1.insert(self.id);
|
||||||
)))]
|
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
self.selected.0.lock().unwrap().1.remove(&self.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -803,9 +1000,11 @@ struct ListSong {
|
|||||||
children: Vec<GuiElem>,
|
children: Vec<GuiElem>,
|
||||||
mouse: bool,
|
mouse: bool,
|
||||||
mouse_pos: Vec2,
|
mouse_pos: Vec2,
|
||||||
|
selected: Selected,
|
||||||
|
sel: bool,
|
||||||
}
|
}
|
||||||
impl ListSong {
|
impl ListSong {
|
||||||
pub fn new(config: GuiElemCfg, id: SongId, name: String) -> Self {
|
pub fn new(mut config: GuiElemCfg, id: SongId, name: String, selected: Selected) -> Self {
|
||||||
let label = Label::new(
|
let label = Label::new(
|
||||||
GuiElemCfg::default(),
|
GuiElemCfg::default(),
|
||||||
name,
|
name,
|
||||||
@ -813,12 +1012,15 @@ impl ListSong {
|
|||||||
None,
|
None,
|
||||||
Vec2::new(0.0, 0.5),
|
Vec2::new(0.0, 0.5),
|
||||||
);
|
);
|
||||||
|
config.redraw = true;
|
||||||
Self {
|
Self {
|
||||||
config: config.w_mouse(),
|
config: config.w_mouse(),
|
||||||
id,
|
id,
|
||||||
children: vec![GuiElem::new(label)],
|
children: vec![GuiElem::new(label)],
|
||||||
mouse: false,
|
mouse: false,
|
||||||
mouse_pos: Vec2::ZERO,
|
mouse_pos: Vec2::ZERO,
|
||||||
|
selected,
|
||||||
|
sel: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -842,11 +1044,50 @@ impl GuiElemTrait for ListSong {
|
|||||||
Box::new(self.clone())
|
Box::new(self.clone())
|
||||||
}
|
}
|
||||||
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 {
|
||||||
|
self.config.redraw = false;
|
||||||
|
let sel = self.selected.0.lock().unwrap().2.contains(&self.id);
|
||||||
|
if sel != self.sel {
|
||||||
|
self.sel = sel;
|
||||||
|
if sel {
|
||||||
|
self.children.push(GuiElem::new(Panel::with_background(
|
||||||
|
GuiElemCfg::default(),
|
||||||
|
vec![],
|
||||||
|
Color::from_rgba(1.0, 1.0, 1.0, 0.2),
|
||||||
|
)));
|
||||||
|
} else {
|
||||||
|
self.children.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if self.mouse {
|
if self.mouse {
|
||||||
if info.pos.contains(info.mouse_pos) {
|
if info.pos.contains(info.mouse_pos) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
self.mouse = false;
|
self.mouse = false;
|
||||||
|
if self.sel {
|
||||||
|
let selected = self.selected.clone();
|
||||||
|
info.actions.push(GuiAction::Do(Box::new(move |gui| {
|
||||||
|
let q = selected.as_queue(
|
||||||
|
gui.gui
|
||||||
|
.inner
|
||||||
|
.children()
|
||||||
|
.nth(2)
|
||||||
|
.unwrap()
|
||||||
|
.inner
|
||||||
|
.children()
|
||||||
|
.nth(2)
|
||||||
|
.unwrap()
|
||||||
|
.try_as()
|
||||||
|
.unwrap(),
|
||||||
|
&gui.database.lock().unwrap(),
|
||||||
|
);
|
||||||
|
gui.exec_gui_action(GuiAction::SetDragging(Some((
|
||||||
|
Dragging::Queues(q),
|
||||||
|
None,
|
||||||
|
))));
|
||||||
|
})));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.mouse_pos = Vec2::new(
|
self.mouse_pos = Vec2::new(
|
||||||
@ -861,6 +1102,9 @@ impl GuiElemTrait for ListSong {
|
|||||||
let w = self.config.pixel_pos.width();
|
let w = self.config.pixel_pos.width();
|
||||||
let h = self.config.pixel_pos.height();
|
let h = self.config.pixel_pos.height();
|
||||||
let mut el = GuiElem::new(self.clone());
|
let mut el = GuiElem::new(self.clone());
|
||||||
|
if self.sel {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
vec![GuiAction::SetDragging(Some((
|
vec![GuiAction::SetDragging(Some((
|
||||||
Dragging::Song(self.id),
|
Dragging::Song(self.id),
|
||||||
Some(Box::new(move |i, g| {
|
Some(Box::new(move |i, g| {
|
||||||
@ -873,6 +1117,7 @@ impl GuiElemTrait for ListSong {
|
|||||||
el.draw(i, g)
|
el.draw(i, g)
|
||||||
})),
|
})),
|
||||||
)))]
|
)))]
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
@ -880,14 +1125,15 @@ impl GuiElemTrait for ListSong {
|
|||||||
fn mouse_up(&mut self, button: MouseButton) -> Vec<GuiAction> {
|
fn mouse_up(&mut self, button: MouseButton) -> Vec<GuiAction> {
|
||||||
if self.mouse && button == MouseButton::Left {
|
if self.mouse && button == MouseButton::Left {
|
||||||
self.mouse = false;
|
self.mouse = false;
|
||||||
vec![GuiAction::OpenEditPanel(GuiElem::new(GuiEdit::new(
|
self.config.redraw = true;
|
||||||
GuiElemCfg::default(),
|
if !self.sel {
|
||||||
crate::gui_edit::Editable::Song(vec![self.id]),
|
self.selected.0.lock().unwrap().2.insert(self.id);
|
||||||
)))]
|
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
self.selected.0.lock().unwrap().2.remove(&self.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -914,13 +1160,25 @@ impl FilterPanel {
|
|||||||
filter_songs: Rc<Mutex<Filter>>,
|
filter_songs: Rc<Mutex<Filter>>,
|
||||||
filter_albums: Rc<Mutex<Filter>>,
|
filter_albums: Rc<Mutex<Filter>>,
|
||||||
filter_artists: Rc<Mutex<Filter>>,
|
filter_artists: Rc<Mutex<Filter>>,
|
||||||
|
selected: Selected,
|
||||||
|
do_something_sender: mpsc::Sender<Box<dyn FnOnce(&mut LibraryBrowser)>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let is_case_sensitive = search_is_case_sensitive.load(std::sync::atomic::Ordering::Relaxed);
|
let is_case_sensitive = search_is_case_sensitive.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
let prefer_start_matches =
|
let prefer_start_matches =
|
||||||
search_prefer_start_matches.load(std::sync::atomic::Ordering::Relaxed);
|
search_prefer_start_matches.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
let ssc1 = Rc::clone(&search_settings_changed);
|
let ssc1 = Rc::clone(&search_settings_changed);
|
||||||
let ssc2 = Rc::clone(&search_settings_changed);
|
let ssc2 = Rc::clone(&search_settings_changed);
|
||||||
let tab_settings = GuiElem::new(ScrollBox::new(
|
let ssc3 = Rc::clone(&search_settings_changed);
|
||||||
|
let ssc4 = Rc::clone(&search_settings_changed);
|
||||||
|
let ssc5 = Rc::clone(&search_settings_changed);
|
||||||
|
let ssc6 = Rc::clone(&search_settings_changed);
|
||||||
|
let ssc7 = Rc::clone(&search_settings_changed);
|
||||||
|
let sel3 = selected.clone();
|
||||||
|
let sel4 = selected.clone();
|
||||||
|
let sel5 = selected.clone();
|
||||||
|
let sel6 = selected.clone();
|
||||||
|
let sel7 = selected.clone();
|
||||||
|
let tab_main = GuiElem::new(ScrollBox::new(
|
||||||
GuiElemCfg::default(),
|
GuiElemCfg::default(),
|
||||||
crate::gui_base::ScrollBoxSizeUnit::Pixels,
|
crate::gui_base::ScrollBoxSizeUnit::Pixels,
|
||||||
vec![
|
vec![
|
||||||
@ -997,6 +1255,153 @@ impl FilterPanel {
|
|||||||
)),
|
)),
|
||||||
1.0,
|
1.0,
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
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();
|
||||||
|
vec![]
|
||||||
|
},
|
||||||
|
vec![GuiElem::new(Label::new(
|
||||||
|
GuiElemCfg::default(),
|
||||||
|
"deselect all".to_owned(),
|
||||||
|
Color::GRAY,
|
||||||
|
None,
|
||||||
|
Vec2::new(0.5, 0.5),
|
||||||
|
))],
|
||||||
|
)),
|
||||||
|
1.0,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
GuiElem::new(Panel::new(
|
||||||
|
GuiElemCfg::default(),
|
||||||
|
vec![
|
||||||
|
GuiElem::new(Button::new(
|
||||||
|
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.0), (0.5, 1.0))),
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
vec![GuiElem::new(Label::new(
|
||||||
|
GuiElemCfg::default(),
|
||||||
|
"select all".to_owned(),
|
||||||
|
Color::GRAY,
|
||||||
|
None,
|
||||||
|
Vec2::new(0.5, 0.5),
|
||||||
|
))],
|
||||||
|
)),
|
||||||
|
GuiElem::new(Button::new(
|
||||||
|
GuiElemCfg::at(Rectangle::from_tuples((0.55, 0.0), (0.65, 1.0))),
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
vec![GuiElem::new(Label::new(
|
||||||
|
GuiElemCfg::default(),
|
||||||
|
"songs".to_owned(),
|
||||||
|
Color::GRAY,
|
||||||
|
None,
|
||||||
|
Vec2::new(0.5, 0.5),
|
||||||
|
))],
|
||||||
|
)),
|
||||||
|
GuiElem::new(Button::new(
|
||||||
|
GuiElemCfg::at(Rectangle::from_tuples((0.7, 0.0), (0.8, 1.0))),
|
||||||
|
{
|
||||||
|
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, _, _) in albums {
|
||||||
|
sel.1.insert(*id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.unwrap();
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
vec![GuiElem::new(Label::new(
|
||||||
|
GuiElemCfg::default(),
|
||||||
|
"albums".to_owned(),
|
||||||
|
Color::GRAY,
|
||||||
|
None,
|
||||||
|
Vec2::new(0.5, 0.5),
|
||||||
|
))],
|
||||||
|
)),
|
||||||
|
GuiElem::new(Button::new(
|
||||||
|
GuiElemCfg::at(Rectangle::from_tuples((0.85, 0.0), (0.95, 1.0))),
|
||||||
|
{
|
||||||
|
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, _, _, _) in &s.library_filtered {
|
||||||
|
sel.0.insert(*id);
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.unwrap();
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
vec![GuiElem::new(Label::new(
|
||||||
|
GuiElemCfg::default(),
|
||||||
|
"artists".to_owned(),
|
||||||
|
Color::GRAY,
|
||||||
|
None,
|
||||||
|
Vec2::new(0.5, 0.5),
|
||||||
|
))],
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
1.0,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
let tab_filters_songs = GuiElem::new(ScrollBox::new(
|
let tab_filters_songs = GuiElem::new(ScrollBox::new(
|
||||||
@ -1015,7 +1420,6 @@ impl FilterPanel {
|
|||||||
vec![],
|
vec![],
|
||||||
));
|
));
|
||||||
let new_tab = Rc::new(AtomicUsize::new(0));
|
let new_tab = Rc::new(AtomicUsize::new(0));
|
||||||
let set_tab_0 = Rc::clone(&new_tab);
|
|
||||||
let set_tab_1 = Rc::clone(&new_tab);
|
let set_tab_1 = Rc::clone(&new_tab);
|
||||||
let set_tab_2 = Rc::clone(&new_tab);
|
let set_tab_2 = Rc::clone(&new_tab);
|
||||||
let set_tab_3 = Rc::clone(&new_tab);
|
let set_tab_3 = Rc::clone(&new_tab);
|
||||||
@ -1027,56 +1431,60 @@ impl FilterPanel {
|
|||||||
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.0), (1.0, HEIGHT))),
|
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.0), (1.0, HEIGHT))),
|
||||||
vec![
|
vec![
|
||||||
GuiElem::new(Button::new(
|
GuiElem::new(Button::new(
|
||||||
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.0), (0.4, 1.0))),
|
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.0), (0.33, 1.0))),
|
||||||
move |_| {
|
move |_| {
|
||||||
set_tab_0.store(0, std::sync::atomic::Ordering::Relaxed);
|
let v = if set_tab_1.load(std::sync::atomic::Ordering::Relaxed) != 1
|
||||||
|
{
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
set_tab_1.store(v, std::sync::atomic::Ordering::Relaxed);
|
||||||
vec![]
|
vec![]
|
||||||
},
|
},
|
||||||
vec![GuiElem::new(Label::new(
|
vec![GuiElem::new(Label::new(
|
||||||
GuiElemCfg::default(),
|
GuiElemCfg::default(),
|
||||||
"Settings".to_owned(),
|
"Filter Songs".to_owned(),
|
||||||
Color::WHITE,
|
|
||||||
None,
|
|
||||||
Vec2::new(0.5, 0.5),
|
|
||||||
))],
|
|
||||||
)),
|
|
||||||
GuiElem::new(Button::new(
|
|
||||||
GuiElemCfg::at(Rectangle::from_tuples((0.4, 0.0), (0.6, 1.0))),
|
|
||||||
move |_| {
|
|
||||||
set_tab_1.store(1, std::sync::atomic::Ordering::Relaxed);
|
|
||||||
vec![]
|
|
||||||
},
|
|
||||||
vec![GuiElem::new(Label::new(
|
|
||||||
GuiElemCfg::default(),
|
|
||||||
"Filters\n(Songs)".to_owned(),
|
|
||||||
Color::GRAY,
|
Color::GRAY,
|
||||||
None,
|
None,
|
||||||
Vec2::new(0.5, 0.5),
|
Vec2::new(0.5, 0.5),
|
||||||
))],
|
))],
|
||||||
)),
|
)),
|
||||||
GuiElem::new(Button::new(
|
GuiElem::new(Button::new(
|
||||||
GuiElemCfg::at(Rectangle::from_tuples((0.6, 0.0), (0.8, 1.0))),
|
GuiElemCfg::at(Rectangle::from_tuples((0.33, 0.0), (0.67, 1.0))),
|
||||||
move |_| {
|
move |_| {
|
||||||
set_tab_2.store(2, std::sync::atomic::Ordering::Relaxed);
|
let v = if set_tab_2.load(std::sync::atomic::Ordering::Relaxed) != 2
|
||||||
|
{
|
||||||
|
2
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
set_tab_2.store(v, std::sync::atomic::Ordering::Relaxed);
|
||||||
vec![]
|
vec![]
|
||||||
},
|
},
|
||||||
vec![GuiElem::new(Label::new(
|
vec![GuiElem::new(Label::new(
|
||||||
GuiElemCfg::default(),
|
GuiElemCfg::default(),
|
||||||
"Filters\n(Albums)".to_owned(),
|
"Filter Albums".to_owned(),
|
||||||
Color::GRAY,
|
Color::GRAY,
|
||||||
None,
|
None,
|
||||||
Vec2::new(0.5, 0.5),
|
Vec2::new(0.5, 0.5),
|
||||||
))],
|
))],
|
||||||
)),
|
)),
|
||||||
GuiElem::new(Button::new(
|
GuiElem::new(Button::new(
|
||||||
GuiElemCfg::at(Rectangle::from_tuples((0.8, 0.0), (1.0, 1.0))),
|
GuiElemCfg::at(Rectangle::from_tuples((0.67, 0.0), (1.0, 1.0))),
|
||||||
move |_| {
|
move |_| {
|
||||||
set_tab_3.store(3, std::sync::atomic::Ordering::Relaxed);
|
let v = if set_tab_3.load(std::sync::atomic::Ordering::Relaxed) != 3
|
||||||
|
{
|
||||||
|
3
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
set_tab_3.store(v, std::sync::atomic::Ordering::Relaxed);
|
||||||
vec![]
|
vec![]
|
||||||
},
|
},
|
||||||
vec![GuiElem::new(Label::new(
|
vec![GuiElem::new(Label::new(
|
||||||
GuiElemCfg::default(),
|
GuiElemCfg::default(),
|
||||||
"Filters\n(Artists)".to_owned(),
|
"Filter Artists".to_owned(),
|
||||||
Color::GRAY,
|
Color::GRAY,
|
||||||
None,
|
None,
|
||||||
Vec2::new(0.5, 0.5),
|
Vec2::new(0.5, 0.5),
|
||||||
@ -1087,7 +1495,7 @@ impl FilterPanel {
|
|||||||
GuiElem::new(Panel::new(
|
GuiElem::new(Panel::new(
|
||||||
GuiElemCfg::at(Rectangle::from_tuples((0.0, HEIGHT), (1.0, 1.0))),
|
GuiElemCfg::at(Rectangle::from_tuples((0.0, HEIGHT), (1.0, 1.0))),
|
||||||
vec![
|
vec![
|
||||||
tab_settings,
|
tab_main,
|
||||||
tab_filters_songs,
|
tab_filters_songs,
|
||||||
tab_filters_albums,
|
tab_filters_albums,
|
||||||
tab_filters_artists,
|
tab_filters_artists,
|
||||||
@ -1390,12 +1798,15 @@ impl GuiElemTrait for FilterPanel {
|
|||||||
self.line_height = info.line_height;
|
self.line_height = info.line_height;
|
||||||
}
|
}
|
||||||
// maybe switch tabs
|
// maybe switch tabs
|
||||||
let new_tab = self.new_tab.load(std::sync::atomic::Ordering::Relaxed);
|
let mut new_tab = self.new_tab.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
let mut load_tab = false;
|
let mut load_tab = false;
|
||||||
if new_tab < usize::MAX {
|
if new_tab != self.tab {
|
||||||
load_tab = true;
|
load_tab = true;
|
||||||
|
if new_tab == usize::MAX {
|
||||||
self.new_tab
|
self.new_tab
|
||||||
.store(usize::MAX, std::sync::atomic::Ordering::Relaxed);
|
.store(self.tab, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
new_tab = self.tab;
|
||||||
|
} else {
|
||||||
self.children[1]
|
self.children[1]
|
||||||
.inner
|
.inner
|
||||||
.children()
|
.children()
|
||||||
@ -1412,10 +1823,11 @@ impl GuiElemTrait for FilterPanel {
|
|||||||
.inner
|
.inner
|
||||||
.config_mut()
|
.config_mut()
|
||||||
.enabled = true;
|
.enabled = true;
|
||||||
|
if self.tab > 0 {
|
||||||
*self.children[0]
|
*self.children[0]
|
||||||
.inner
|
.inner
|
||||||
.children()
|
.children()
|
||||||
.nth(self.tab)
|
.nth(self.tab - 1)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.try_as_mut::<Button>()
|
.try_as_mut::<Button>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -1424,10 +1836,12 @@ impl GuiElemTrait for FilterPanel {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.content
|
.content
|
||||||
.color() = Color::GRAY;
|
.color() = Color::GRAY;
|
||||||
|
}
|
||||||
|
if new_tab > 0 {
|
||||||
*self.children[0]
|
*self.children[0]
|
||||||
.inner
|
.inner
|
||||||
.children()
|
.children()
|
||||||
.nth(new_tab)
|
.nth(new_tab - 1)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.try_as_mut::<Button>()
|
.try_as_mut::<Button>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -1436,8 +1850,10 @@ impl GuiElemTrait for FilterPanel {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.content
|
.content
|
||||||
.color() = Color::WHITE;
|
.color() = Color::WHITE;
|
||||||
|
}
|
||||||
self.tab = new_tab;
|
self.tab = new_tab;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// load tab
|
// load tab
|
||||||
if load_tab {
|
if load_tab {
|
||||||
match new_tab {
|
match new_tab {
|
||||||
@ -1450,7 +1866,6 @@ impl GuiElemTrait for FilterPanel {
|
|||||||
.try_as_mut::<ScrollBox>()
|
.try_as_mut::<ScrollBox>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let ssc = Rc::clone(&self.search_settings_changed);
|
let ssc = Rc::clone(&self.search_settings_changed);
|
||||||
let my_tab = self.tab;
|
|
||||||
let ntab = Rc::clone(&self.new_tab);
|
let ntab = Rc::clone(&self.new_tab);
|
||||||
sb.children = Self::build_filter(
|
sb.children = Self::build_filter(
|
||||||
match new_tab {
|
match new_tab {
|
||||||
@ -1462,7 +1877,7 @@ impl GuiElemTrait for FilterPanel {
|
|||||||
info.line_height,
|
info.line_height,
|
||||||
&Rc::new(move |update_ui| {
|
&Rc::new(move |update_ui| {
|
||||||
if update_ui {
|
if update_ui {
|
||||||
ntab.store(my_tab, std::sync::atomic::Ordering::Relaxed);
|
ntab.store(usize::MAX, std::sync::atomic::Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
ssc.store(true, std::sync::atomic::Ordering::Relaxed);
|
ssc.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
}),
|
}),
|
||||||
|
@ -116,7 +116,13 @@ impl QueueViewer {
|
|||||||
GuiElemCfg::at(Rectangle::from_tuples((0.5, 0.5), (1.0, 1.0)))
|
GuiElemCfg::at(Rectangle::from_tuples((0.5, 0.5), (1.0, 1.0)))
|
||||||
.w_mouse(),
|
.w_mouse(),
|
||||||
vec![],
|
vec![],
|
||||||
QueueContent::Shuffle { inner: Box::new(QueueContent::Folder(0, vec![], String::new()).into()), state: ShuffleState::NotShuffled }.into(),
|
QueueContent::Shuffle {
|
||||||
|
inner: Box::new(
|
||||||
|
QueueContent::Folder(0, vec![], String::new()).into(),
|
||||||
|
),
|
||||||
|
state: ShuffleState::NotShuffled,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
.alwayscopy(),
|
.alwayscopy(),
|
||||||
@ -262,7 +268,12 @@ fn queue_gui(
|
|||||||
}
|
}
|
||||||
QueueContent::Random(q) => {
|
QueueContent::Random(q) => {
|
||||||
target.push((
|
target.push((
|
||||||
GuiElem::new(QueueRandom::new(cfg.clone(), path.clone(), queue.clone(), current)),
|
GuiElem::new(QueueRandom::new(
|
||||||
|
cfg.clone(),
|
||||||
|
path.clone(),
|
||||||
|
queue.clone(),
|
||||||
|
current,
|
||||||
|
)),
|
||||||
line_height,
|
line_height,
|
||||||
));
|
));
|
||||||
for (i, inner) in q.iter().enumerate() {
|
for (i, inner) in q.iter().enumerate() {
|
||||||
@ -289,7 +300,12 @@ fn queue_gui(
|
|||||||
}
|
}
|
||||||
QueueContent::Shuffle { inner, state: _ } => {
|
QueueContent::Shuffle { inner, state: _ } => {
|
||||||
target.push((
|
target.push((
|
||||||
GuiElem::new(QueueShuffle::new(cfg.clone(), path.clone(), queue.clone(), current)),
|
GuiElem::new(QueueShuffle::new(
|
||||||
|
cfg.clone(),
|
||||||
|
path.clone(),
|
||||||
|
queue.clone(),
|
||||||
|
current,
|
||||||
|
)),
|
||||||
line_height * 0.8,
|
line_height * 0.8,
|
||||||
));
|
));
|
||||||
let mut p = path.clone();
|
let mut p = path.clone();
|
||||||
@ -348,7 +364,7 @@ impl GuiElemTrait for QueueEmptySpaceDragHandler {
|
|||||||
Box::new(self.clone())
|
Box::new(self.clone())
|
||||||
}
|
}
|
||||||
fn dragged(&mut self, dragged: Dragging) -> Vec<GuiAction> {
|
fn dragged(&mut self, dragged: Dragging) -> Vec<GuiAction> {
|
||||||
dragged_add_to_queue(dragged, |q| Command::QueueAdd(vec![], q))
|
dragged_add_to_queue(dragged, |q, _| Command::QueueAdd(vec![], q))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -552,11 +568,11 @@ impl GuiElemTrait for QueueSong {
|
|||||||
if !self.always_copy {
|
if !self.always_copy {
|
||||||
let mut p = self.path.clone();
|
let mut p = self.path.clone();
|
||||||
let insert_below = self.insert_below;
|
let insert_below = self.insert_below;
|
||||||
dragged_add_to_queue(dragged, move |q| {
|
dragged_add_to_queue(dragged, move |q, i| {
|
||||||
if let Some(i) = p.pop() {
|
if let Some(j) = p.pop() {
|
||||||
Command::QueueInsert(p, if insert_below { i + 1 } else { i }, q)
|
Command::QueueInsert(p.clone(), if insert_below { j + 1 } else { j } + i, q)
|
||||||
} else {
|
} else {
|
||||||
Command::QueueAdd(p, q)
|
Command::QueueAdd(p.clone(), q)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@ -722,11 +738,13 @@ impl GuiElemTrait for QueueFolder {
|
|||||||
if !self.always_copy {
|
if !self.always_copy {
|
||||||
if self.insert_into {
|
if self.insert_into {
|
||||||
let p = self.path.clone();
|
let p = self.path.clone();
|
||||||
dragged_add_to_queue(dragged, move |q| Command::QueueAdd(p, q))
|
dragged_add_to_queue(dragged, move |q, _| Command::QueueAdd(p.clone(), q))
|
||||||
} else {
|
} else {
|
||||||
let mut p = self.path.clone();
|
let mut p = self.path.clone();
|
||||||
let i = p.pop();
|
let j = p.pop().unwrap_or(0);
|
||||||
dragged_add_to_queue(dragged, move |q| Command::QueueInsert(p, i.unwrap_or(0), q))
|
dragged_add_to_queue(dragged, move |q, i| {
|
||||||
|
Command::QueueInsert(p.clone(), j + i, q)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
@ -785,8 +803,10 @@ impl GuiElemTrait for QueueIndentEnd {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn dragged(&mut self, dragged: Dragging) -> Vec<GuiAction> {
|
fn dragged(&mut self, dragged: Dragging) -> Vec<GuiAction> {
|
||||||
let (p, i) = self.path_insert.clone();
|
let (p, j) = self.path_insert.clone();
|
||||||
dragged_add_to_queue(dragged, move |q| Command::QueueInsert(p, i, q))
|
dragged_add_to_queue(dragged, move |q, i| {
|
||||||
|
Command::QueueInsert(p.clone(), j + i, q)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -945,7 +965,7 @@ impl GuiElemTrait for QueueLoop {
|
|||||||
if !self.always_copy {
|
if !self.always_copy {
|
||||||
let mut p = self.path.clone();
|
let mut p = self.path.clone();
|
||||||
p.push(0);
|
p.push(0);
|
||||||
dragged_add_to_queue(dragged, move |q| Command::QueueAdd(p, q))
|
dragged_add_to_queue(dragged, move |q, _| Command::QueueAdd(p.clone(), q))
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
@ -1077,7 +1097,7 @@ impl GuiElemTrait for QueueRandom {
|
|||||||
fn dragged(&mut self, dragged: Dragging) -> Vec<GuiAction> {
|
fn dragged(&mut self, dragged: Dragging) -> Vec<GuiAction> {
|
||||||
if !self.always_copy {
|
if !self.always_copy {
|
||||||
let p = self.path.clone();
|
let p = self.path.clone();
|
||||||
dragged_add_to_queue(dragged, move |q| Command::QueueAdd(p, q))
|
dragged_add_to_queue(dragged, move |q, _| Command::QueueAdd(p.clone(), q))
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
@ -1210,22 +1230,22 @@ impl GuiElemTrait for QueueShuffle {
|
|||||||
if !self.always_copy {
|
if !self.always_copy {
|
||||||
let mut p = self.path.clone();
|
let mut p = self.path.clone();
|
||||||
p.push(0);
|
p.push(0);
|
||||||
dragged_add_to_queue(dragged, move |q| Command::QueueAdd(p, q))
|
dragged_add_to_queue(dragged, move |q, _| Command::QueueAdd(p.clone(), q))
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dragged_add_to_queue<F: FnOnce(Queue) -> Command + 'static>(
|
fn dragged_add_to_queue<F: FnMut(Queue, usize) -> Command + 'static>(
|
||||||
dragged: Dragging,
|
dragged: Dragging,
|
||||||
f: F,
|
mut f: F,
|
||||||
) -> Vec<GuiAction> {
|
) -> Vec<GuiAction> {
|
||||||
match dragged {
|
match dragged {
|
||||||
Dragging::Artist(id) => {
|
Dragging::Artist(id) => {
|
||||||
vec![GuiAction::Build(Box::new(move |db| {
|
vec![GuiAction::Build(Box::new(move |db| {
|
||||||
if let Some(q) = add_to_queue_artist_by_id(id, db) {
|
if let Some(q) = add_to_queue_artist_by_id(id, db) {
|
||||||
vec![GuiAction::SendToServer(f(q))]
|
vec![GuiAction::SendToServer(f(q, 0))]
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
@ -1234,7 +1254,7 @@ fn dragged_add_to_queue<F: FnOnce(Queue) -> Command + 'static>(
|
|||||||
Dragging::Album(id) => {
|
Dragging::Album(id) => {
|
||||||
vec![GuiAction::Build(Box::new(move |db| {
|
vec![GuiAction::Build(Box::new(move |db| {
|
||||||
if let Some(q) = add_to_queue_album_by_id(id, db) {
|
if let Some(q) = add_to_queue_album_by_id(id, db) {
|
||||||
vec![GuiAction::SendToServer(f(q))]
|
vec![GuiAction::SendToServer(f(q, 0))]
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
@ -1242,11 +1262,16 @@ fn dragged_add_to_queue<F: FnOnce(Queue) -> Command + 'static>(
|
|||||||
}
|
}
|
||||||
Dragging::Song(id) => {
|
Dragging::Song(id) => {
|
||||||
let q = QueueContent::Song(id).into();
|
let q = QueueContent::Song(id).into();
|
||||||
vec![GuiAction::SendToServer(f(q))]
|
vec![GuiAction::SendToServer(f(q, 0))]
|
||||||
}
|
}
|
||||||
Dragging::Queue(q) => {
|
Dragging::Queue(q) => {
|
||||||
vec![GuiAction::SendToServer(f(q))]
|
vec![GuiAction::SendToServer(f(q, 0))]
|
||||||
}
|
}
|
||||||
|
Dragging::Queues(q) => q
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, q)| GuiAction::SendToServer(f(q, i)))
|
||||||
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +37,11 @@ pub struct GuiScreen {
|
|||||||
/// 0: StatusBar / Idle display
|
/// 0: StatusBar / Idle display
|
||||||
/// 1: Settings
|
/// 1: Settings
|
||||||
/// 2: Panel for Main view
|
/// 2: Panel for Main view
|
||||||
|
/// 0: settings button
|
||||||
|
/// 1: exit button
|
||||||
|
/// 2: library browser
|
||||||
|
/// 3: queue
|
||||||
|
/// 4: queue clear button
|
||||||
/// 3: Edit Panel
|
/// 3: Edit Panel
|
||||||
children: Vec<GuiElem>,
|
children: Vec<GuiElem>,
|
||||||
pub idle: (bool, Option<Instant>),
|
pub idle: (bool, Option<Instant>),
|
||||||
|
Loading…
Reference in New Issue
Block a user