get rid of GuiElem in favor of explicit Box<dyn GuiElemTrait>

This commit is contained in:
Mark 2023-11-05 20:43:55 +01:00
parent a151aa8712
commit 671bf17c53
11 changed files with 872 additions and 853 deletions

View File

@ -196,7 +196,7 @@ pub struct Gui {
pub database: Arc<Mutex<Database>>,
pub connection: TcpStream,
pub get_con: Arc<Mutex<get::Client<TcpStream>>>,
pub gui: GuiElem,
pub gui: WithFocusHotkey<GuiScreen>,
pub size: UVec2,
pub mouse_pos: Vec2,
pub font: Font,
@ -268,9 +268,9 @@ impl Gui {
notif_sender
.send(Box::new(move |_| {
(
GuiElem::new(Panel::with_background(
Box::new(Panel::with_background(
GuiElemCfg::default(),
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
if t.is_empty() {
format!("Server message\n{d}")
@ -300,7 +300,7 @@ impl Gui {
database,
connection,
get_con,
gui: GuiElem::new(WithFocusHotkey::new_noshift(
gui: WithFocusHotkey::new_noshift(
VirtualKeyCode::Escape,
GuiScreen::new(
GuiElemCfg::default(),
@ -310,7 +310,7 @@ impl Gui {
scroll_lines_multiplier,
scroll_pages_multiplier,
),
)),
),
size: UVec2::ZERO,
mouse_pos: Vec2::ZERO,
font,
@ -332,19 +332,20 @@ impl Gui {
/// the trait implemented by all Gui elements.
/// feel free to override the methods you wish to use.
#[allow(unused)]
pub trait GuiElemTrait {
pub trait GuiElemTrait: 'static {
fn config(&self) -> &GuiElemCfg;
fn config_mut(&mut self) -> &mut GuiElemCfg;
/// note: drawing happens from the last to the first element, while priority is from first to last.
/// if you wish to add a "high priority" child to a Vec<GuiElem> using push, .rev() the iterator in this method and change draw_rev to false.
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_>;
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_>;
/// defaults to true.
fn draw_rev(&self) -> bool {
true
}
fn any(&self) -> &dyn Any;
fn any_mut(&mut self) -> &mut dyn Any;
fn clone_gui(&self) -> Box<dyn GuiElemTrait>;
fn elem(&self) -> &dyn GuiElemTrait;
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait;
/// handles drawing.
fn draw(&mut self, info: &mut DrawInfo, g: &mut Graphics2D) {}
/// an event that is invoked whenever a mouse button is pressed on the element.
@ -392,6 +393,281 @@ pub trait GuiElemTrait {
}
fn updated_library(&mut self) {}
fn updated_queue(&mut self) {}
fn _draw(&mut self, info: &mut DrawInfo, g: &mut Graphics2D) {
if !self.config_mut().enabled {
return;
}
// adjust info
let npos = adjust_area(&info.pos, &self.config_mut().pos);
let ppos = std::mem::replace(&mut info.pos, npos);
if info.child_has_keyboard_focus {
if self.config().keyboard_focus_index == usize::MAX {
info.has_keyboard_focus = true;
info.child_has_keyboard_focus = false;
}
}
// call trait's draw function
self.draw(info, g);
// reset info
info.has_keyboard_focus = false;
let focus_path = info.child_has_keyboard_focus;
// children (in reverse order - first element has the highest priority)
let kbd_focus_index = self.config().keyboard_focus_index;
if self.draw_rev() {
for (i, c) in self
.children()
.collect::<Vec<_>>()
.into_iter()
.enumerate()
.rev()
{
info.child_has_keyboard_focus = focus_path && i == kbd_focus_index;
c._draw(info, g);
}
} else {
for (i, c) in self.children().enumerate() {
info.child_has_keyboard_focus = focus_path && i == kbd_focus_index;
c._draw(info, g);
}
}
// reset pt. 2
info.child_has_keyboard_focus = focus_path;
self.config_mut().pixel_pos = std::mem::replace(&mut info.pos, ppos);
}
/// recursively applies the function to all gui elements below and including this one
fn _recursive_all(&mut self, f: &mut dyn FnMut(&mut dyn GuiElemTrait)) {
f(self.elem_mut());
for c in self.children() {
c._recursive_all(f);
}
}
fn _mouse_event(
&mut self,
condition: &mut dyn FnMut(&mut dyn GuiElemTrait) -> Option<Vec<GuiAction>>,
pos: Vec2,
) -> Option<Vec<GuiAction>> {
for c in &mut self.children() {
if c.config().enabled {
if c.config().pixel_pos.contains(pos) {
if let Some(v) = c._mouse_event(condition, pos) {
return Some(v);
}
}
}
}
condition(self.elem_mut())
}
fn _release_drag(
&mut self,
dragged: &mut Option<Dragging>,
pos: Vec2,
) -> Option<Vec<GuiAction>> {
self._mouse_event(
&mut |v| {
if v.config().drag_target {
if let Some(d) = dragged.take() {
return Some(v.dragged(d));
}
}
None
},
pos,
)
}
fn _mouse_button(
&mut self,
button: MouseButton,
down: bool,
pos: Vec2,
) -> Option<Vec<GuiAction>> {
if down {
self._mouse_event(
&mut |v: &mut dyn GuiElemTrait| {
if v.config().mouse_events {
match button {
MouseButton::Left => v.config_mut().mouse_down.0 = true,
MouseButton::Middle => v.config_mut().mouse_down.1 = true,
MouseButton::Right => v.config_mut().mouse_down.2 = true,
MouseButton::Other(_) => {}
}
Some(v.mouse_down(button))
} else {
None
}
},
pos,
)
} else {
let mut vec = vec![];
if let Some(a) = self._mouse_event(
&mut |v: &mut dyn GuiElemTrait| {
let down = v.config().mouse_down;
if v.config().mouse_events
&& ((button == MouseButton::Left && down.0)
|| (button == MouseButton::Middle && down.1)
|| (button == MouseButton::Right && down.2))
{
Some(v.mouse_pressed(button))
} else {
None
}
},
pos,
) {
vec.extend(a);
};
self._recursive_all(&mut |v| {
if v.config().mouse_events {
match button {
MouseButton::Left => v.config_mut().mouse_down.0 = false,
MouseButton::Middle => v.config_mut().mouse_down.1 = false,
MouseButton::Right => v.config_mut().mouse_down.2 = false,
MouseButton::Other(_) => {}
}
vec.extend(v.mouse_up(button));
}
});
Some(vec)
}
}
fn _mouse_wheel(&mut self, diff: f32, pos: Vec2) -> Option<Vec<GuiAction>> {
self._mouse_event(
&mut |v| {
if v.config().scroll_events {
Some(v.mouse_wheel(diff))
} else {
None
}
},
pos,
)
}
fn _keyboard_event(
&mut self,
f_focus: &mut dyn FnMut(&mut dyn GuiElemTrait, &mut Vec<GuiAction>),
f_watch: &mut dyn FnMut(&mut dyn GuiElemTrait, &mut Vec<GuiAction>),
) -> Vec<GuiAction> {
let mut o = vec![];
self._keyboard_event_inner(&mut Some(f_focus), f_watch, &mut o, true);
o
}
fn _keyboard_event_inner(
&mut self,
f_focus: &mut Option<&mut dyn FnMut(&mut dyn GuiElemTrait, &mut Vec<GuiAction>)>,
f_watch: &mut dyn FnMut(&mut dyn GuiElemTrait, &mut Vec<GuiAction>),
events: &mut Vec<GuiAction>,
focus: bool,
) {
f_watch(self.elem_mut(), events);
let focus_index = self.config().keyboard_focus_index;
for (i, child) in self.children().enumerate() {
child._keyboard_event_inner(f_focus, f_watch, events, focus && i == focus_index);
}
if focus {
// we have focus and no child has consumed f_focus
if let Some(f) = f_focus.take() {
f(self.elem_mut(), events)
}
}
}
fn _keyboard_move_focus(&mut self, decrement: bool, refocus: bool) -> bool {
let mut focus_index = if refocus {
usize::MAX
} else {
self.config().keyboard_focus_index
};
let allow_focus = self.config().keyboard_events_focus;
let mut children = self.children().collect::<Vec<_>>();
if focus_index == usize::MAX {
if decrement {
focus_index = children.len().saturating_sub(1);
} else {
focus_index = 0;
}
}
let mut changed = refocus;
let ok = loop {
if let Some(child) = children.get_mut(focus_index) {
if child._keyboard_move_focus(decrement, changed) {
break true;
} else {
changed = true;
if !decrement {
focus_index += 1;
} else {
focus_index = focus_index.wrapping_sub(1);
}
}
} else {
focus_index = usize::MAX;
break allow_focus && refocus;
}
};
self.config_mut().keyboard_focus_index = focus_index;
ok
}
fn _keyboard_reset_focus(&mut self) -> bool {
let mut index = usize::MAX;
for (i, c) in self.children().enumerate() {
if c._keyboard_reset_focus() {
index = i;
break;
}
}
let wants = std::mem::replace(&mut self.config_mut().request_keyboard_focus, false);
self.config_mut().keyboard_focus_index = index;
index != usize::MAX || wants
}
}
pub trait GuiElemChildren {
fn iter(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_>;
}
impl<T: GuiElemTrait> GuiElemChildren for T {
fn iter(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new([self.elem_mut()].into_iter())
}
}
impl<A: GuiElemTrait, B: GuiElemTrait> GuiElemChildren for (A, B) {
fn iter(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new([self.0.elem_mut(), self.1.elem_mut()].into_iter())
}
}
impl<A: GuiElemTrait, B: GuiElemTrait, C: GuiElemTrait> GuiElemChildren for (A, B, C) {
fn iter(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new([self.0.elem_mut(), self.1.elem_mut(), self.2.elem_mut()].into_iter())
}
}
impl<A: GuiElemTrait, B: GuiElemTrait, C: GuiElemTrait, D: GuiElemTrait> GuiElemChildren
for (A, B, C, D)
{
fn iter(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(
[
self.0.elem_mut(),
self.1.elem_mut(),
self.2.elem_mut(),
self.3.elem_mut(),
]
.into_iter(),
)
}
}
impl<A: GuiElemTrait, B: GuiElemTrait, C: GuiElemTrait, D: GuiElemTrait, E: GuiElemTrait>
GuiElemChildren for (A, B, C, D, E)
{
fn iter(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(
[
self.0.elem_mut(),
self.1.elem_mut(),
self.2.elem_mut(),
self.3.elem_mut(),
self.4.elem_mut(),
]
.into_iter(),
)
}
}
#[derive(Debug, Clone)]
@ -485,7 +761,7 @@ pub enum GuiAction {
OpenMain,
SetIdle(bool),
OpenSettings(bool),
OpenEditPanel(GuiElem),
OpenEditPanel(Box<dyn GuiElemTrait>),
CloseEditPanel,
/// Build the GuiAction(s) later, when we have access to the Database (can turn an AlbumId into a QueueContent::Folder, etc)
Build(Box<dyn FnOnce(&mut Database) -> Vec<Self>>),
@ -536,263 +812,6 @@ pub struct DrawInfo<'a> {
pub gui_config: &'a GuiConfig,
}
/// Generic wrapper over anything that implements GuiElemTrait
pub struct GuiElem {
pub inner: Box<dyn GuiElemTrait>,
}
impl Clone for GuiElem {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone_gui(),
}
}
}
impl GuiElem {
pub fn new<T: GuiElemTrait + 'static>(inner: T) -> Self {
Self {
inner: Box::new(inner),
}
}
pub fn try_as<T: Any>(&self) -> Option<&T> {
self.inner.any().downcast_ref()
}
pub fn try_as_mut<T: Any>(&mut self) -> Option<&mut T> {
self.inner.any_mut().downcast_mut()
}
pub fn draw(&mut self, info: &mut DrawInfo, g: &mut Graphics2D) {
if !self.inner.config_mut().enabled {
return;
}
// adjust info
let npos = adjust_area(&info.pos, &self.inner.config_mut().pos);
let ppos = std::mem::replace(&mut info.pos, npos);
if info.child_has_keyboard_focus {
if self.inner.config().keyboard_focus_index == usize::MAX {
info.has_keyboard_focus = true;
info.child_has_keyboard_focus = false;
}
}
// call trait's draw function
self.inner.draw(info, g);
// reset info
info.has_keyboard_focus = false;
let focus_path = info.child_has_keyboard_focus;
// children (in reverse order - first element has the highest priority)
let kbd_focus_index = self.inner.config().keyboard_focus_index;
if self.inner.draw_rev() {
for (i, c) in self
.inner
.children()
.collect::<Vec<_>>()
.into_iter()
.enumerate()
.rev()
{
info.child_has_keyboard_focus = focus_path && i == kbd_focus_index;
c.draw(info, g);
}
} else {
for (i, c) in self.inner.children().enumerate() {
info.child_has_keyboard_focus = focus_path && i == kbd_focus_index;
c.draw(info, g);
}
}
// reset pt. 2
info.child_has_keyboard_focus = focus_path;
self.inner.config_mut().pixel_pos = std::mem::replace(&mut info.pos, ppos);
}
/// recursively applies the function to all gui elements below and including this one
pub fn recursive_all<F: FnMut(&mut GuiElem)>(&mut self, f: &mut F) {
f(self);
for c in self.inner.children() {
c.recursive_all(f);
}
}
fn mouse_event<F: FnMut(&mut GuiElem) -> Option<Vec<GuiAction>>>(
&mut self,
condition: &mut F,
pos: Vec2,
) -> Option<Vec<GuiAction>> {
for c in &mut self.inner.children() {
if c.inner.config().enabled {
if c.inner.config().pixel_pos.contains(pos) {
if let Some(v) = c.mouse_event(condition, pos) {
return Some(v);
}
}
}
}
condition(self)
}
fn release_drag(
&mut self,
dragged: &mut Option<Dragging>,
pos: Vec2,
) -> Option<Vec<GuiAction>> {
self.mouse_event(
&mut |v| {
if v.inner.config().drag_target {
if let Some(d) = dragged.take() {
return Some(v.inner.dragged(d));
}
}
None
},
pos,
)
}
fn mouse_button(
&mut self,
button: MouseButton,
down: bool,
pos: Vec2,
) -> Option<Vec<GuiAction>> {
if down {
self.mouse_event(
&mut |v: &mut GuiElem| {
if v.inner.config().mouse_events {
match button {
MouseButton::Left => v.inner.config_mut().mouse_down.0 = true,
MouseButton::Middle => v.inner.config_mut().mouse_down.1 = true,
MouseButton::Right => v.inner.config_mut().mouse_down.2 = true,
MouseButton::Other(_) => {}
}
Some(v.inner.mouse_down(button))
} else {
None
}
},
pos,
)
} else {
let mut vec = vec![];
if let Some(a) = self.mouse_event(
&mut |v: &mut GuiElem| {
let down = v.inner.config().mouse_down;
if v.inner.config().mouse_events
&& ((button == MouseButton::Left && down.0)
|| (button == MouseButton::Middle && down.1)
|| (button == MouseButton::Right && down.2))
{
Some(v.inner.mouse_pressed(button))
} else {
None
}
},
pos,
) {
vec.extend(a);
};
self.recursive_all(&mut |v| {
if v.inner.config().mouse_events {
match button {
MouseButton::Left => v.inner.config_mut().mouse_down.0 = false,
MouseButton::Middle => v.inner.config_mut().mouse_down.1 = false,
MouseButton::Right => v.inner.config_mut().mouse_down.2 = false,
MouseButton::Other(_) => {}
}
vec.extend(v.inner.mouse_up(button));
}
});
Some(vec)
}
}
fn mouse_wheel(&mut self, diff: f32, pos: Vec2) -> Option<Vec<GuiAction>> {
self.mouse_event(
&mut |v| {
if v.inner.config().scroll_events {
Some(v.inner.mouse_wheel(diff))
} else {
None
}
},
pos,
)
}
fn keyboard_event<
F: FnOnce(&mut Self, &mut Vec<GuiAction>),
G: FnMut(&mut Self, &mut Vec<GuiAction>),
>(
&mut self,
f_focus: F,
mut f_watch: G,
) -> Vec<GuiAction> {
let mut o = vec![];
self.keyboard_event_inner(&mut Some(f_focus), &mut f_watch, &mut o, true);
o
}
fn keyboard_event_inner<
F: FnOnce(&mut Self, &mut Vec<GuiAction>),
G: FnMut(&mut Self, &mut Vec<GuiAction>),
>(
&mut self,
f_focus: &mut Option<F>,
f_watch: &mut G,
events: &mut Vec<GuiAction>,
focus: bool,
) {
f_watch(self, events);
let focus_index = self.inner.config().keyboard_focus_index;
for (i, child) in self.inner.children().enumerate() {
child.keyboard_event_inner(f_focus, f_watch, events, focus && i == focus_index);
}
if focus {
// we have focus and no child has consumed f_focus
if let Some(f) = f_focus.take() {
f(self, events)
}
}
}
fn keyboard_move_focus(&mut self, decrement: bool, refocus: bool) -> bool {
let mut focus_index = if refocus {
usize::MAX
} else {
self.inner.config().keyboard_focus_index
};
let allow_focus = self.inner.config().keyboard_events_focus;
let mut children = self.inner.children().collect::<Vec<_>>();
if focus_index == usize::MAX {
if decrement {
focus_index = children.len().saturating_sub(1);
} else {
focus_index = 0;
}
}
let mut changed = refocus;
let ok = loop {
if let Some(child) = children.get_mut(focus_index) {
if child.keyboard_move_focus(decrement, changed) {
break true;
} else {
changed = true;
if !decrement {
focus_index += 1;
} else {
focus_index = focus_index.wrapping_sub(1);
}
}
} else {
focus_index = usize::MAX;
break allow_focus && refocus;
}
};
self.inner.config_mut().keyboard_focus_index = focus_index;
ok
}
fn keyboard_reset_focus(&mut self) -> bool {
let mut index = usize::MAX;
for (i, c) in self.inner.children().enumerate() {
if c.keyboard_reset_focus() {
index = i;
break;
}
}
let wants = std::mem::replace(&mut self.inner.config_mut().request_keyboard_focus, false);
self.inner.config_mut().keyboard_focus_index = index;
index != usize::MAX || wants
}
}
pub fn adjust_area(outer: &Rectangle, rel_area: &Rectangle) -> Rectangle {
Rectangle::new(
adjust_pos(outer, rel_area.top_left()),
@ -820,12 +839,12 @@ impl Gui {
eprintln!("Error sending command to server: {e}");
}
}
GuiAction::ResetKeyboardFocus => _ = self.gui.keyboard_reset_focus(),
GuiAction::ResetKeyboardFocus => _ = self.gui._keyboard_reset_focus(),
GuiAction::SetDragging(d) => self.dragging = d,
GuiAction::SetLineHeight(h) => {
self.line_height = h;
self.gui
.recursive_all(&mut |e| e.inner.config_mut().redraw = true);
._recursive_all(&mut |e| e.config_mut().redraw = true);
}
GuiAction::LoadCover(id) => {
self.covers
@ -838,7 +857,6 @@ impl Gui {
GuiAction::SetIdle(v) => {
if let Some(gui) = self
.gui
.inner
.any_mut()
.downcast_mut::<WithFocusHotkey<GuiScreen>>()
{
@ -850,7 +868,6 @@ impl Gui {
GuiAction::OpenSettings(v) => {
if let Some(gui) = self
.gui
.inner
.any_mut()
.downcast_mut::<WithFocusHotkey<GuiScreen>>()
{
@ -865,7 +882,6 @@ impl Gui {
GuiAction::OpenMain => {
if let Some(gui) = self
.gui
.inner
.any_mut()
.downcast_mut::<WithFocusHotkey<GuiScreen>>()
{
@ -880,7 +896,6 @@ impl Gui {
GuiAction::OpenEditPanel(p) => {
if let Some(gui) = self
.gui
.inner
.any_mut()
.downcast_mut::<WithFocusHotkey<GuiScreen>>()
{
@ -896,7 +911,6 @@ impl Gui {
GuiAction::CloseEditPanel => {
if let Some(gui) = self
.gui
.inner
.any_mut()
.downcast_mut::<WithFocusHotkey<GuiScreen>>()
{
@ -937,7 +951,7 @@ impl WindowHandler<GuiEvent> for Gui {
dragging: self.dragging.take(),
gui_config: &cfg,
};
self.gui.draw(&mut info, graphics);
self.gui._draw(&mut info, graphics);
let actions = std::mem::replace(&mut info.actions, Vec::with_capacity(0));
self.dragging = info.dragging.take();
if let Some((d, f)) = &mut self.dragging {
@ -988,7 +1002,7 @@ impl WindowHandler<GuiEvent> for Gui {
self.last_draw = start;
}
fn on_mouse_button_down(&mut self, helper: &mut WindowHelper<GuiEvent>, button: MouseButton) {
if let Some(a) = self.gui.mouse_button(button, true, self.mouse_pos.clone()) {
if let Some(a) = self.gui._mouse_button(button, true, self.mouse_pos.clone()) {
for a in a {
self.exec_gui_action(a)
}
@ -997,7 +1011,7 @@ impl WindowHandler<GuiEvent> for Gui {
}
fn on_mouse_button_up(&mut self, helper: &mut WindowHelper<GuiEvent>, button: MouseButton) {
if self.dragging.is_some() {
if let Some(a) = self.gui.release_drag(
if let Some(a) = self.gui._release_drag(
&mut self.dragging.take().map(|v| v.0),
self.mouse_pos.clone(),
) {
@ -1006,7 +1020,10 @@ impl WindowHandler<GuiEvent> for Gui {
}
}
}
if let Some(a) = self.gui.mouse_button(button, false, self.mouse_pos.clone()) {
if let Some(a) = self
.gui
._mouse_button(button, false, self.mouse_pos.clone())
{
for a in a {
self.exec_gui_action(a)
}
@ -1030,7 +1047,7 @@ impl WindowHandler<GuiEvent> for Gui {
* self.last_height
}
};
if let Some(a) = self.gui.mouse_wheel(dist, self.mouse_pos.clone()) {
if let Some(a) = self.gui._mouse_wheel(dist, self.mouse_pos.clone()) {
for a in a {
self.exec_gui_action(a)
}
@ -1039,23 +1056,15 @@ impl WindowHandler<GuiEvent> for Gui {
}
fn on_keyboard_char(&mut self, helper: &mut WindowHelper<GuiEvent>, unicode_codepoint: char) {
helper.request_redraw();
for a in self.gui.keyboard_event(
|e, a| {
if e.inner.config().keyboard_events_focus {
a.append(
&mut e
.inner
.char_focus(self.modifiers.clone(), unicode_codepoint),
);
for a in self.gui._keyboard_event(
&mut |e, a| {
if e.config().keyboard_events_focus {
a.append(&mut e.char_focus(self.modifiers.clone(), unicode_codepoint));
}
},
|e, a| {
if e.inner.config().keyboard_events_watch {
a.append(
&mut e
.inner
.char_watch(self.modifiers.clone(), unicode_codepoint),
);
&mut |e, a| {
if e.config().keyboard_events_watch {
a.append(&mut e.char_watch(self.modifiers.clone(), unicode_codepoint));
}
},
) {
@ -1071,13 +1080,13 @@ impl WindowHandler<GuiEvent> for Gui {
helper.request_redraw();
if let Some(VirtualKeyCode::Tab) = virtual_key_code {
if !(self.modifiers.ctrl() || self.modifiers.alt() || self.modifiers.logo()) {
self.gui.keyboard_move_focus(self.modifiers.shift(), false);
self.gui._keyboard_move_focus(self.modifiers.shift(), false);
}
}
for a in self.gui.keyboard_event(
|e, a| {
if e.inner.config().keyboard_events_focus {
a.append(&mut e.inner.key_focus(
for a in self.gui._keyboard_event(
&mut |e, a| {
if e.config().keyboard_events_focus {
a.append(&mut e.key_focus(
self.modifiers.clone(),
true,
virtual_key_code,
@ -1085,9 +1094,9 @@ impl WindowHandler<GuiEvent> for Gui {
));
}
},
|e, a| {
if e.inner.config().keyboard_events_watch {
a.append(&mut e.inner.key_watch(
&mut |e, a| {
if e.config().keyboard_events_watch {
a.append(&mut e.key_watch(
self.modifiers.clone(),
true,
virtual_key_code,
@ -1106,10 +1115,10 @@ impl WindowHandler<GuiEvent> for Gui {
scancode: KeyScancode,
) {
helper.request_redraw();
for a in self.gui.keyboard_event(
|e, a| {
if e.inner.config().keyboard_events_focus {
a.append(&mut e.inner.key_focus(
for a in self.gui._keyboard_event(
&mut |e, a| {
if e.config().keyboard_events_focus {
a.append(&mut e.key_focus(
self.modifiers.clone(),
false,
virtual_key_code,
@ -1117,9 +1126,9 @@ impl WindowHandler<GuiEvent> for Gui {
));
}
},
|e, a| {
if e.inner.config().keyboard_events_watch {
a.append(&mut e.inner.key_watch(
&mut |e, a| {
if e.config().keyboard_events_watch {
a.append(&mut e.key_watch(
self.modifiers.clone(),
false,
virtual_key_code,
@ -1142,11 +1151,11 @@ impl WindowHandler<GuiEvent> for Gui {
match user_event {
GuiEvent::Refresh => helper.request_redraw(),
GuiEvent::UpdatedLibrary => {
self.gui.recursive_all(&mut |e| e.inner.updated_library());
self.gui._recursive_all(&mut |e| e.updated_library());
helper.request_redraw();
}
GuiEvent::UpdatedQueue => {
self.gui.recursive_all(&mut |e| e.inner.updated_queue());
self.gui._recursive_all(&mut |e| e.updated_queue());
helper.request_redraw();
}
GuiEvent::Exit => helper.terminate_loop(),
@ -1159,7 +1168,7 @@ impl WindowHandler<GuiEvent> for Gui {
fn on_resize(&mut self, _helper: &mut WindowHelper<GuiEvent>, size_pixels: UVec2) {
self.size = size_pixels;
self.gui
.recursive_all(&mut |e| e.inner.config_mut().redraw = true);
._recursive_all(&mut |e| e.config_mut().redraw = true);
}
}

View File

@ -3,7 +3,7 @@ use std::{sync::Arc, time::Instant};
use speedy2d::{color::Color, dimen::Vec2, shape::Rectangle, window::MouseButton};
use crate::{
gui::{DrawInfo, GuiAction, GuiElem, GuiElemCfg, GuiElemTrait},
gui::{DrawInfo, GuiAction, GuiElemCfg, GuiElemTrait},
gui_text::Label,
};
@ -15,21 +15,24 @@ Mostly containers for other GuiElems.
*/
/// A simple container for zero, one, or multiple child GuiElems. Can optionally fill the background with a color.
#[derive(Clone)]
pub struct Panel {
config: GuiElemCfg,
pub children: Vec<GuiElem>,
pub children: Vec<Box<dyn GuiElemTrait>>,
pub background: Option<Color>,
}
impl Panel {
pub fn new(config: GuiElemCfg, children: Vec<GuiElem>) -> Self {
pub fn new(config: GuiElemCfg, children: Vec<Box<dyn GuiElemTrait>>) -> Self {
Self {
config,
children,
background: None,
}
}
pub fn with_background(config: GuiElemCfg, children: Vec<GuiElem>, background: Color) -> Self {
pub fn with_background(
config: GuiElemCfg,
children: Vec<Box<dyn GuiElemTrait>>,
background: Color,
) -> Self {
Self {
config,
children,
@ -44,8 +47,8 @@ impl GuiElemTrait for Panel {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -53,8 +56,11 @@ impl GuiElemTrait for Panel {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn draw(&mut self, info: &mut DrawInfo, g: &mut speedy2d::Graphics2D) {
if let Some(c) = self.background {
@ -64,25 +70,25 @@ impl GuiElemTrait for Panel {
}
#[derive(Clone)]
pub struct Square {
pub struct Square<T: GuiElemTrait> {
config: GuiElemCfg,
pub inner: GuiElem,
pub inner: T,
}
impl Square {
pub fn new(mut config: GuiElemCfg, inner: GuiElem) -> Self {
impl<T: GuiElemTrait> Square<T> {
pub fn new(mut config: GuiElemCfg, inner: T) -> Self {
config.redraw = true;
Self { config, inner }
}
}
impl GuiElemTrait for Square {
impl<T: GuiElemTrait> GuiElemTrait for Square<T> {
fn config(&self) -> &GuiElemCfg {
&self.config
}
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new([&mut self.inner].into_iter())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new([self.inner.elem_mut()].into_iter())
}
fn any(&self) -> &dyn std::any::Any {
self
@ -90,8 +96,11 @@ impl GuiElemTrait for Square {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn draw(&mut self, info: &mut DrawInfo, _g: &mut speedy2d::Graphics2D) {
if info.pos.size() != self.config.pixel_pos.size() {
@ -101,21 +110,20 @@ impl GuiElemTrait for Square {
self.config.redraw = false;
if info.pos.width() > info.pos.height() {
let w = 0.5 * info.pos.height() / info.pos.width();
self.inner.inner.config_mut().pos =
self.inner.config_mut().pos =
Rectangle::from_tuples((0.5 - w, 0.0), (0.5 + w, 1.0));
} else {
let h = 0.5 * info.pos.width() / info.pos.height();
self.inner.inner.config_mut().pos =
self.inner.config_mut().pos =
Rectangle::from_tuples((0.0, 0.5 - h), (1.0, 0.5 + h));
}
}
}
}
#[derive(Clone)]
pub struct ScrollBox {
config: GuiElemCfg,
pub children: Vec<(GuiElem, f32)>,
pub children: Vec<(Box<dyn GuiElemTrait>, f32)>,
pub size_unit: ScrollBoxSizeUnit,
pub scroll_target: f32,
pub scroll_display: f32,
@ -136,7 +144,7 @@ impl ScrollBox {
pub fn new(
config: GuiElemCfg,
size_unit: ScrollBoxSizeUnit,
children: Vec<(GuiElem, f32)>,
children: Vec<(Box<dyn GuiElemTrait>, f32)>,
) -> Self {
// config.redraw = true;
Self {
@ -162,13 +170,13 @@ impl GuiElemTrait for ScrollBox {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(
self.children
.iter_mut()
.map(|(v, _)| v)
.skip_while(|v| v.inner.config().pos.bottom_right().y < 0.0)
.take_while(|v| v.inner.config().pos.top_left().y <= 1.0),
.map(|(v, _)| v.as_mut())
.skip_while(|v| v.config().pos.bottom_right().y < 0.0)
.take_while(|v| v.config().pos.top_left().y <= 1.0),
)
}
fn draw_rev(&self) -> bool {
@ -180,8 +188,11 @@ impl GuiElemTrait for ScrollBox {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn draw(&mut self, info: &mut DrawInfo, g: &mut speedy2d::Graphics2D) {
if self.config.pixel_pos.size() != info.pos.size() {
@ -212,7 +223,7 @@ impl GuiElemTrait for ScrollBox {
let h_rel = self.size_unit.to_rel(*h, info.pos.height());
let y_rel = self.size_unit.to_rel(y_pos, info.pos.height());
if y_rel + h_rel >= 0.0 && y_rel <= 1.0 {
let cfg = e.inner.config_mut();
let cfg = e.config_mut();
cfg.enabled = true;
cfg.pos = Rectangle::new(
Vec2::new(cfg.pos.top_left().x, 0.0f32.max(y_rel)),
@ -222,7 +233,7 @@ impl GuiElemTrait for ScrollBox {
),
);
} else {
e.inner.config_mut().enabled = false;
e.config_mut().enabled = false;
}
y_pos += *h;
}
@ -304,10 +315,9 @@ impl ScrollBoxSizeUnit {
}
}
#[derive(Clone)]
pub struct Button {
config: GuiElemCfg,
pub children: Vec<GuiElem>,
pub children: Vec<Box<dyn GuiElemTrait>>,
action: Arc<dyn Fn(&mut Self) -> Vec<GuiAction> + 'static>,
}
impl Button {
@ -315,7 +325,7 @@ impl Button {
pub fn new<F: Fn(&mut Self) -> Vec<GuiAction> + 'static>(
config: GuiElemCfg,
action: F,
children: Vec<GuiElem>,
children: Vec<Box<dyn GuiElemTrait>>,
) -> Self {
Self {
config: config.w_mouse(),
@ -331,8 +341,8 @@ impl GuiElemTrait for Button {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -340,8 +350,11 @@ impl GuiElemTrait for Button {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn mouse_pressed(&mut self, button: MouseButton) -> Vec<GuiAction> {
if button == MouseButton::Left {
@ -366,10 +379,9 @@ impl GuiElemTrait for Button {
}
}
#[derive(Clone)]
pub struct Slider {
pub config: GuiElemCfg,
pub children: Vec<GuiElem>,
pub children: Vec<Box<dyn GuiElemTrait>>,
pub slider_pos: Rectangle,
pub min: f64,
pub max: f64,
@ -406,7 +418,7 @@ impl Slider {
min: f64,
max: f64,
val: f64,
children: Vec<GuiElem>,
children: Vec<Box<dyn GuiElemTrait>>,
on_update: F,
) -> Self {
Self {
@ -436,7 +448,7 @@ impl Slider {
min,
max,
val,
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
String::new(),
Color::WHITE,
@ -447,7 +459,7 @@ impl Slider {
move |s, i| {
if s.display || s.display_since.is_some() {
let mut label = s.children.pop().unwrap();
if let Some(l) = label.inner.any_mut().downcast_mut::<Label>() {
if let Some(l) = label.any_mut().downcast_mut::<Label>() {
let display_state = if let Some(since) =
s.display_since.map(|v| v.elapsed().as_secs_f64() / 0.2)
{
@ -502,8 +514,8 @@ impl GuiElemTrait for Slider {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -511,8 +523,11 @@ impl GuiElemTrait for Slider {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn draw(&mut self, info: &mut DrawInfo, g: &mut speedy2d::Graphics2D) {
if self.display != (self.config.mouse_down.0 || info.pos.contains(info.mouse_pos)) {

View File

@ -1,6 +1,6 @@
use std::{
collections::{HashMap, HashSet},
sync::{atomic::AtomicBool, mpsc, Arc, Mutex},
sync::mpsc,
};
use musicdb_lib::{
@ -13,14 +13,14 @@ use musicdb_lib::{
use speedy2d::{color::Color, dimen::Vec2, shape::Rectangle};
use crate::{
gui::{Dragging, DrawInfo, GuiAction, GuiElem, GuiElemCfg, GuiElemTrait},
gui::{Dragging, DrawInfo, GuiAction, GuiElemCfg, GuiElemTrait},
gui_base::{Button, Panel, ScrollBox},
gui_text::{Label, TextField},
};
pub struct GuiEdit {
config: GuiElemCfg,
children: Vec<GuiElem>,
children: Vec<Box<dyn GuiElemTrait>>,
editable: Editable,
editing: Editing,
reload: bool,
@ -78,20 +78,20 @@ impl GuiEdit {
apply_change,
change_recv,
children: vec![
GuiElem::new(ScrollBox::new(
Box::new(ScrollBox::new(
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.0), (1.0, 0.6))),
crate::gui_base::ScrollBoxSizeUnit::Pixels,
vec![],
)),
GuiElem::new(ScrollBox::new(
Box::new(ScrollBox::new(
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.6), (1.0, 0.9))),
crate::gui_base::ScrollBoxSizeUnit::Pixels,
vec![],
)),
GuiElem::new(Button::new(
Box::new(Button::new(
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.95), (0.33, 1.0))),
|_| vec![GuiAction::CloseEditPanel],
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
"Back".to_string(),
Color::WHITE,
@ -99,13 +99,13 @@ impl GuiEdit {
Vec2::new(0.5, 0.5),
))],
)),
GuiElem::new(Button::new(
Box::new(Button::new(
GuiElemCfg::at(Rectangle::from_tuples((0.33, 0.95), (0.67, 1.0))),
move |_| {
_ = ac1.send(Box::new(|s| s.reload = true));
vec![]
},
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
"Reload".to_string(),
Color::WHITE,
@ -113,13 +113,13 @@ impl GuiEdit {
Vec2::new(0.5, 0.5),
))],
)),
GuiElem::new(Button::new(
Box::new(Button::new(
GuiElemCfg::at(Rectangle::from_tuples((0.67, 0.95), (1.0, 1.0))),
move |_| {
_ = ac2.send(Box::new(|s| s.send = true));
vec![]
},
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
"Send".to_string(),
Color::WHITE,
@ -138,8 +138,8 @@ impl GuiElemTrait for GuiEdit {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -147,8 +147,11 @@ impl GuiElemTrait for GuiEdit {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn draw(&mut self, info: &mut crate::gui::DrawInfo, g: &mut speedy2d::Graphics2D) {
loop {
@ -300,14 +303,13 @@ impl GuiElemTrait for GuiEdit {
self.rebuild_main = true;
self.rebuild_changes = true;
}
if let Some(sb) = self.children[0].inner.any_mut().downcast_mut::<ScrollBox>() {
if let Some(sb) = self.children[0].any_mut().downcast_mut::<ScrollBox>() {
for (c, _) in sb.children.iter() {
if let Some(p) = c
.inner
.any()
.downcast_ref::<Panel>()
.and_then(|p| p.children.get(0))
.and_then(|e| e.inner.any().downcast_ref::<TextField>())
.and_then(|e| e.any().downcast_ref::<TextField>())
{
if p.label_input().content.will_redraw() {
if let Some((key, _)) = p.label_hint().content.get_text().split_once(':') {
@ -364,7 +366,7 @@ impl GuiElemTrait for GuiEdit {
}
if self.config.redraw {
self.config.redraw = false;
if let Some(sb) = self.children[0].inner.any_mut().downcast_mut::<ScrollBox>() {
if let Some(sb) = self.children[0].any_mut().downcast_mut::<ScrollBox>() {
for c in sb.children.iter_mut() {
c.1 = info.line_height;
}
@ -406,7 +408,7 @@ impl GuiElemTrait for GuiEdit {
}
impl GuiEdit {
fn rebuild_main(&mut self, info: &mut DrawInfo) {
if let Some(sb) = self.children[0].inner.any_mut().downcast_mut::<ScrollBox>() {
if let Some(sb) = self.children[0].any_mut().downcast_mut::<ScrollBox>() {
sb.children.clear();
sb.config_mut().redraw = true;
match &self.editing {
@ -431,9 +433,9 @@ impl GuiEdit {
name
};
sb.children.push((
GuiElem::new(Panel::new(
Box::new(Panel::new(
GuiElemCfg::default(),
vec![GuiElem::new(TextField::new(
vec![Box::new(TextField::new(
GuiElemCfg::default(),
name,
Color::LIGHT_GRAY,
@ -455,9 +457,9 @@ impl GuiEdit {
cover
};
sb.children.push((
GuiElem::new(Panel::new(
Box::new(Panel::new(
GuiElemCfg::default(),
vec![GuiElem::new(TextField::new(
vec![Box::new(TextField::new(
GuiElemCfg::default(),
cover,
Color::LIGHT_GRAY,
@ -480,21 +482,18 @@ impl GuiEdit {
{
fn get_id(s: &mut GuiEdit) -> Option<AlbumId> {
s.children[0]
.inner
.children()
.collect::<Vec<_>>()
.into_iter()
.rev()
.nth(2)
.unwrap()
.inner
.any_mut()
.downcast_mut::<Panel>()
.unwrap()
.children()
.next()
.unwrap()
.inner
.any_mut()
.downcast_mut::<TextField>()
.unwrap()
@ -506,7 +505,7 @@ impl GuiEdit {
}
let add_button = {
let apply_change = self.apply_change.clone();
GuiElem::new(Button::new(
Box::new(Button::new(
GuiElemCfg::at(Rectangle::from_tuples((0.9, 0.0), (1.0, 1.0))),
move |_| {
_ = apply_change.send(Box::new(move |s| {
@ -524,7 +523,7 @@ impl GuiEdit {
}));
vec![]
},
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
format!("add"),
Color::GREEN,
@ -533,28 +532,28 @@ impl GuiEdit {
))],
))
};
let name = GuiElem::new(TextField::new(
let name = Box::new(TextField::new(
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.0), (0.9, 1.0))),
"add album by id".to_string(),
Color::LIGHT_GRAY,
Color::WHITE,
));
sb.children.push((
GuiElem::new(Panel::new(GuiElemCfg::default(), vec![name, add_button])),
Box::new(Panel::new(GuiElemCfg::default(), vec![name, add_button])),
info.line_height * 2.0,
));
}
for (album_id, count) in albums {
let album = info.database.albums().get(&album_id);
let name = GuiElem::new(Button::new(
let name = Box::new(Button::new(
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.0), (1.0, 1.0))),
move |_| {
vec![GuiAction::OpenEditPanel(GuiElem::new(GuiEdit::new(
vec![GuiAction::OpenEditPanel(Box::new(GuiEdit::new(
GuiElemCfg::default(),
Editable::Album(vec![album_id]),
)))]
},
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
if let Some(a) = album {
a.name.clone()
@ -567,7 +566,7 @@ impl GuiEdit {
))],
));
sb.children.push((
GuiElem::new(Panel::new(GuiElemCfg::default(), vec![name])),
Box::new(Panel::new(GuiElemCfg::default(), vec![name])),
info.line_height,
));
}
@ -578,7 +577,7 @@ impl GuiEdit {
}
}
fn rebuild_changes(&mut self, info: &mut DrawInfo) {
if let Some(sb) = self.children[1].inner.any_mut().downcast_mut::<ScrollBox>() {
if let Some(sb) = self.children[1].any_mut().downcast_mut::<ScrollBox>() {
sb.children.clear();
sb.config_mut().redraw = true;
match &self.editing {
@ -598,7 +597,7 @@ impl GuiEdit {
};
let s = self.apply_change.clone();
sb.children.push((
GuiElem::new(Button::new(
Box::new(Button::new(
GuiElemCfg::default(),
move |_| {
_ = s.send(Box::new(move |s| {
@ -613,7 +612,7 @@ impl GuiEdit {
}));
vec![]
},
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
text,
Color::WHITE,

View File

@ -25,7 +25,7 @@ use speedy2d::{
};
use crate::{
gui::{Dragging, DrawInfo, GuiAction, GuiElem, GuiElemCfg, GuiElemTrait},
gui::{Dragging, DrawInfo, GuiAction, GuiElemCfg, GuiElemTrait},
gui_base::{Button, Panel, ScrollBox},
gui_text::{self, AdvancedLabel, Label, TextField},
gui_wrappers::WithFocusHotkey,
@ -42,7 +42,7 @@ with Regex search and drag-n-drop.
pub struct LibraryBrowser {
config: GuiElemCfg,
pub children: Vec<GuiElem>,
pub children: Vec<Box<dyn GuiElemTrait>>,
// - - -
library_sorted: Vec<(ArtistId, Vec<SongId>, Vec<(AlbumId, Vec<SongId>)>)>,
library_filtered: Vec<(
@ -138,7 +138,7 @@ impl LibraryBrowser {
);
vec![]
},
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
"more".to_owned(),
Color::GRAY,
@ -162,12 +162,12 @@ impl LibraryBrowser {
Self {
config: config.w_keyboard_watch(),
children: vec![
GuiElem::new(search_artist),
GuiElem::new(search_album),
GuiElem::new(search_song),
GuiElem::new(library_scroll_box),
GuiElem::new(filter_button),
GuiElem::new(FilterPanel::new(
Box::new(search_artist),
Box::new(search_album),
Box::new(search_song),
Box::new(library_scroll_box),
Box::new(filter_button),
Box::new(FilterPanel::new(
Arc::clone(&search_settings_changed),
Arc::clone(&search_is_case_sensitive),
Arc::clone(&search_prefer_start_matches),
@ -177,9 +177,9 @@ impl LibraryBrowser {
selected.clone(),
do_something_sender.clone(),
)),
GuiElem::new(Panel::with_background(
Box::new(Panel::with_background(
GuiElemCfg::default().disabled(),
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
String::new(),
Color::LIGHT_GRAY,
@ -265,8 +265,8 @@ impl GuiElemTrait for LibraryBrowser {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -274,8 +274,11 @@ impl GuiElemTrait for LibraryBrowser {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn draw_rev(&self) -> bool {
false
@ -313,8 +316,13 @@ impl GuiElemTrait for LibraryBrowser {
}
}
{
let v = &mut self.children[0].try_as_mut::<TextField>().unwrap().children[0]
.try_as_mut::<Label>()
let v = &mut self.children[0]
.any_mut()
.downcast_mut::<TextField>()
.unwrap()
.children[0]
.any_mut()
.downcast_mut::<Label>()
.unwrap()
.content;
if rebuild_regex || v.will_redraw() && self.search_artist != *v.get_text() {
@ -332,8 +340,13 @@ impl GuiElemTrait for LibraryBrowser {
}
}
{
let v = &mut self.children[1].try_as_mut::<TextField>().unwrap().children[0]
.try_as_mut::<Label>()
let v = &mut self.children[1]
.any_mut()
.downcast_mut::<TextField>()
.unwrap()
.children[0]
.any_mut()
.downcast_mut::<Label>()
.unwrap()
.content;
if rebuild_regex || v.will_redraw() && self.search_album != *v.get_text() {
@ -352,11 +365,13 @@ impl GuiElemTrait for LibraryBrowser {
}
{
let v = &mut self.children[2]
.try_as_mut::<WithFocusHotkey<TextField>>()
.any_mut()
.downcast_mut::<WithFocusHotkey<TextField>>()
.unwrap()
.inner
.children[0]
.try_as_mut::<Label>()
.any_mut()
.downcast_mut::<Label>()
.unwrap()
.content;
if rebuild_regex || v.will_redraw() && self.search_song != *v.get_text() {
@ -401,11 +416,15 @@ impl GuiElemTrait for LibraryBrowser {
if draw_filter {
let y = LP_LIB1 + (LP_LIB1S - LP_LIB1) * self.filter_state;
self.children[3]
.try_as_mut::<ScrollBox>()
.any_mut()
.downcast_mut::<ScrollBox>()
.unwrap()
.config_mut()
.pos = Rectangle::new(Vec2::new(0.0, y), Vec2::new(1.0, LP_LIB2));
let filter_panel = self.children[5].try_as_mut::<FilterPanel>().unwrap();
let filter_panel = self.children[5]
.any_mut()
.downcast_mut::<FilterPanel>()
.unwrap();
filter_panel.config_mut().pos =
Rectangle::new(Vec2::new(0.0, LP_LIB1), Vec2::new(1.0, y));
filter_panel.config.enabled = self.filter_state > 0.0;
@ -563,12 +582,10 @@ impl GuiElemTrait for LibraryBrowser {
}
} {
*self.children[6]
.inner
.any_mut()
.downcast_mut::<Panel>()
.unwrap()
.children[0]
.inner
.any_mut()
.downcast_mut::<Label>()
.unwrap()
@ -590,7 +607,7 @@ impl GuiElemTrait for LibraryBrowser {
{
if self.selected_popup_state.0 != 1.0 {
redraw = true;
self.children[6].inner.config_mut().enabled = true;
self.children[6].config_mut().enabled = true;
self.selected_popup_state.0 = 0.3 + 0.7 * self.selected_popup_state.0;
if self.selected_popup_state.0 > 0.99 {
self.selected_popup_state.0 = 1.0;
@ -602,12 +619,12 @@ impl GuiElemTrait for LibraryBrowser {
self.selected_popup_state.0 = 0.7 * self.selected_popup_state.0;
if self.selected_popup_state.0 < 0.01 {
self.selected_popup_state.0 = 0.0;
self.children[6].inner.config_mut().enabled = false;
self.children[6].config_mut().enabled = false;
}
}
}
if redraw {
self.children[6].inner.config_mut().pos = Rectangle::from_tuples(
self.children[6].config_mut().pos = Rectangle::from_tuples(
(0.0, 1.0 - 0.05 * self.selected_popup_state.0),
(1.0, 1.0),
);
@ -777,13 +794,21 @@ impl LibraryBrowser {
}
}
}
let library_scroll_box = self.children[3].try_as_mut::<ScrollBox>().unwrap();
let library_scroll_box = self.children[3]
.any_mut()
.downcast_mut::<ScrollBox>()
.unwrap();
library_scroll_box.children = elems;
library_scroll_box.config_mut().redraw = true;
}
fn build_ui_element_artist(&self, id: ArtistId, db: &Database, h: f32) -> (GuiElem, f32) {
fn build_ui_element_artist(
&self,
id: ArtistId,
db: &Database,
h: f32,
) -> (Box<dyn GuiElemTrait>, f32) {
(
GuiElem::new(ListArtist::new(
Box::new(ListArtist::new(
GuiElemCfg::default(),
id,
if let Some(v) = db.artists().get(&id) {
@ -796,7 +821,12 @@ impl LibraryBrowser {
h * 2.5,
)
}
fn build_ui_element_album(&self, id: ArtistId, db: &Database, h: f32) -> (GuiElem, f32) {
fn build_ui_element_album(
&self,
id: ArtistId,
db: &Database,
h: f32,
) -> (Box<dyn GuiElemTrait>, f32) {
let (name, duration) = if let Some(v) = db.albums().get(&id) {
let duration = v
.songs
@ -822,7 +852,7 @@ impl LibraryBrowser {
(format!("[ Album #{id} ]"), String::new())
};
(
GuiElem::new(ListAlbum::new(
Box::new(ListAlbum::new(
GuiElemCfg::default(),
id,
name,
@ -832,7 +862,12 @@ impl LibraryBrowser {
h * 1.5,
)
}
fn build_ui_element_song(&self, id: ArtistId, db: &Database, h: f32) -> (GuiElem, f32) {
fn build_ui_element_song(
&self,
id: ArtistId,
db: &Database,
h: f32,
) -> (Box<dyn GuiElemTrait>, f32) {
let (name, duration) = if let Some(v) = db.songs().get(&id) {
let duration = v.duration_millis / 1000;
(
@ -843,7 +878,7 @@ impl LibraryBrowser {
(format!("[ Song #{id} ]"), String::new())
};
(
GuiElem::new(ListSong::new(
Box::new(ListSong::new(
GuiElemCfg::default(),
id,
name,
@ -855,11 +890,10 @@ impl LibraryBrowser {
}
}
#[derive(Clone)]
struct ListArtist {
config: GuiElemCfg,
id: ArtistId,
children: Vec<GuiElem>,
children: Vec<Box<dyn GuiElemTrait>>,
mouse: bool,
mouse_pos: Vec2,
selected: Selected,
@ -878,7 +912,7 @@ impl ListArtist {
Self {
config: config.w_mouse(),
id,
children: vec![GuiElem::new(label)],
children: vec![Box::new(label)],
mouse: false,
mouse_pos: Vec2::ZERO,
selected,
@ -893,8 +927,8 @@ impl GuiElemTrait for ListArtist {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -902,8 +936,11 @@ impl GuiElemTrait for ListArtist {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn draw(&mut self, info: &mut DrawInfo, _g: &mut speedy2d::Graphics2D) {
if self.config.redraw {
@ -912,7 +949,7 @@ impl GuiElemTrait for ListArtist {
if sel != self.sel {
self.sel = sel;
if sel {
self.children.push(GuiElem::new(Panel::with_background(
self.children.push(Box::new(Panel::with_background(
GuiElemCfg::default(),
vec![],
Color::from_rgba(1.0, 1.0, 1.0, 0.2),
@ -936,11 +973,11 @@ impl GuiElemTrait for ListArtist {
.children()
.nth(3)
.unwrap()
.inner
.children()
.nth(2)
.unwrap()
.try_as()
.any()
.downcast_ref()
.unwrap(),
&gui.database.lock().unwrap(),
);
@ -963,21 +1000,12 @@ impl GuiElemTrait for ListArtist {
let mouse_pos = self.mouse_pos;
let w = self.config.pixel_pos.width();
let h = self.config.pixel_pos.height();
let mut el = GuiElem::new(self.clone());
if self.sel {
vec![]
} else {
vec![GuiAction::SetDragging(Some((
Dragging::Artist(self.id),
Some(Box::new(move |i, g| {
let sw = i.pos.width();
let sh = i.pos.height();
let x = (i.mouse_pos.x - mouse_pos.x) / sw;
let y = (i.mouse_pos.y - mouse_pos.y) / sh;
el.inner.config_mut().pos =
Rectangle::from_tuples((x, y), (x + w / sw, y + h / sh));
el.draw(i, g)
})),
None,
)))]
}
} else {
@ -998,11 +1026,10 @@ impl GuiElemTrait for ListArtist {
}
}
#[derive(Clone)]
struct ListAlbum {
config: GuiElemCfg,
id: AlbumId,
children: Vec<GuiElem>,
children: Vec<Box<dyn GuiElemTrait>>,
mouse: bool,
mouse_pos: Vec2,
selected: Selected,
@ -1036,7 +1063,7 @@ impl ListAlbum {
Self {
config: config.w_mouse(),
id,
children: vec![GuiElem::new(label)],
children: vec![Box::new(label)],
mouse: false,
mouse_pos: Vec2::ZERO,
selected,
@ -1051,8 +1078,8 @@ impl GuiElemTrait for ListAlbum {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -1060,8 +1087,11 @@ impl GuiElemTrait for ListAlbum {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn draw(&mut self, info: &mut DrawInfo, _g: &mut speedy2d::Graphics2D) {
if self.config.redraw {
@ -1070,7 +1100,7 @@ impl GuiElemTrait for ListAlbum {
if sel != self.sel {
self.sel = sel;
if sel {
self.children.push(GuiElem::new(Panel::with_background(
self.children.push(Box::new(Panel::with_background(
GuiElemCfg::default(),
vec![],
Color::from_rgba(1.0, 1.0, 1.0, 0.2),
@ -1094,11 +1124,11 @@ impl GuiElemTrait for ListAlbum {
.children()
.nth(3)
.unwrap()
.inner
.children()
.nth(2)
.unwrap()
.try_as()
.any()
.downcast_ref()
.unwrap(),
&gui.database.lock().unwrap(),
);
@ -1121,21 +1151,12 @@ impl GuiElemTrait for ListAlbum {
let mouse_pos = self.mouse_pos;
let w = self.config.pixel_pos.width();
let h = self.config.pixel_pos.height();
let mut el = GuiElem::new(self.clone());
if self.sel {
vec![]
} else {
vec![GuiAction::SetDragging(Some((
Dragging::Album(self.id),
Some(Box::new(move |i, g| {
let sw = i.pos.width();
let sh = i.pos.height();
let x = (i.mouse_pos.x - mouse_pos.x) / sw;
let y = (i.mouse_pos.y - mouse_pos.y) / sh;
el.inner.config_mut().pos =
Rectangle::from_tuples((x, y), (x + w / sw, y + h / sh));
el.draw(i, g)
})),
None,
)))]
}
} else {
@ -1156,11 +1177,10 @@ impl GuiElemTrait for ListAlbum {
}
}
#[derive(Clone)]
struct ListSong {
config: GuiElemCfg,
id: SongId,
children: Vec<GuiElem>,
children: Vec<Box<dyn GuiElemTrait>>,
mouse: bool,
mouse_pos: Vec2,
selected: Selected,
@ -1190,7 +1210,7 @@ impl ListSong {
Self {
config: config.w_mouse(),
id,
children: vec![GuiElem::new(label)],
children: vec![Box::new(label)],
mouse: false,
mouse_pos: Vec2::ZERO,
selected,
@ -1205,8 +1225,8 @@ impl GuiElemTrait for ListSong {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -1214,8 +1234,11 @@ impl GuiElemTrait for ListSong {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn draw(&mut self, info: &mut DrawInfo, _g: &mut speedy2d::Graphics2D) {
if self.config.redraw {
@ -1224,7 +1247,7 @@ impl GuiElemTrait for ListSong {
if sel != self.sel {
self.sel = sel;
if sel {
self.children.push(GuiElem::new(Panel::with_background(
self.children.push(Box::new(Panel::with_background(
GuiElemCfg::default(),
vec![],
Color::from_rgba(1.0, 1.0, 1.0, 0.2),
@ -1248,11 +1271,11 @@ impl GuiElemTrait for ListSong {
.children()
.nth(3)
.unwrap()
.inner
.children()
.nth(2)
.unwrap()
.try_as()
.any()
.downcast_ref()
.unwrap(),
&gui.database.lock().unwrap(),
);
@ -1275,21 +1298,12 @@ impl GuiElemTrait for ListSong {
let mouse_pos = self.mouse_pos;
let w = self.config.pixel_pos.width();
let h = self.config.pixel_pos.height();
let mut el = GuiElem::new(self.clone());
if self.sel {
vec![]
} else {
vec![GuiAction::SetDragging(Some((
Dragging::Song(self.id),
Some(Box::new(move |i, g| {
let sw = i.pos.width();
let sh = i.pos.height();
let x = (i.mouse_pos.x - mouse_pos.x) / sw;
let y = (i.mouse_pos.y - mouse_pos.y) / sh;
el.inner.config_mut().pos =
Rectangle::from_tuples((x, y), (x + w / sw, y + h / sh));
el.draw(i, g)
})),
None,
)))]
}
} else {
@ -1310,10 +1324,9 @@ impl GuiElemTrait for ListSong {
}
}
#[derive(Clone)]
struct FilterPanel {
config: GuiElemCfg,
children: Vec<GuiElem>,
children: Vec<Box<dyn GuiElemTrait>>,
search_settings_changed: Arc<AtomicBool>,
tab: usize,
new_tab: Arc<AtomicUsize>,
@ -1344,12 +1357,12 @@ impl FilterPanel {
let ssc2 = Arc::clone(&search_settings_changed);
let sel3 = selected.clone();
const VSPLIT: f32 = 0.4;
let tab_main = GuiElem::new(ScrollBox::new(
let tab_main = Box::new(ScrollBox::new(
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.0), (VSPLIT, 1.0))),
crate::gui_base::ScrollBoxSizeUnit::Pixels,
vec![
(
GuiElem::new(Button::new(
Box::new(Button::new(
GuiElemCfg::default(),
move |button| {
let v = !search_is_case_sensitive
@ -1360,7 +1373,8 @@ impl FilterPanel {
.children()
.next()
.unwrap()
.try_as_mut::<Label>()
.any_mut()
.downcast_mut::<Label>()
.unwrap()
.content
.text() = if v {
@ -1370,7 +1384,7 @@ impl FilterPanel {
};
vec![]
},
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
if is_case_sensitive {
FP_CASESENS_Y.to_owned()
@ -1385,7 +1399,7 @@ impl FilterPanel {
1.0,
),
(
GuiElem::new(Button::new(
Box::new(Button::new(
GuiElemCfg::default(),
move |button| {
let v = !search_prefer_start_matches
@ -1397,7 +1411,8 @@ impl FilterPanel {
.children()
.next()
.unwrap()
.try_as_mut::<Label>()
.any_mut()
.downcast_mut::<Label>()
.unwrap()
.content
.text() = if v {
@ -1407,7 +1422,7 @@ impl FilterPanel {
};
vec![]
},
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
if prefer_start_matches {
FP_PREFSTART_Y.to_owned()
@ -1422,13 +1437,13 @@ impl FilterPanel {
1.0,
),
(
GuiElem::new(Button::new(
Box::new(Button::new(
GuiElemCfg::default(),
move |_| {
let mut sel = sel3.clear();
vec![]
},
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
"deselect all".to_owned(),
Color::GRAY,
@ -1439,10 +1454,10 @@ impl FilterPanel {
1.0,
),
(
GuiElem::new(Panel::new(
Box::new(Panel::new(
GuiElemCfg::default(),
vec![
GuiElem::new(Button::new(
Box::new(Button::new(
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.0), (0.5, 1.0))),
{
let dss = do_something_sender.clone();
@ -1451,7 +1466,7 @@ impl FilterPanel {
vec![]
}
},
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
"select all".to_owned(),
Color::GRAY,
@ -1459,7 +1474,7 @@ impl FilterPanel {
Vec2::new(0.5, 0.5),
))],
)),
GuiElem::new(Button::new(
Box::new(Button::new(
GuiElemCfg::at(Rectangle::from_tuples((0.55, 0.0), (0.75, 1.0))),
{
let dss = do_something_sender.clone();
@ -1468,7 +1483,7 @@ impl FilterPanel {
vec![]
}
},
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
"songs".to_owned(),
Color::GRAY,
@ -1476,7 +1491,7 @@ impl FilterPanel {
Vec2::new(0.5, 0.5),
))],
)),
GuiElem::new(Button::new(
Box::new(Button::new(
GuiElemCfg::at(Rectangle::from_tuples((0.8, 0.0), (1.0, 1.0))),
{
let dss = do_something_sender.clone();
@ -1485,7 +1500,7 @@ impl FilterPanel {
vec![]
}
},
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
"albums".to_owned(),
Color::GRAY,
@ -1499,17 +1514,17 @@ impl FilterPanel {
),
],
));
let tab_filters_songs = GuiElem::new(ScrollBox::new(
let tab_filters_songs = Box::new(ScrollBox::new(
GuiElemCfg::at(Rectangle::from_tuples((VSPLIT, 0.0), (1.0, 1.0))),
crate::gui_base::ScrollBoxSizeUnit::Pixels,
vec![],
));
let tab_filters_albums = GuiElem::new(ScrollBox::new(
let tab_filters_albums = Box::new(ScrollBox::new(
GuiElemCfg::at(Rectangle::from_tuples((VSPLIT, 0.0), (1.0, 1.0))).disabled(),
crate::gui_base::ScrollBoxSizeUnit::Pixels,
vec![],
));
let tab_filters_artists = GuiElem::new(ScrollBox::new(
let tab_filters_artists = Box::new(ScrollBox::new(
GuiElemCfg::at(Rectangle::from_tuples((VSPLIT, 0.0), (1.0, 1.0))).disabled(),
crate::gui_base::ScrollBoxSizeUnit::Pixels,
vec![],
@ -1522,16 +1537,16 @@ impl FilterPanel {
Self {
config: GuiElemCfg::default().disabled(),
children: vec![
GuiElem::new(Panel::new(
Box::new(Panel::new(
GuiElemCfg::at(Rectangle::from_tuples((VSPLIT, 0.0), (1.0, HEIGHT))),
vec![
GuiElem::new(Button::new(
Box::new(Button::new(
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.0), (0.33, 1.0))),
move |_| {
set_tab_1.store(0, std::sync::atomic::Ordering::Relaxed);
vec![]
},
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
"Filter Songs".to_owned(),
Color::GRAY,
@ -1539,13 +1554,13 @@ impl FilterPanel {
Vec2::new(0.5, 0.5),
))],
)),
GuiElem::new(Button::new(
Box::new(Button::new(
GuiElemCfg::at(Rectangle::from_tuples((0.33, 0.0), (0.67, 1.0))),
move |_| {
set_tab_2.store(1, std::sync::atomic::Ordering::Relaxed);
vec![]
},
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
"Filter Albums".to_owned(),
Color::GRAY,
@ -1553,13 +1568,13 @@ impl FilterPanel {
Vec2::new(0.5, 0.5),
))],
)),
GuiElem::new(Button::new(
Box::new(Button::new(
GuiElemCfg::at(Rectangle::from_tuples((0.67, 0.0), (1.0, 1.0))),
move |_| {
set_tab_3.store(2, std::sync::atomic::Ordering::Relaxed);
vec![]
},
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
"Filter Artists".to_owned(),
Color::GRAY,
@ -1569,7 +1584,7 @@ impl FilterPanel {
)),
],
)),
GuiElem::new(Panel::new(
Box::new(Panel::new(
GuiElemCfg::at(Rectangle::from_tuples((0.0, HEIGHT), (1.0, 1.0))),
vec![tab_filters_songs, tab_filters_albums, tab_filters_artists],
)),
@ -1589,22 +1604,22 @@ impl FilterPanel {
line_height: f32,
on_change: &Arc<impl Fn(bool) + 'static>,
path: Vec<usize>,
) -> Vec<(GuiElem, f32)> {
) -> Vec<(Box<dyn GuiElemTrait>, f32)> {
let f0 = Arc::clone(filter);
let oc0 = Arc::clone(on_change);
let f1 = Arc::clone(filter);
let f2 = Arc::clone(filter);
let oc1 = Arc::clone(on_change);
let oc2 = Arc::clone(on_change);
let mut children = vec![
GuiElem::new(Button::new(
let mut children: Vec<Box<dyn GuiElemTrait>> = vec![
Box::new(Button::new(
GuiElemCfg::default(),
move |_| {
f0.lock().unwrap().filters.clear();
oc0(true);
vec![]
},
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
"clear filters".to_owned(),
Color::LIGHT_GRAY,
@ -1612,7 +1627,7 @@ impl FilterPanel {
Vec2::new(0.5, 0.5),
))],
)),
GuiElem::new(Button::new(
Box::new(Button::new(
GuiElemCfg::default(),
move |_| {
f1.lock()
@ -1622,7 +1637,7 @@ impl FilterPanel {
oc1(true);
vec![]
},
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
"must have tag".to_owned(),
Color::GRAY,
@ -1630,7 +1645,7 @@ impl FilterPanel {
Vec2::new(0.5, 0.5),
))],
)),
GuiElem::new(Button::new(
Box::new(Button::new(
GuiElemCfg::default(),
move |_| {
f2.lock().unwrap().filters.push(FilterType::TagWithValueInt(
@ -1641,7 +1656,7 @@ impl FilterPanel {
oc2(true);
vec![]
},
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
"tag with integer value between (min) and (max)".to_owned(),
Color::GRAY,
@ -1664,7 +1679,7 @@ impl FilterPanel {
fn build_filter_editor(
filter: &Filter,
mutex: &Arc<Mutex<Filter>>,
children: &mut Vec<GuiElem>,
children: &mut Vec<Box<dyn GuiElemTrait>>,
mut indent: f32,
indent_by: f32,
on_change: &Arc<impl Fn(bool) + 'static>,
@ -1674,7 +1689,7 @@ impl FilterPanel {
let mx = Arc::clone(mutex);
let oc = Arc::clone(on_change);
let p = path.clone();
children.push(GuiElem::new(Button::new(
children.push(Box::new(Button::new(
GuiElemCfg::at(Rectangle::from_tuples((indent, 0.0), (1.0, 1.0))),
move |_| {
if let Some(f) = match mx.lock().unwrap().get_mut(&p) {
@ -1687,7 +1702,7 @@ impl FilterPanel {
}
vec![]
},
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
if filter.and { "AND" } else { "OR" }.to_owned(),
Color::WHITE,
@ -1705,7 +1720,7 @@ impl FilterPanel {
f, mutex, children, indent, indent_by, on_change, path,
),
FilterType::Not(f) => {
children.push(GuiElem::new(Label::new(
children.push(Box::new(Label::new(
GuiElemCfg::at(Rectangle::from_tuples((indent, 0.0), (1.0, 1.0))),
"NOT".to_owned(),
Color::WHITE,
@ -1732,17 +1747,17 @@ impl FilterPanel {
oc(false);
}
}));
children.push(GuiElem::new(Panel::new(
children.push(Box::new(Panel::new(
GuiElemCfg::at(Rectangle::from_tuples((indent, 0.0), (1.0, 1.0))),
vec![
GuiElem::new(Label::new(
Box::new(Label::new(
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.0), (0.1, 1.0))),
"=".to_owned(),
Color::WHITE,
None,
Vec2::new(0.5, 0.5),
)),
GuiElem::new(tf),
Box::new(tf),
],
)));
}
@ -1764,17 +1779,17 @@ impl FilterPanel {
oc(false);
}
}));
children.push(GuiElem::new(Panel::new(
children.push(Box::new(Panel::new(
GuiElemCfg::at(Rectangle::from_tuples((indent, 0.0), (1.0, 1.0))),
vec![
GuiElem::new(Label::new(
Box::new(Label::new(
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.0), (0.1, 1.0))),
">".to_owned(),
Color::WHITE,
None,
Vec2::new(0.5, 0.5),
)),
GuiElem::new(tf),
Box::new(tf),
],
)));
}
@ -1837,19 +1852,19 @@ impl FilterPanel {
}
}
}));
children.push(GuiElem::new(Panel::new(
children.push(Box::new(Panel::new(
GuiElemCfg::at(Rectangle::from_tuples((indent, 0.0), (1.0, 1.0))),
vec![
GuiElem::new(Label::new(
Box::new(Label::new(
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.0), (0.1, 1.0))),
"..".to_owned(),
Color::WHITE,
None,
Vec2::new(0.5, 0.5),
)),
GuiElem::new(tf),
GuiElem::new(tf1),
GuiElem::new(tf2),
Box::new(tf),
Box::new(tf1),
Box::new(tf2),
],
)));
}
@ -1861,12 +1876,15 @@ impl GuiElemTrait for FilterPanel {
fn draw(&mut self, info: &mut DrawInfo, _g: &mut speedy2d::Graphics2D) {
// set line height
if info.line_height != self.line_height {
let sb = self.children[2].try_as_mut::<ScrollBox>().unwrap();
let sb = self.children[2]
.any_mut()
.downcast_mut::<ScrollBox>()
.unwrap();
for (_, h) in &mut sb.children {
*h = info.line_height;
}
for c in &mut self.children[1].inner.children() {
if let Some(sb) = c.try_as_mut::<ScrollBox>() {
for c in &mut self.children[1].children() {
if let Some(sb) = c.any_mut().downcast_mut::<ScrollBox>() {
for (_, h) in &mut sb.children {
*h = info.line_height;
}
@ -1887,42 +1905,40 @@ impl GuiElemTrait for FilterPanel {
new_tab = self.tab;
} else {
self.children[1]
.inner
.children()
.nth(self.tab)
.unwrap()
.inner
.config_mut()
.enabled = false;
self.children[1]
.inner
.children()
.nth(new_tab)
.unwrap()
.inner
.config_mut()
.enabled = true;
*self.children[0]
.inner
.children()
.nth(self.tab)
.unwrap()
.try_as_mut::<Button>()
.any_mut()
.downcast_mut::<Button>()
.unwrap()
.children[0]
.try_as_mut::<Label>()
.any_mut()
.downcast_mut::<Label>()
.unwrap()
.content
.color() = Color::GRAY;
*self.children[0]
.inner
.children()
.nth(new_tab)
.unwrap()
.try_as_mut::<Button>()
.any_mut()
.downcast_mut::<Button>()
.unwrap()
.children[0]
.try_as_mut::<Label>()
.any_mut()
.downcast_mut::<Label>()
.unwrap()
.content
.color() = Color::WHITE;
@ -1934,11 +1950,11 @@ impl GuiElemTrait for FilterPanel {
match new_tab {
0 | 1 | 2 => {
let sb = self.children[1]
.inner
.children()
.nth(new_tab)
.unwrap()
.try_as_mut::<ScrollBox>()
.any_mut()
.downcast_mut::<ScrollBox>()
.unwrap();
let ssc = Arc::clone(&self.search_settings_changed);
let my_tab = new_tab;
@ -1971,8 +1987,8 @@ impl GuiElemTrait for FilterPanel {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -1980,8 +1996,11 @@ impl GuiElemTrait for FilterPanel {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
}
struct Filter {

View File

@ -5,21 +5,21 @@ use std::{
use speedy2d::{color::Color, dimen::Vector2, shape::Rectangle};
use crate::gui::{GuiElem, GuiElemCfg, GuiElemTrait};
use crate::gui::{GuiElemCfg, GuiElemTrait};
/// This should be added on top of overything else and set to fullscreen.
/// It will respond to notification events.
pub struct NotifOverlay {
config: GuiElemCfg,
notifs: Vec<(GuiElem, NotifInfo)>,
notifs: Vec<(Box<dyn GuiElemTrait>, NotifInfo)>,
light: Option<(Instant, Color)>,
receiver: mpsc::Receiver<Box<dyn FnOnce(&Self) -> (GuiElem, NotifInfo) + Send>>,
receiver: mpsc::Receiver<Box<dyn FnOnce(&Self) -> (Box<dyn GuiElemTrait>, NotifInfo) + Send>>,
}
impl NotifOverlay {
pub fn new() -> (
Self,
mpsc::Sender<Box<dyn FnOnce(&Self) -> (GuiElem, NotifInfo) + Send>>,
mpsc::Sender<Box<dyn FnOnce(&Self) -> (Box<dyn GuiElemTrait>, NotifInfo) + Send>>,
) {
let (sender, receiver) = mpsc::channel();
(
@ -46,7 +46,7 @@ impl NotifOverlay {
self.light = Some((now, color));
}
adjust_heights = true;
gui.inner.config_mut().enabled = true;
gui.config_mut().enabled = true;
}
}
NotifInfoTime::FadingIn(since) => {
@ -108,8 +108,7 @@ impl NotifOverlay {
y
};
y += height;
gui.inner.config_mut().pos =
Rectangle::from_tuples((left, pos_y), (right, pos_y + height));
gui.config_mut().pos = Rectangle::from_tuples((left, pos_y), (right, pos_y + height));
}
}
}
@ -157,7 +156,7 @@ impl GuiElemTrait for NotifOverlay {
fn draw(&mut self, info: &mut crate::gui::DrawInfo, g: &mut speedy2d::Graphics2D) {
if let Ok(notif) = self.receiver.try_recv() {
let mut n = notif(self);
n.0.inner.config_mut().enabled = false;
n.0.config_mut().enabled = false;
self.notifs.push(n);
}
self.check_notifs();
@ -194,8 +193,11 @@ impl GuiElemTrait for NotifOverlay {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.notifs.iter_mut().map(|(v, _)| v))
// fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
// Box::new(self.notifs.iter_mut().map(|(v, _)| v))
// }
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.notifs.iter_mut().map(|(v, _)| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -203,7 +205,10 @@ impl GuiElemTrait for NotifOverlay {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
}

View File

@ -7,9 +7,7 @@ use musicdb_lib::{
use speedy2d::{color::Color, dimen::Vec2, shape::Rectangle, window::MouseButton};
use crate::{
gui::{
adjust_area, adjust_pos, morph_rect, GuiAction, GuiCover, GuiElem, GuiElemCfg, GuiElemTrait,
},
gui::{adjust_area, adjust_pos, morph_rect, GuiAction, GuiCover, GuiElemCfg, GuiElemTrait},
gui_text::AdvancedLabel,
};
@ -20,13 +18,12 @@ 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>,
children: Vec<Box<dyn GuiElemTrait>>,
prev_song: Option<SongId>,
cover_pos: Rectangle,
covers: VecDeque<(CoverId, Option<(bool, Instant)>)>,
@ -46,7 +43,7 @@ impl CurrentSong {
let cover_pos_l = Rectangle::from_tuples((0.0, 0.26), (0.4, 0.80));
Self {
config,
children: vec![GuiElem::new(AdvancedLabel::new(
children: vec![Box::new(AdvancedLabel::new(
GuiElemCfg::at(text_pos_s.clone()),
Vec2::new(0.0, 0.5),
vec![],
@ -66,7 +63,10 @@ impl CurrentSong {
pub fn set_idle_mode(&mut self, idle_mode: f32) {
self.idle = idle_mode;
self.idle_changed = true;
let label = self.children[0].try_as_mut::<AdvancedLabel>().unwrap();
let label = self.children[0]
.any_mut()
.downcast_mut::<AdvancedLabel>()
.unwrap();
label.config_mut().pos = morph_rect(&self.text_pos_s, &self.text_pos_l, idle_mode);
label.align = Vec2::new(0.5 * idle_mode, 0.5);
}
@ -96,8 +96,8 @@ impl GuiElemTrait for CurrentSong {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -105,8 +105,11 @@ impl GuiElemTrait for CurrentSong {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn draw(&mut self, info: &mut crate::gui::DrawInfo, g: &mut speedy2d::Graphics2D) {
// check if there is a new song
@ -157,7 +160,8 @@ impl GuiElemTrait for CurrentSong {
if self.config.redraw {
self.config.redraw = false;
self.children[0]
.try_as_mut::<AdvancedLabel>()
.any_mut()
.downcast_mut::<AdvancedLabel>()
.unwrap()
.content = if let Some(song) = new_song {
self.text_updated = Some(Instant::now());
@ -179,7 +183,8 @@ impl GuiElemTrait for CurrentSong {
self.text_updated = None;
}
for c in self.children[0]
.try_as_mut::<AdvancedLabel>()
.any_mut()
.downcast_mut::<AdvancedLabel>()
.unwrap()
.content
.iter_mut()
@ -312,10 +317,9 @@ impl GuiElemTrait for CurrentSong {
}
}
#[derive(Clone)]
pub struct PlayPauseToggle {
config: GuiElemCfg,
children: Vec<GuiElem>,
children: Vec<Box<dyn GuiElemTrait>>,
playing_target: bool,
playing_waiting_for_change: bool,
}
@ -337,8 +341,8 @@ impl GuiElemTrait for PlayPauseToggle {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -346,8 +350,11 @@ impl GuiElemTrait for PlayPauseToggle {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn draw(&mut self, info: &mut crate::gui::DrawInfo, g: &mut speedy2d::Graphics2D) {
if self.playing_waiting_for_change {

View File

@ -17,7 +17,7 @@ use speedy2d::{
};
use crate::{
gui::{Dragging, DrawInfo, GuiAction, GuiElem, GuiElemCfg, GuiElemTrait},
gui::{Dragging, DrawInfo, GuiAction, GuiElemCfg, GuiElemTrait},
gui_base::{Panel, ScrollBox},
gui_text::{self, AdvancedLabel, Label},
};
@ -31,10 +31,9 @@ because simple clicks have to be GoTo events.
*/
#[derive(Clone)]
pub struct QueueViewer {
config: GuiElemCfg,
children: Vec<GuiElem>,
children: Vec<Box<dyn GuiElemTrait>>,
queue_updated: bool,
}
const QP_QUEUE1: f32 = 0.0;
@ -46,11 +45,11 @@ impl QueueViewer {
Self {
config,
children: vec![
GuiElem::new(ScrollBox::new(
Box::new(ScrollBox::new(
GuiElemCfg::at(Rectangle::from_tuples((0.0, QP_QUEUE1), (1.0, QP_QUEUE2))),
crate::gui_base::ScrollBoxSizeUnit::Pixels,
vec![(
GuiElem::new(Label::new(
Box::new(Label::new(
GuiElemCfg::default(),
"loading...".to_string(),
Color::DARK_GRAY,
@ -60,13 +59,13 @@ impl QueueViewer {
1.0,
)],
)),
GuiElem::new(QueueEmptySpaceDragHandler::new(GuiElemCfg::at(
Box::new(QueueEmptySpaceDragHandler::new(GuiElemCfg::at(
Rectangle::from_tuples((0.0, QP_QUEUE1), (1.0, QP_QUEUE2)),
))),
GuiElem::new(Panel::new(
Box::new(Panel::new(
GuiElemCfg::at(Rectangle::from_tuples((0.0, QP_INV1), (0.5, QP_INV2))),
vec![
GuiElem::new(
Box::new(
QueueLoop::new(
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.0), (0.5, 0.5)))
.w_mouse(),
@ -84,7 +83,7 @@ impl QueueViewer {
)
.alwayscopy(),
),
GuiElem::new(
Box::new(
QueueLoop::new(
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.5), (0.5, 1.0)))
.w_mouse(),
@ -102,7 +101,7 @@ impl QueueViewer {
)
.alwayscopy(),
),
GuiElem::new(
Box::new(
QueueRandom::new(
GuiElemCfg::at(Rectangle::from_tuples((0.5, 0.0), (1.0, 0.5)))
.w_mouse(),
@ -112,7 +111,7 @@ impl QueueViewer {
)
.alwayscopy(),
),
GuiElem::new(
Box::new(
QueueShuffle::new(
GuiElemCfg::at(Rectangle::from_tuples((0.5, 0.5), (1.0, 1.0)))
.w_mouse(),
@ -130,7 +129,7 @@ impl QueueViewer {
),
],
)),
GuiElem::new(AdvancedLabel::new(
Box::new(AdvancedLabel::new(
GuiElemCfg::at(Rectangle::from_tuples((0.5, QP_INV1), (1.0, QP_INV2))),
Vec2::new(0.0, 0.5),
vec![],
@ -147,8 +146,8 @@ impl GuiElemTrait for QueueViewer {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -156,14 +155,16 @@ impl GuiElemTrait for QueueViewer {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn draw(&mut self, info: &mut DrawInfo, _g: &mut speedy2d::Graphics2D) {
if self.queue_updated {
self.queue_updated = false;
let label = self.children[3]
.inner
.any_mut()
.downcast_mut::<AdvancedLabel>()
.unwrap();
@ -226,7 +227,10 @@ impl GuiElemTrait for QueueViewer {
true,
true,
);
let mut scroll_box = self.children[0].try_as_mut::<ScrollBox>().unwrap();
let mut scroll_box = self.children[0]
.any_mut()
.downcast_mut::<ScrollBox>()
.unwrap();
scroll_box.children = c;
scroll_box.config_mut().redraw = true;
}
@ -243,7 +247,7 @@ fn queue_gui(
depth: f32,
depth_inc_by: f32,
line_height: f32,
target: &mut Vec<(GuiElem, f32)>,
target: &mut Vec<(Box<dyn GuiElemTrait>, f32)>,
path: Vec<usize>,
current: bool,
skip_folder: bool,
@ -253,7 +257,7 @@ fn queue_gui(
QueueContent::Song(id) => {
if let Some(s) = db.songs().get(id) {
target.push((
GuiElem::new(QueueSong::new(
Box::new(QueueSong::new(
cfg,
path,
s.clone(),
@ -268,7 +272,7 @@ fn queue_gui(
QueueContent::Folder(ia, q, _) => {
if !skip_folder {
target.push((
GuiElem::new(QueueFolder::new(
Box::new(QueueFolder::new(
cfg.clone(),
path.clone(),
queue.clone(),
@ -296,7 +300,7 @@ fn queue_gui(
let mut p1 = path;
let p2 = p1.pop().unwrap_or(0) + 1;
target.push((
GuiElem::new(QueueIndentEnd::new(cfg, (p1, p2))),
Box::new(QueueIndentEnd::new(cfg, (p1, p2))),
line_height * 0.4,
));
}
@ -307,7 +311,7 @@ fn queue_gui(
let mut p1 = path.clone();
let p2 = p1.pop().unwrap_or(0) + 1;
target.push((
GuiElem::new(QueueLoop::new(cfg.clone(), path, queue.clone(), current)),
Box::new(QueueLoop::new(cfg.clone(), path, queue.clone(), current)),
line_height * 0.8,
));
queue_gui(
@ -322,13 +326,13 @@ fn queue_gui(
true,
);
target.push((
GuiElem::new(QueueIndentEnd::new(cfg, (p1, p2))),
Box::new(QueueIndentEnd::new(cfg, (p1, p2))),
line_height * 0.4,
));
}
QueueContent::Random(q) => {
target.push((
GuiElem::new(QueueRandom::new(
Box::new(QueueRandom::new(
cfg.clone(),
path.clone(),
queue.clone(),
@ -354,13 +358,13 @@ fn queue_gui(
let mut p1 = path.clone();
let p2 = p1.pop().unwrap_or(0) + 1;
target.push((
GuiElem::new(QueueIndentEnd::new(cfg, (p1, p2))),
Box::new(QueueIndentEnd::new(cfg, (p1, p2))),
line_height * 0.4,
));
}
QueueContent::Shuffle { inner, state: _ } => {
target.push((
GuiElem::new(QueueShuffle::new(
Box::new(QueueShuffle::new(
cfg.clone(),
path.clone(),
queue.clone(),
@ -384,17 +388,16 @@ fn queue_gui(
let mut p1 = path.clone();
let p2 = p1.pop().unwrap_or(0) + 1;
target.push((
GuiElem::new(QueueIndentEnd::new(cfg, (p1, p2))),
Box::new(QueueIndentEnd::new(cfg, (p1, p2))),
line_height * 0.4,
));
}
}
}
#[derive(Clone)]
struct QueueEmptySpaceDragHandler {
config: GuiElemCfg,
children: Vec<GuiElem>,
children: Vec<Box<dyn GuiElemTrait>>,
}
impl QueueEmptySpaceDragHandler {
pub fn new(config: GuiElemCfg) -> Self {
@ -411,8 +414,8 @@ impl GuiElemTrait for QueueEmptySpaceDragHandler {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -420,8 +423,11 @@ impl GuiElemTrait for QueueEmptySpaceDragHandler {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn dragged(&mut self, dragged: Dragging) -> Vec<GuiAction> {
dragged_add_to_queue(dragged, |q, _| Command::QueueAdd(vec![], q))
@ -446,10 +452,9 @@ fn generic_queue_draw(
}
}
#[derive(Clone)]
struct QueueSong {
config: GuiElemCfg,
children: Vec<GuiElem>,
children: Vec<Box<dyn GuiElemTrait>>,
path: Vec<usize>,
song: Song,
current: bool,
@ -472,7 +477,7 @@ impl QueueSong {
Self {
config: config.w_mouse().w_keyboard_watch().w_drag_target(),
children: vec![
GuiElem::new(AdvancedLabel::new(
Box::new(AdvancedLabel::new(
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.0), (1.0, 0.57))),
Vec2::new(0.0, 0.5),
vec![vec![
@ -505,7 +510,7 @@ impl QueueSong {
),
]],
)),
GuiElem::new(Label::new(
Box::new(Label::new(
GuiElemCfg::at(Rectangle::from_tuples((sub_offset, 0.57), (1.0, 1.0))),
match (
db.artists().get(&song.artist),
@ -558,8 +563,8 @@ impl GuiElemTrait for QueueSong {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -567,8 +572,11 @@ impl GuiElemTrait for QueueSong {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn mouse_down(&mut self, button: MouseButton) -> Vec<GuiAction> {
if button == MouseButton::Left {
@ -625,18 +633,9 @@ impl GuiElemTrait for QueueSong {
let mouse_pos = self.mouse_pos;
let w = self.config.pixel_pos.width();
let h = self.config.pixel_pos.height();
let mut el = GuiElem::new(self.clone());
info.actions.push(GuiAction::SetDragging(Some((
Dragging::Queue(QueueContent::Song(self.song.id).into()),
Some(Box::new(move |i, g| {
let sw = i.pos.width();
let sh = i.pos.height();
let x = (i.mouse_pos.x - mouse_pos.x) / sw;
let y = (i.mouse_pos.y - mouse_pos.y) / sh;
el.inner.config_mut().pos =
Rectangle::from_tuples((x, y), (x + w / sw, y + h / sh));
el.draw(i, g)
})),
None,
))));
}
}
@ -667,10 +666,9 @@ impl GuiElemTrait for QueueSong {
}
}
#[derive(Clone)]
struct QueueFolder {
config: GuiElemCfg,
children: Vec<GuiElem>,
children: Vec<Box<dyn GuiElemTrait>>,
path: Vec<usize>,
queue: Queue,
current: bool,
@ -690,7 +688,7 @@ impl QueueFolder {
config.w_mouse().w_keyboard_watch()
}
.w_drag_target(),
children: vec![GuiElem::new(Label::new(
children: vec![Box::new(Label::new(
GuiElemCfg::default(),
match queue.content() {
QueueContent::Folder(_, q, n) => format!(
@ -732,8 +730,8 @@ impl GuiElemTrait for QueueFolder {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -741,8 +739,11 @@ impl GuiElemTrait for QueueFolder {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn draw(&mut self, info: &mut DrawInfo, g: &mut speedy2d::Graphics2D) {
self.insert_into = info.mouse_pos.y > info.pos.top_left().y + info.pos.height() * 0.5;
@ -778,18 +779,9 @@ impl GuiElemTrait for QueueFolder {
let mouse_pos = self.mouse_pos;
let w = self.config.pixel_pos.width();
let h = self.config.pixel_pos.height();
let mut el = GuiElem::new(self.clone());
info.actions.push(GuiAction::SetDragging(Some((
Dragging::Queue(self.queue.clone()),
Some(Box::new(move |i, g| {
let sw = i.pos.width();
let sh = i.pos.height();
let x = (i.mouse_pos.x - mouse_pos.x) / sw;
let y = (i.mouse_pos.y - mouse_pos.y) / sh;
el.inner.config_mut().pos =
Rectangle::from_tuples((x, y), (x + w / sw, y + h / sh));
el.draw(i, g)
})),
None,
))));
}
}
@ -841,10 +833,9 @@ impl GuiElemTrait for QueueFolder {
}
}
}
#[derive(Clone)]
pub struct QueueIndentEnd {
config: GuiElemCfg,
children: Vec<GuiElem>,
children: Vec<Box<dyn GuiElemTrait>>,
path_insert: (Vec<usize>, usize),
}
impl QueueIndentEnd {
@ -863,8 +854,8 @@ impl GuiElemTrait for QueueIndentEnd {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -872,8 +863,11 @@ impl GuiElemTrait for QueueIndentEnd {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn draw(&mut self, info: &mut DrawInfo, g: &mut speedy2d::Graphics2D) {
if info.dragging.is_some() {
@ -900,10 +894,9 @@ impl GuiElemTrait for QueueIndentEnd {
}
}
#[derive(Clone)]
struct QueueLoop {
config: GuiElemCfg,
children: Vec<GuiElem>,
children: Vec<Box<dyn GuiElemTrait>>,
path: Vec<usize>,
queue: Queue,
current: bool,
@ -922,7 +915,7 @@ impl QueueLoop {
config.w_mouse().w_keyboard_watch()
}
.w_drag_target(),
children: vec![GuiElem::new(Label::new(
children: vec![Box::new(Label::new(
GuiElemCfg::default(),
Self::get_label_text(&queue),
Color::from_int_rgb(217, 197, 65),
@ -967,8 +960,8 @@ impl GuiElemTrait for QueueLoop {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -976,8 +969,11 @@ impl GuiElemTrait for QueueLoop {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn mouse_wheel(&mut self, diff: f32) -> Vec<GuiAction> {
if self.always_copy {
@ -989,7 +985,6 @@ impl GuiElemTrait for QueueLoop {
}
}
*self.children[0]
.inner
.any_mut()
.downcast_mut::<Label>()
.unwrap()
@ -1009,18 +1004,9 @@ impl GuiElemTrait for QueueLoop {
let mouse_pos = self.mouse_pos;
let w = self.config.pixel_pos.width();
let h = self.config.pixel_pos.height();
let mut el = GuiElem::new(self.clone());
info.actions.push(GuiAction::SetDragging(Some((
Dragging::Queue(self.queue.clone()),
Some(Box::new(move |i, g| {
let sw = i.pos.width();
let sh = i.pos.height();
let x = (i.mouse_pos.x - mouse_pos.x) / sw;
let y = (i.mouse_pos.y - mouse_pos.y) / sh;
el.inner.config_mut().pos =
Rectangle::from_tuples((x, y), (x + w / sw, y + h / sh));
el.draw(i, g)
})),
None,
))));
}
}
@ -1066,10 +1052,9 @@ impl GuiElemTrait for QueueLoop {
}
}
#[derive(Clone)]
struct QueueRandom {
config: GuiElemCfg,
children: Vec<GuiElem>,
children: Vec<Box<dyn GuiElemTrait>>,
path: Vec<usize>,
queue: Queue,
current: bool,
@ -1088,7 +1073,7 @@ impl QueueRandom {
config.w_mouse().w_keyboard_watch()
}
.w_drag_target(),
children: vec![GuiElem::new(Label::new(
children: vec![Box::new(Label::new(
GuiElemCfg::default(),
match queue.content() {
QueueContent::Random(_) => {
@ -1123,8 +1108,8 @@ impl GuiElemTrait for QueueRandom {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -1132,8 +1117,11 @@ impl GuiElemTrait for QueueRandom {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn draw(&mut self, info: &mut DrawInfo, _g: &mut speedy2d::Graphics2D) {
if !self.mouse {
@ -1146,18 +1134,9 @@ impl GuiElemTrait for QueueRandom {
let mouse_pos = self.mouse_pos;
let w = self.config.pixel_pos.width();
let h = self.config.pixel_pos.height();
let mut el = GuiElem::new(self.clone());
info.actions.push(GuiAction::SetDragging(Some((
Dragging::Queue(self.queue.clone()),
Some(Box::new(move |i, g| {
let sw = i.pos.width();
let sh = i.pos.height();
let x = (i.mouse_pos.x - mouse_pos.x) / sw;
let y = (i.mouse_pos.y - mouse_pos.y) / sh;
el.inner.config_mut().pos =
Rectangle::from_tuples((x, y), (x + w / sw, y + h / sh));
el.draw(i, g)
})),
None,
))));
}
}
@ -1202,10 +1181,9 @@ impl GuiElemTrait for QueueRandom {
}
}
#[derive(Clone)]
struct QueueShuffle {
config: GuiElemCfg,
children: Vec<GuiElem>,
children: Vec<Box<dyn GuiElemTrait>>,
path: Vec<usize>,
queue: Queue,
current: bool,
@ -1224,7 +1202,7 @@ impl QueueShuffle {
config.w_mouse().w_keyboard_watch()
}
.w_drag_target(),
children: vec![GuiElem::new(Label::new(
children: vec![Box::new(Label::new(
GuiElemCfg::default(),
match queue.content() {
QueueContent::Shuffle { .. } => {
@ -1259,8 +1237,8 @@ impl GuiElemTrait for QueueShuffle {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -1268,8 +1246,11 @@ impl GuiElemTrait for QueueShuffle {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn draw(&mut self, info: &mut DrawInfo, _g: &mut speedy2d::Graphics2D) {
if !self.mouse {
@ -1282,18 +1263,9 @@ impl GuiElemTrait for QueueShuffle {
let mouse_pos = self.mouse_pos;
let w = self.config.pixel_pos.width();
let h = self.config.pixel_pos.height();
let mut el = GuiElem::new(self.clone());
info.actions.push(GuiAction::SetDragging(Some((
Dragging::Queue(self.queue.clone()),
Some(Box::new(move |i, g| {
let sw = i.pos.width();
let sh = i.pos.height();
let x = (i.mouse_pos.x - mouse_pos.x) / sw;
let y = (i.mouse_pos.y - mouse_pos.y) / sh;
el.inner.config_mut().pos =
Rectangle::from_tuples((x, y), (x + w / sw, y + h / sh));
el.draw(i, g)
})),
None,
))));
}
}

View File

@ -1,16 +1,10 @@
use std::time::Instant;
use musicdb_lib::{data::queue::QueueContent, server::Command};
use speedy2d::{
color::Color,
dimen::Vec2,
shape::Rectangle,
window::{KeyScancode, VirtualKeyCode},
Graphics2D,
};
use speedy2d::{color::Color, dimen::Vec2, shape::Rectangle, window::VirtualKeyCode, Graphics2D};
use crate::{
gui::{morph_rect, DrawInfo, GuiAction, GuiElem, GuiElemCfg, GuiElemTrait},
gui::{morph_rect, DrawInfo, GuiAction, GuiElemCfg, GuiElemTrait},
gui_base::{Button, Panel},
gui_library::LibraryBrowser,
gui_notif::NotifOverlay,
@ -38,7 +32,6 @@ pub fn transition(p: f32) -> f32 {
3.0 * p * p - 2.0 * p * p * p
}
#[derive(Clone)]
pub struct GuiScreen {
config: GuiElemCfg,
/// 0: Notifications
@ -51,7 +44,7 @@ pub struct GuiScreen {
/// 3: queue
/// 4: queue clear button
/// 4: Edit Panel
children: Vec<GuiElem>,
children: Vec<Box<dyn GuiElemTrait>>,
pub idle: (bool, Option<Instant>),
pub settings: (bool, Option<Instant>),
pub edit_panel: (bool, Option<Instant>),
@ -60,22 +53,22 @@ pub struct GuiScreen {
pub prev_mouse_pos: Vec2,
}
impl GuiScreen {
pub fn open_edit(&mut self, mut edit: GuiElem) {
pub fn open_edit(&mut self, mut edit: Box<dyn GuiElemTrait>) {
if !self.edit_panel.0 {
self.edit_panel = (true, Some(Instant::now()));
edit.inner.config_mut().pos = Rectangle::from_tuples((-0.5, 0.0), (0.0, 0.9));
edit.config_mut().pos = Rectangle::from_tuples((-0.5, 0.0), (0.0, 0.9));
} else {
edit.inner.config_mut().pos = Rectangle::from_tuples((0.0, 0.0), (0.5, 0.9));
edit.config_mut().pos = Rectangle::from_tuples((0.0, 0.0), (0.5, 0.9));
}
if let Some(prev) = self.children.get_mut(4) {
prev.inner.config_mut().enabled = false;
prev.config_mut().enabled = false;
}
self.children.insert(4, edit);
}
pub fn close_edit(&mut self) {
if self.children.len() > 5 {
self.children.remove(4);
self.children[4].inner.config_mut().enabled = true;
self.children[4].config_mut().enabled = true;
} else if self.edit_panel.0 {
self.edit_panel = (false, Some(Instant::now()));
}
@ -91,25 +84,25 @@ impl GuiScreen {
Self {
config: config.w_keyboard_watch().w_mouse().w_keyboard_focus(),
children: vec![
GuiElem::new(notif_overlay),
GuiElem::new(StatusBar::new(
Box::new(notif_overlay),
Box::new(StatusBar::new(
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.9), (1.0, 1.0))),
true,
)),
GuiElem::new(Settings::new(
Box::new(Settings::new(
GuiElemCfg::default().disabled(),
line_height,
scroll_sensitivity_pixels,
scroll_sensitivity_lines,
scroll_sensitivity_pages,
)),
GuiElem::new(Panel::new(
Box::new(Panel::new(
GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.0), (1.0, 0.9))),
vec![
GuiElem::new(Button::new(
Box::new(Button::new(
GuiElemCfg::at(Rectangle::from_tuples((0.75, 0.0), (0.875, 0.03))),
|_| vec![GuiAction::OpenSettings(true)],
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
"Settings".to_string(),
Color::WHITE,
@ -117,10 +110,10 @@ impl GuiScreen {
Vec2::new(0.5, 0.5),
))],
)),
GuiElem::new(Button::new(
Box::new(Button::new(
GuiElemCfg::at(Rectangle::from_tuples((0.875, 0.0), (1.0, 0.03))),
|_| vec![GuiAction::Exit],
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
"Exit".to_string(),
Color::WHITE,
@ -128,15 +121,15 @@ impl GuiScreen {
Vec2::new(0.5, 0.5),
))],
)),
GuiElem::new(LibraryBrowser::new(GuiElemCfg::at(Rectangle::from_tuples(
Box::new(LibraryBrowser::new(GuiElemCfg::at(Rectangle::from_tuples(
(0.0, 0.0),
(0.5, 1.0),
)))),
GuiElem::new(QueueViewer::new(GuiElemCfg::at(Rectangle::from_tuples(
Box::new(QueueViewer::new(GuiElemCfg::at(Rectangle::from_tuples(
(0.5, 0.03),
(1.0, 1.0),
)))),
GuiElem::new(Button::new(
Box::new(Button::new(
GuiElemCfg::at(Rectangle::from_tuples((0.5, 0.0), (0.75, 0.03))),
|_| {
vec![GuiAction::SendToServer(
@ -151,7 +144,7 @@ impl GuiScreen {
),
)]
},
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
"Clear Queue".to_string(),
Color::WHITE,
@ -216,8 +209,8 @@ impl GuiElemTrait for GuiScreen {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -225,8 +218,11 @@ impl GuiElemTrait for GuiScreen {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn key_watch(
&mut self,
@ -271,21 +267,20 @@ impl GuiElemTrait for GuiScreen {
if let Some(h) = &info.helper {
h.set_cursor_visible(!self.idle.0);
if self.settings.0 {
self.children[2].inner.config_mut().enabled = !self.idle.0;
self.children[2].config_mut().enabled = !self.idle.0;
}
if self.edit_panel.0 {
if let Some(c) = self.children.get_mut(4) {
c.inner.config_mut().enabled = !self.idle.0;
c.config_mut().enabled = !self.idle.0;
}
}
self.children[3].inner.config_mut().enabled = !self.idle.0;
self.children[3].config_mut().enabled = !self.idle.0;
}
}
let p = transition(p1);
self.children[1].inner.config_mut().pos =
self.children[1].config_mut().pos =
Rectangle::from_tuples((0.0, 0.9 - 0.9 * p), (1.0, 1.0));
self.children[1]
.inner
.any_mut()
.downcast_mut::<StatusBar>()
.unwrap()
@ -295,7 +290,7 @@ impl GuiElemTrait for GuiScreen {
if self.settings.1.is_some() {
let p1 = Self::get_prog(&mut self.settings, 0.3);
let p = transition(p1);
let cfg = self.children[2].inner.config_mut();
let cfg = self.children[2].config_mut();
cfg.enabled = p > 0.0;
cfg.pos = Rectangle::from_tuples((0.0, 0.9 - 0.9 * p), (1.0, 0.9));
}
@ -304,22 +299,20 @@ impl GuiElemTrait for GuiScreen {
let p1 = Self::get_prog(&mut self.edit_panel, 0.3);
let p = transition(p1);
if let Some(c) = self.children.get_mut(4) {
c.inner.config_mut().enabled = p > 0.0;
c.inner.config_mut().pos =
Rectangle::from_tuples((-0.5 + 0.5 * p, 0.0), (0.5 * p, 0.9));
c.config_mut().enabled = p > 0.0;
c.config_mut().pos = Rectangle::from_tuples((-0.5 + 0.5 * p, 0.0), (0.5 * p, 0.9));
}
if !self.edit_panel.0 && p == 0.0 {
while self.children.len() > 4 {
self.children.pop();
}
}
self.children[3].inner.config_mut().pos =
self.children[3].config_mut().pos =
Rectangle::from_tuples((0.5 * p, 0.0), (1.0 + 0.5 * p, 0.9));
}
// set idle timeout (only when settings are open)
if self.settings.0 || self.settings.1.is_some() {
self.idle_timeout = self.children[2]
.inner
.any()
.downcast_ref::<Settings>()
.unwrap()
@ -352,10 +345,9 @@ impl GuiElemTrait for GuiScreen {
}
}
#[derive(Clone)]
pub struct StatusBar {
config: GuiElemCfg,
children: Vec<GuiElem>,
children: Vec<Box<dyn GuiElemTrait>>,
idle_mode: f32,
idle_prev: f32,
pos_current_song_s: Rectangle,
@ -372,12 +364,12 @@ impl StatusBar {
Self {
config,
children: vec![
GuiElem::new(CurrentSong::new(GuiElemCfg::at(pos_current_song_s.clone()))),
GuiElem::new(PlayPauseToggle::new(
Box::new(CurrentSong::new(GuiElemCfg::at(pos_current_song_s.clone()))),
Box::new(PlayPauseToggle::new(
GuiElemCfg::at(pos_play_pause_s.clone()),
playing,
)),
GuiElem::new(Panel::new(GuiElemCfg::default(), vec![])),
Box::new(Panel::new(GuiElemCfg::default(), vec![])),
],
idle_mode: 0.0,
idle_prev: 0.0,
@ -398,7 +390,6 @@ impl StatusBar {
}
pub fn set_background(&mut self, bg: Option<Color>) {
self.children[Self::index_bgpanel()]
.inner
.any_mut()
.downcast_mut::<Panel>()
.unwrap()
@ -412,8 +403,8 @@ impl GuiElemTrait for StatusBar {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -421,8 +412,11 @@ impl GuiElemTrait for StatusBar {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn draw(&mut self, info: &mut DrawInfo, g: &mut Graphics2D) {
// the line that separates this section from the rest of the ui.
@ -446,7 +440,6 @@ impl GuiElemTrait for StatusBar {
// position the text
let l = self.idle_mode;
let current_song = self.children[Self::index_current_song()]
.inner
.any_mut()
.downcast_mut::<CurrentSong>()
.unwrap();
@ -454,7 +447,6 @@ impl GuiElemTrait for StatusBar {
current_song.config_mut().pos =
morph_rect(&self.pos_current_song_s, &self.pos_current_song_l, l);
let play_pause = self.children[Self::index_play_pause_toggle()]
.inner
.any_mut()
.downcast_mut::<PlayPauseToggle>()
.unwrap();

View File

@ -1,17 +1,14 @@
use std::{ops::DerefMut, time::Duration};
use speedy2d::{color::Color, dimen::Vec2, shape::Rectangle, Graphics2D};
use crate::{
gui::{DrawInfo, GuiAction, GuiElem, GuiElemCfg, GuiElemTrait},
gui::{DrawInfo, GuiAction, GuiElemCfg, GuiElemTrait},
gui_base::{Button, Panel, ScrollBox, Slider},
gui_text::Label,
};
#[derive(Clone)]
pub struct Settings {
pub config: GuiElemCfg,
pub children: Vec<GuiElem>,
pub children: Vec<Box<dyn GuiElemTrait>>,
}
impl Settings {
pub fn new(
@ -25,15 +22,15 @@ impl Settings {
Self {
config,
children: vec![
GuiElem::new(ScrollBox::new(
Box::new(ScrollBox::new(
GuiElemCfg::default(),
crate::gui_base::ScrollBoxSizeUnit::Pixels,
vec![
(
GuiElem::new(Button::new(
Box::new(Button::new(
GuiElemCfg::at(Rectangle::from_tuples((0.75, 0.0), (1.0, 1.0))),
|btn| vec![GuiAction::OpenSettings(false)],
vec![GuiElem::new(Label::new(
vec![Box::new(Label::new(
GuiElemCfg::default(),
"Back".to_string(),
Color::WHITE,
@ -44,10 +41,10 @@ impl Settings {
0.0,
),
(
GuiElem::new(Panel::new(
Box::new(Panel::new(
GuiElemCfg::default(),
vec![
GuiElem::new(Label::new(
Box::new(Label::new(
GuiElemCfg::at(Rectangle::from_tuples(
(0.0, 0.0),
(0.33, 1.0),
@ -57,7 +54,7 @@ impl Settings {
None,
Vec2::new(0.9, 0.5),
)),
GuiElem::new({
Box::new({
let mut s = Slider::new_labeled(
GuiElemCfg::at(Rectangle::from_tuples(
(0.33, 0.0),
@ -81,10 +78,10 @@ impl Settings {
0.0,
),
(
GuiElem::new(Panel::new(
Box::new(Panel::new(
GuiElemCfg::default(),
vec![
GuiElem::new(Label::new(
Box::new(Label::new(
GuiElemCfg::at(Rectangle::from_tuples(
(0.0, 0.0),
(0.33, 1.0),
@ -94,7 +91,7 @@ impl Settings {
None,
Vec2::new(0.9, 0.5),
)),
GuiElem::new(Slider::new_labeled(
Box::new(Slider::new_labeled(
GuiElemCfg::at(Rectangle::from_tuples(
(0.33, 0.0),
(1.0, 1.0),
@ -116,10 +113,10 @@ impl Settings {
0.0,
),
(
GuiElem::new(Panel::new(
Box::new(Panel::new(
GuiElemCfg::default(),
vec![
GuiElem::new(Label::new(
Box::new(Label::new(
GuiElemCfg::at(Rectangle::from_tuples(
(0.0, 0.0),
(0.33, 1.0),
@ -129,7 +126,7 @@ impl Settings {
None,
Vec2::new(0.9, 0.5),
)),
GuiElem::new(Slider::new_labeled(
Box::new(Slider::new_labeled(
GuiElemCfg::at(Rectangle::from_tuples(
(0.33, 0.0),
(1.0, 1.0),
@ -153,10 +150,10 @@ impl Settings {
0.0,
),
(
GuiElem::new(Panel::new(
Box::new(Panel::new(
GuiElemCfg::default(),
vec![
GuiElem::new(Label::new(
Box::new(Label::new(
GuiElemCfg::at(Rectangle::from_tuples(
(0.0, 0.0),
(0.33, 1.0),
@ -166,7 +163,7 @@ impl Settings {
None,
Vec2::new(0.9, 0.5),
)),
GuiElem::new(Slider::new_labeled(
Box::new(Slider::new_labeled(
GuiElemCfg::at(Rectangle::from_tuples(
(0.33, 0.0),
(1.0, 1.0),
@ -219,7 +216,7 @@ impl Settings {
),
],
)),
GuiElem::new(Panel::with_background(
Box::new(Panel::with_background(
GuiElemCfg::default().w_mouse(),
vec![],
Color::BLACK,
@ -229,18 +226,15 @@ impl Settings {
}
pub fn get_timeout_val(&self) -> Option<f64> {
let v = self.children[0]
.inner
.any()
.downcast_ref::<ScrollBox>()
.unwrap()
.children[4]
.0
.inner
.any()
.downcast_ref::<Panel>()
.unwrap()
.children[1]
.inner
.any()
.downcast_ref::<Slider>()
.unwrap()
@ -259,11 +253,8 @@ impl GuiElemTrait for Settings {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -271,24 +262,27 @@ impl GuiElemTrait for Settings {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn draw(&mut self, info: &mut DrawInfo, _g: &mut Graphics2D) {
let (rest, background) = self.children.split_at_mut(1);
let scrollbox = rest[0].inner.any_mut().downcast_mut::<ScrollBox>().unwrap();
let scrollbox = rest[0].any_mut().downcast_mut::<ScrollBox>().unwrap();
let settings_opacity_slider = scrollbox.children[1]
.0
.inner
.any_mut()
.downcast_mut::<Panel>()
.unwrap()
.children[1]
.inner
.any_mut()
.downcast_mut::<Slider>()
.unwrap();
if settings_opacity_slider.val_changed_subs[0] {
settings_opacity_slider.val_changed_subs[0] = false;
let color = background[0]
.inner
.any_mut()
.downcast_mut::<Panel>()
.unwrap()

View File

@ -8,7 +8,7 @@ use speedy2d::{
window::{ModifiersState, MouseButton},
};
use crate::gui::{GuiAction, GuiElem, GuiElemCfg, GuiElemTrait};
use crate::gui::{GuiAction, GuiElemCfg, GuiElemTrait};
/*
@ -17,10 +17,9 @@ except they are all text-related.
*/
#[derive(Clone)]
pub struct Label {
config: GuiElemCfg,
children: Vec<GuiElem>,
children: Vec<Box<dyn GuiElemTrait>>,
pub content: Content,
pub pos: Vec2,
}
@ -91,8 +90,8 @@ impl GuiElemTrait for Label {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -100,8 +99,11 @@ impl GuiElemTrait for Label {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn draw(&mut self, info: &mut crate::gui::DrawInfo, g: &mut speedy2d::Graphics2D) {
if self.config.pixel_pos.size() != info.pos.size() {
@ -144,7 +146,7 @@ impl GuiElemTrait for Label {
/// a single-line text field for users to type text into.
pub struct TextField {
config: GuiElemCfg,
pub children: Vec<GuiElem>,
pub children: Vec<Box<dyn GuiElemTrait>>,
pub on_changed: Option<Box<dyn FnMut(&str)>>,
pub on_changed_mut: Option<Box<dyn FnMut(&mut Self, String)>>,
}
@ -163,14 +165,14 @@ impl TextField {
Self {
config: config.w_mouse().w_keyboard_focus(),
children: vec![
GuiElem::new(Label::new(
Box::new(Label::new(
GuiElemCfg::default(),
text,
color_input,
None,
Vec2::new(0.0, 0.5),
)),
GuiElem::new(Label::new(
Box::new(Label::new(
if text_is_empty {
GuiElemCfg::default()
} else {
@ -187,16 +189,16 @@ impl TextField {
}
}
pub fn label_input(&self) -> &Label {
self.children[0].inner.any().downcast_ref().unwrap()
self.children[0].any().downcast_ref().unwrap()
}
pub fn label_input_mut(&mut self) -> &mut Label {
self.children[0].inner.any_mut().downcast_mut().unwrap()
self.children[0].any_mut().downcast_mut().unwrap()
}
pub fn label_hint(&self) -> &Label {
self.children[1].inner.any().downcast_ref().unwrap()
self.children[1].any().downcast_ref().unwrap()
}
pub fn label_hint_mut(&mut self) -> &mut Label {
self.children[1].inner.any_mut().downcast_mut().unwrap()
self.children[1].any_mut().downcast_mut().unwrap()
}
}
impl GuiElemTrait for TextField {
@ -206,8 +208,8 @@ impl GuiElemTrait for TextField {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -215,8 +217,11 @@ impl GuiElemTrait for TextField {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn draw(&mut self, info: &mut crate::gui::DrawInfo, g: &mut speedy2d::Graphics2D) {
let (t, c) = if info.has_keyboard_focus {
@ -235,7 +240,11 @@ impl GuiElemTrait for TextField {
}
fn char_focus(&mut self, modifiers: ModifiersState, key: char) -> Vec<GuiAction> {
if !(modifiers.ctrl() || modifiers.alt() || modifiers.logo()) && !key.is_control() {
let content = &mut self.children[0].try_as_mut::<Label>().unwrap().content;
let content = &mut self.children[0]
.any_mut()
.downcast_mut::<Label>()
.unwrap()
.content;
let was_empty = content.get_text().is_empty();
content.text().push(key);
if let Some(f) = &mut self.on_changed {
@ -247,7 +256,7 @@ impl GuiElemTrait for TextField {
self.on_changed_mut = Some(f);
}
if was_empty {
self.children[1].inner.config_mut().enabled = false;
self.children[1].config_mut().enabled = false;
}
}
vec![]
@ -263,7 +272,11 @@ impl GuiElemTrait for TextField {
&& !(modifiers.alt() || modifiers.logo())
&& key == Some(speedy2d::window::VirtualKeyCode::Backspace)
{
let content = &mut self.children[0].try_as_mut::<Label>().unwrap().content;
let content = &mut self.children[0]
.any_mut()
.downcast_mut::<Label>()
.unwrap()
.content;
if !content.get_text().is_empty() {
if modifiers.ctrl() {
for s in [true, false, true] {
@ -286,30 +299,19 @@ impl GuiElemTrait for TextField {
self.on_changed_mut = Some(f);
}
if is_now_empty {
self.children[1].inner.config_mut().enabled = true;
self.children[1].config_mut().enabled = true;
}
}
}
vec![]
}
}
impl Clone for TextField {
fn clone(&self) -> Self {
Self {
config: self.config.clone(),
children: self.children.clone(),
on_changed: None,
on_changed_mut: None,
}
}
}
/// More advanced version of `Label`.
/// Allows stringing together multiple `Content`s in one line.
#[derive(Clone)]
pub struct AdvancedLabel {
config: GuiElemCfg,
children: Vec<GuiElem>,
children: Vec<Box<dyn GuiElemTrait>>,
/// 0.0 => align to top/left
/// 0.5 => center
/// 1.0 => align to bottom/right
@ -339,8 +341,8 @@ impl GuiElemTrait for AdvancedLabel {
fn config_mut(&mut self) -> &mut GuiElemCfg {
&mut self.config
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
Box::new(self.children.iter_mut())
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
Box::new(self.children.iter_mut().map(|v| v.as_mut()))
}
fn any(&self) -> &dyn std::any::Any {
self
@ -348,8 +350,11 @@ impl GuiElemTrait for AdvancedLabel {
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn draw(&mut self, info: &mut crate::gui::DrawInfo, g: &mut speedy2d::Graphics2D) {
if self.config.redraw

View File

@ -3,17 +3,16 @@ use speedy2d::{
Graphics2D,
};
use crate::gui::{DrawInfo, GuiAction, GuiElem, GuiElemCfg, GuiElemTrait};
use crate::gui::{DrawInfo, GuiAction, GuiElemCfg, GuiElemTrait};
#[derive(Clone)]
pub struct WithFocusHotkey<T: GuiElemTrait + Clone> {
pub struct WithFocusHotkey<T: GuiElemTrait> {
pub inner: T,
/// 4 * (ignore, pressed): 10 or 11 -> doesn't matter, 01 -> must be pressed, 00 -> must not be pressed
/// logo alt shift ctrl
pub modifiers: u8,
pub key: VirtualKeyCode,
}
impl<T: GuiElemTrait + Clone> WithFocusHotkey<T> {
impl<T: GuiElemTrait> WithFocusHotkey<T> {
/// unlike noshift, this ignores the shift modifier
pub fn new_key(key: VirtualKeyCode, inner: T) -> WithFocusHotkey<T> {
Self::new(0b1000, key, inner)
@ -52,7 +51,7 @@ impl<T: GuiElemTrait + Clone> WithFocusHotkey<T> {
}
}
}
impl<T: Clone + 'static> GuiElemTrait for WithFocusHotkey<T>
impl<T: GuiElemTrait> GuiElemTrait for WithFocusHotkey<T>
where
T: GuiElemTrait,
{
@ -62,7 +61,7 @@ where
fn config_mut(&mut self) -> &mut GuiElemCfg {
self.inner.config_mut()
}
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_> {
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElemTrait> + '_> {
self.inner.children()
}
fn any(&self) -> &dyn std::any::Any {
@ -71,8 +70,11 @@ where
fn any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn clone_gui(&self) -> Box<dyn GuiElemTrait> {
Box::new(self.clone())
fn elem(&self) -> &dyn GuiElemTrait {
self
}
fn elem_mut(&mut self) -> &mut dyn GuiElemTrait {
self
}
fn draw(&mut self, info: &mut DrawInfo, g: &mut Graphics2D) {
self.inner.draw(info, g)