musicdb/musicdb-client/src/gui.rs
2023-08-13 23:58:53 +02:00

881 lines
30 KiB
Rust
Executable File

use std::{
any::Any,
eprintln,
net::TcpStream,
sync::{Arc, Mutex},
time::Instant,
usize,
};
use musicdb_lib::{
data::{database::Database, queue::Queue, AlbumId, ArtistId, SongId},
load::ToFromBytes,
server::Command,
};
use speedy2d::{
color::Color,
dimen::{UVec2, Vec2},
font::Font,
shape::Rectangle,
window::{
KeyScancode, ModifiersState, MouseButton, MouseScrollDistance, UserEventSender,
VirtualKeyCode, WindowCreationOptions, WindowHandler, WindowHelper,
},
Graphics2D,
};
use crate::{gui_screen::GuiScreen, gui_wrappers::WithFocusHotkey};
pub enum GuiEvent {
Refresh,
UpdatedQueue,
UpdatedLibrary,
Exit,
}
pub fn main(
database: Arc<Mutex<Database>>,
connection: TcpStream,
event_sender_arc: Arc<Mutex<Option<UserEventSender<GuiEvent>>>>,
) {
let window = speedy2d::Window::<GuiEvent>::new_with_user_events(
"MusicDB Client",
WindowCreationOptions::new_fullscreen_borderless(),
)
.expect("couldn't open window");
*event_sender_arc.lock().unwrap() = Some(window.create_user_event_sender());
let sender = window.create_user_event_sender();
window.run_loop(Gui::new(database, connection, event_sender_arc, sender));
}
pub struct Gui {
pub event_sender: UserEventSender<GuiEvent>,
pub database: Arc<Mutex<Database>>,
pub connection: TcpStream,
pub gui: GuiElem,
pub size: UVec2,
pub mouse_pos: Vec2,
pub font: Font,
pub last_draw: Instant,
pub modifiers: ModifiersState,
pub dragging: Option<(
Dragging,
Option<Box<dyn FnMut(&mut DrawInfo, &mut Graphics2D)>>,
)>,
pub line_height: f32,
pub last_height: f32,
pub scroll_pixels_multiplier: f64,
pub scroll_lines_multiplier: f64,
pub scroll_pages_multiplier: f64,
}
impl Gui {
fn new(
database: Arc<Mutex<Database>>,
connection: TcpStream,
event_sender_arc: Arc<Mutex<Option<UserEventSender<GuiEvent>>>>,
event_sender: UserEventSender<GuiEvent>,
) -> Self {
database.lock().unwrap().update_endpoints.push(
musicdb_lib::data::database::UpdateEndpoint::Custom(Box::new(move |cmd| match cmd {
Command::Resume
| Command::Pause
| Command::Stop
| Command::Save
| Command::SetLibraryDirectory(..) => {}
Command::NextSong
| Command::QueueUpdate(..)
| Command::QueueAdd(..)
| Command::QueueInsert(..)
| Command::QueueRemove(..)
| Command::QueueGoto(..) => {
if let Some(s) = &*event_sender_arc.lock().unwrap() {
_ = s.send_event(GuiEvent::UpdatedQueue);
}
}
Command::SyncDatabase(..)
| Command::AddSong(_)
| Command::AddAlbum(_)
| Command::AddArtist(_)
| Command::ModifySong(_)
| Command::ModifyAlbum(_)
| Command::ModifyArtist(_) => {
if let Some(s) = &*event_sender_arc.lock().unwrap() {
_ = s.send_event(GuiEvent::UpdatedLibrary);
}
}
})),
);
let line_height = 32.0;
let scroll_pixels_multiplier = 1.0;
let scroll_lines_multiplier = 3.0;
let scroll_pages_multiplier = 0.75;
Gui {
event_sender,
database,
connection,
gui: GuiElem::new(WithFocusHotkey::new_noshift(
VirtualKeyCode::Escape,
GuiScreen::new(
GuiElemCfg::default(),
line_height,
scroll_pixels_multiplier,
scroll_lines_multiplier,
scroll_pages_multiplier,
),
)),
size: UVec2::ZERO,
mouse_pos: Vec2::ZERO,
font: Font::new(include_bytes!(
"/usr/share/fonts/mozilla-fira/FiraSans-Regular.otf"
))
.unwrap(),
// font: Font::new(include_bytes!("/usr/share/fonts/TTF/FiraSans-Regular.ttf")).unwrap(),
last_draw: Instant::now(),
modifiers: ModifiersState::default(),
dragging: None,
line_height,
last_height: 720.0,
scroll_pixels_multiplier,
scroll_lines_multiplier,
scroll_pages_multiplier,
}
}
}
/// the trait implemented by all Gui elements.
/// feel free to override the methods you wish to use.
#[allow(unused)]
pub trait GuiElemTrait {
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.
fn children(&mut self) -> Box<dyn Iterator<Item = &mut GuiElem> + '_>;
fn any(&self) -> &dyn Any;
fn any_mut(&mut self) -> &mut dyn Any;
fn clone_gui(&self) -> Box<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.
fn mouse_down(&mut self, button: MouseButton) -> Vec<GuiAction> {
Vec::with_capacity(0)
}
/// an event that is invoked whenever a mouse button that was pressed on the element is released anywhere.
fn mouse_up(&mut self, button: MouseButton) -> Vec<GuiAction> {
Vec::with_capacity(0)
}
/// an event that is invoked after a mouse button was pressed and released on the same GUI element.
fn mouse_pressed(&mut self, button: MouseButton) -> Vec<GuiAction> {
Vec::with_capacity(0)
}
fn mouse_wheel(&mut self, diff: f32) -> Vec<GuiAction> {
Vec::with_capacity(0)
}
fn char_watch(&mut self, modifiers: ModifiersState, key: char) -> Vec<GuiAction> {
Vec::with_capacity(0)
}
fn char_focus(&mut self, modifiers: ModifiersState, key: char) -> Vec<GuiAction> {
Vec::with_capacity(0)
}
fn key_watch(
&mut self,
modifiers: ModifiersState,
down: bool,
key: Option<VirtualKeyCode>,
scan: KeyScancode,
) -> Vec<GuiAction> {
Vec::with_capacity(0)
}
fn key_focus(
&mut self,
modifiers: ModifiersState,
down: bool,
key: Option<VirtualKeyCode>,
scan: KeyScancode,
) -> Vec<GuiAction> {
Vec::with_capacity(0)
}
/// When something is dragged and released over this element
fn dragged(&mut self, dragged: Dragging) -> Vec<GuiAction> {
Vec::with_capacity(0)
}
fn updated_library(&mut self) {}
fn updated_queue(&mut self) {}
}
#[derive(Debug, Clone)]
pub struct GuiElemCfg {
pub enabled: bool,
/// if true, indicates that something (text size, screen size, ...) has changed
/// and you should probably relayout and redraw from scratch.
pub redraw: bool,
pub pos: Rectangle,
/// the pixel position after the last call to draw().
/// in draw, use info.pos instead, as pixel_pos is only updated *after* draw().
/// this can act like a "previous pos" field within draw.
pub pixel_pos: Rectangle,
pub mouse_down: (bool, bool, bool),
pub mouse_events: bool,
pub scroll_events: bool,
/// allows elements to watch all keyboard events, regardless of keyboard focus.
pub keyboard_events_watch: bool,
/// indicates that this element can have the keyboard focus
pub keyboard_events_focus: bool,
/// index of the child that has keyboard focus. if usize::MAX, `self` has focus.
/// will automatically be changed when Tab is pressed. [TODO]
pub keyboard_focus_index: usize,
pub request_keyboard_focus: bool,
pub drag_target: bool,
}
impl GuiElemCfg {
pub fn at(pos: Rectangle) -> Self {
Self {
pos,
..Default::default()
}
}
pub fn w_mouse(mut self) -> Self {
self.mouse_events = true;
self
}
pub fn w_scroll(mut self) -> Self {
self.scroll_events = true;
self
}
pub fn w_keyboard_watch(mut self) -> Self {
self.keyboard_events_watch = true;
self
}
pub fn w_keyboard_focus(mut self) -> Self {
self.keyboard_events_focus = true;
self
}
pub fn w_drag_target(mut self) -> Self {
self.drag_target = true;
self
}
pub fn disabled(mut self) -> Self {
self.enabled = false;
self
}
}
impl Default for GuiElemCfg {
fn default() -> Self {
Self {
enabled: true,
redraw: false,
pos: Rectangle::new(Vec2::ZERO, Vec2::new(1.0, 1.0)),
pixel_pos: Rectangle::ZERO,
mouse_down: (false, false, false),
mouse_events: false,
scroll_events: false,
keyboard_events_watch: false,
keyboard_events_focus: false,
keyboard_focus_index: usize::MAX,
request_keyboard_focus: false,
drag_target: false,
}
}
}
pub enum GuiAction {
OpenMain,
SetIdle(bool),
OpenSettings(bool),
Build(Box<dyn FnOnce(&mut Database) -> Vec<Self>>),
SendToServer(Command),
/// unfocuses all gui elements, then assigns keyboard focus to one with config().request_keyboard_focus == true.
ResetKeyboardFocus,
SetDragging(
Option<(
Dragging,
Option<Box<dyn FnMut(&mut DrawInfo, &mut Graphics2D)>>,
)>,
),
SetLineHeight(f32),
Do(Box<dyn FnMut(&mut Gui)>),
Exit,
}
pub enum Dragging {
Artist(ArtistId),
Album(AlbumId),
Song(SongId),
Queue(Queue),
}
pub struct DrawInfo<'a> {
pub actions: Vec<GuiAction>,
pub pos: Rectangle,
pub database: &'a mut Database,
pub font: &'a Font,
/// absolute position of the mouse on the screen.
/// compare this to `pos` to find the mouse's relative position.
pub mouse_pos: Vec2,
pub helper: Option<&'a mut WindowHelper<GuiEvent>>,
pub has_keyboard_focus: bool,
pub child_has_keyboard_focus: bool,
/// the height of one line of text (in pixels)
pub line_height: f32,
}
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;
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);
}
// 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()),
adjust_pos(outer, rel_area.bottom_right()),
)
}
pub fn adjust_pos(outer: &Rectangle, rel_pos: &Vec2) -> Vec2 {
Vec2::new(
outer.top_left().x + outer.width() * rel_pos.x,
outer.top_left().y + outer.height() * rel_pos.y,
)
}
impl Gui {
fn exec_gui_action(&mut self, action: GuiAction) {
match action {
GuiAction::Build(f) => {
let actions = f(&mut *self.database.lock().unwrap());
for action in actions {
self.exec_gui_action(action);
}
}
GuiAction::SendToServer(cmd) => {
if let Err(e) = cmd.to_bytes(&mut self.connection) {
eprintln!("Error sending command to server: {e}");
}
}
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);
}
GuiAction::Do(mut f) => f(self),
GuiAction::Exit => _ = self.event_sender.send_event(GuiEvent::Exit),
GuiAction::SetIdle(v) => {
if let Some(gui) = self
.gui
.inner
.any_mut()
.downcast_mut::<WithFocusHotkey<GuiScreen>>()
{
if gui.inner.idle.0 != v {
gui.inner.idle = (v, Some(Instant::now()));
}
}
}
GuiAction::OpenSettings(v) => {
if let Some(gui) = self
.gui
.inner
.any_mut()
.downcast_mut::<WithFocusHotkey<GuiScreen>>()
{
if gui.inner.idle.0 {
gui.inner.idle = (false, Some(Instant::now()));
}
if gui.inner.settings.0 != v {
gui.inner.settings = (v, Some(Instant::now()));
}
}
}
GuiAction::OpenMain => {
if let Some(gui) = self
.gui
.inner
.any_mut()
.downcast_mut::<WithFocusHotkey<GuiScreen>>()
{
if gui.inner.idle.0 {
gui.inner.idle = (false, Some(Instant::now()));
}
if gui.inner.settings.0 {
gui.inner.settings = (false, Some(Instant::now()));
}
}
}
}
}
}
impl WindowHandler<GuiEvent> for Gui {
fn on_draw(&mut self, helper: &mut WindowHelper<GuiEvent>, graphics: &mut Graphics2D) {
let start = Instant::now();
graphics.draw_rectangle(
Rectangle::new(Vec2::ZERO, self.size.into_f32()),
Color::BLACK,
);
let mut dblock = self.database.lock().unwrap();
let mut info = DrawInfo {
actions: Vec::with_capacity(0),
pos: Rectangle::new(Vec2::ZERO, self.size.into_f32()),
database: &mut *dblock,
font: &self.font,
mouse_pos: self.mouse_pos,
helper: Some(helper),
has_keyboard_focus: false,
child_has_keyboard_focus: true,
line_height: self.line_height,
};
self.gui.draw(&mut info, graphics);
let actions = std::mem::replace(&mut info.actions, Vec::with_capacity(0));
if let Some((d, f)) = &mut self.dragging {
if let Some(f) = f {
f(&mut info, graphics);
} else {
match d {
Dragging::Artist(_) => graphics.draw_circle(
self.mouse_pos,
25.0,
Color::from_int_rgba(0, 100, 255, 100),
),
Dragging::Album(_) => graphics.draw_circle(
self.mouse_pos,
25.0,
Color::from_int_rgba(0, 100, 255, 100),
),
Dragging::Song(_) => graphics.draw_circle(
self.mouse_pos,
25.0,
Color::from_int_rgba(0, 100, 255, 100),
),
Dragging::Queue(_) => graphics.draw_circle(
self.mouse_pos,
25.0,
Color::from_int_rgba(100, 0, 255, 100),
),
}
}
}
drop(info);
drop(dblock);
for a in actions {
self.exec_gui_action(a);
}
// eprintln!(
// "fps <= {}",
// 1000 / self.last_draw.elapsed().as_millis().max(1)
// );
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()) {
for a in a {
self.exec_gui_action(a)
}
}
helper.request_redraw();
}
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(
&mut self.dragging.take().map(|v| v.0),
self.mouse_pos.clone(),
) {
for a in a {
self.exec_gui_action(a)
}
}
}
if let Some(a) = self.gui.mouse_button(button, false, self.mouse_pos.clone()) {
for a in a {
self.exec_gui_action(a)
}
}
helper.request_redraw();
}
fn on_mouse_wheel_scroll(
&mut self,
helper: &mut WindowHelper<GuiEvent>,
distance: speedy2d::window::MouseScrollDistance,
) {
let dist = match distance {
MouseScrollDistance::Pixels { y, .. } => (self.scroll_pixels_multiplier * y) as f32,
MouseScrollDistance::Lines { y, .. } => {
(self.scroll_lines_multiplier * y) as f32 * self.line_height
}
MouseScrollDistance::Pages { y, .. } => {
(self.scroll_pages_multiplier * y) as f32 * self.last_height
}
};
if let Some(a) = self.gui.mouse_wheel(dist, self.mouse_pos.clone()) {
for a in a {
self.exec_gui_action(a)
}
}
helper.request_redraw();
}
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),
);
}
},
|e, a| {
if e.inner.config().keyboard_events_watch {
a.append(
&mut e
.inner
.char_watch(self.modifiers.clone(), unicode_codepoint),
);
}
},
) {
self.exec_gui_action(a);
}
}
fn on_key_down(
&mut self,
helper: &mut WindowHelper<GuiEvent>,
virtual_key_code: Option<VirtualKeyCode>,
scancode: KeyScancode,
) {
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);
}
}
for a in self.gui.keyboard_event(
|e, a| {
if e.inner.config().keyboard_events_focus {
a.append(&mut e.inner.key_focus(
self.modifiers.clone(),
true,
virtual_key_code,
scancode,
));
}
},
|e, a| {
if e.inner.config().keyboard_events_watch {
a.append(&mut e.inner.key_watch(
self.modifiers.clone(),
true,
virtual_key_code,
scancode,
));
}
},
) {
self.exec_gui_action(a);
}
}
fn on_key_up(
&mut self,
helper: &mut WindowHelper<GuiEvent>,
virtual_key_code: Option<VirtualKeyCode>,
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(
self.modifiers.clone(),
false,
virtual_key_code,
scancode,
));
}
},
|e, a| {
if e.inner.config().keyboard_events_watch {
a.append(&mut e.inner.key_watch(
self.modifiers.clone(),
false,
virtual_key_code,
scancode,
));
}
},
) {
self.exec_gui_action(a);
}
}
fn on_keyboard_modifiers_changed(
&mut self,
_helper: &mut WindowHelper<GuiEvent>,
state: ModifiersState,
) {
self.modifiers = state;
}
fn on_user_event(&mut self, helper: &mut WindowHelper<GuiEvent>, user_event: GuiEvent) {
match user_event {
GuiEvent::Refresh => helper.request_redraw(),
GuiEvent::UpdatedLibrary => {
self.gui.recursive_all(&mut |e| e.inner.updated_library());
helper.request_redraw();
}
GuiEvent::UpdatedQueue => {
self.gui.recursive_all(&mut |e| e.inner.updated_queue());
helper.request_redraw();
}
GuiEvent::Exit => helper.terminate_loop(),
}
}
fn on_mouse_move(&mut self, helper: &mut WindowHelper<GuiEvent>, position: Vec2) {
self.mouse_pos = position;
helper.request_redraw();
}
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);
}
}