From 52336040f2f470e0d62dde78ce279dbdf8b8a06f Mon Sep 17 00:00:00 2001 From: hodasemi <michaelh.95@t-online.de> Date: Tue, 4 Mar 2025 18:18:08 +0100 Subject: [PATCH] Improve ecs ergonomics --- src/builder/builder.rs | 27 ++++--- src/builder/snippet.rs | 21 +++-- src/elements/button.rs | 15 ++-- src/elements/callback_builder.rs | 18 +++-- src/elements/multi_line_textfield.rs | 5 +- src/elements/textfield.rs | 3 +- src/elements/traits.rs | 21 +++-- src/gui_handler/gui/executable.rs | 5 +- src/gui_handler/gui/selectable.rs | 14 +++- src/gui_handler/gui/topgui.rs | 9 +-- src/gui_handler/gui_handler.rs | 29 ++++--- src/keyboard/mod.rs | 80 +++++++++++------- src/lib.rs | 2 - src/prelude.rs | 2 - src/states.rs | 116 +++++++++++++++------------ 15 files changed, 216 insertions(+), 151 deletions(-) diff --git a/src/builder/builder.rs b/src/builder/builder.rs index 8cb20e4..3041e9a 100644 --- a/src/builder/builder.rs +++ b/src/builder/builder.rs @@ -2,6 +2,7 @@ use crate::prelude::*; use anyhow::{Context, Result, bail}; use assetpath::AssetPath; +use ecs::World; use super::validator::buttoninfo::{NeighbourDirection, NeighbourInfo}; use super::validator::gridinfo::GridInfo; @@ -178,10 +179,7 @@ impl GuiBuilder { impl Functionality for GuiBuilder { fn set_click_callbacks( &self, - functions: Vec<( - &str, - Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>, - )>, + functions: Vec<(&str, Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>)>, ) -> Result<()> { for (function_name, callback) in functions { let suffix_less_function_name = handle_function_suffix(function_name); @@ -195,14 +193,20 @@ impl Functionality for GuiBuilder { fn set_vec_callbacks( &self, - _functions: Vec<(&str, Box<dyn Fn(&dyn Any) -> Result<()> + Send + Sync>)>, + _functions: Vec<( + &str, + Box<dyn Fn(&mut World, &dyn Any) -> Result<()> + Send + Sync>, + )>, ) -> Result<()> { Ok(()) } fn set_select_callbacks( &self, - callbacks: Vec<(&str, Box<CustomCallback<bool, ()>>)>, + callbacks: Vec<( + &str, + Box<dyn Fn(&mut World, bool) -> Result<()> + Send + Sync>, + )>, ) -> Result<()> { for (function_name, callback) in callbacks { let suffix_less_function_name = handle_function_suffix(function_name); @@ -216,7 +220,10 @@ impl Functionality for GuiBuilder { fn set_custom_callbacks( &self, - callbacks: Vec<(&str, Box<CustomCallback<ControllerButton, bool>>)>, + callbacks: Vec<( + &str, + Box<dyn Fn(&mut World, ControllerButton) -> Result<bool> + Send + Sync>, + )>, ) -> Result<()> { for (function_name, callback) in callbacks { let suffix_less_function_name = handle_function_suffix(function_name); @@ -262,7 +269,7 @@ impl GuiElementTraits for GuiBuilder { } impl TopGui for GuiBuilder { - fn decline(&self, _gui_handler: &mut GuiHandler) -> Result<()> { + fn decline(&self, _world: &mut World) -> Result<()> { if let Some(decline_callback) = self.decline_callback.read().unwrap().as_ref() { decline_callback()?; } @@ -270,11 +277,11 @@ impl TopGui for GuiBuilder { Ok(()) } - fn next_tab(&self, _gui_handler: &mut GuiHandler, _: bool) -> Result<()> { + fn next_tab(&self, _world: &mut World, _: bool) -> Result<()> { Ok(()) } - fn previous_tab(&self, _gui_handler: &mut GuiHandler, _: bool) -> Result<()> { + fn previous_tab(&self, _world: &mut World, _: bool) -> Result<()> { Ok(()) } } diff --git a/src/builder/snippet.rs b/src/builder/snippet.rs index 720f37c..d8436e3 100644 --- a/src/builder/snippet.rs +++ b/src/builder/snippet.rs @@ -1,6 +1,7 @@ use std::{any::Any, collections::HashMap, sync::Arc}; use assetpath::AssetPath; +use ecs::World; use super::validator::{ buttoninfo::NeighbourInfo, @@ -147,10 +148,7 @@ impl Gridable for GuiSnippet { impl Functionality for GuiSnippet { fn set_click_callbacks( &self, - functions: Vec<( - &str, - Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>, - )>, + functions: Vec<(&str, Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>)>, ) -> Result<()> { for (function_name, callback) in functions { let suffix_less_function_name = handle_function_suffix(function_name); @@ -164,14 +162,20 @@ impl Functionality for GuiSnippet { fn set_vec_callbacks( &self, - _functions: Vec<(&str, Box<dyn Fn(&dyn Any) -> Result<()> + Send + Sync>)>, + _functions: Vec<( + &str, + Box<dyn Fn(&mut World, &dyn Any) -> Result<()> + Send + Sync>, + )>, ) -> Result<()> { Ok(()) } fn set_select_callbacks( &self, - callbacks: Vec<(&str, Box<CustomCallback<bool, ()>>)>, + callbacks: Vec<( + &str, + Box<dyn Fn(&mut World, bool) -> Result<()> + Send + Sync>, + )>, ) -> Result<()> { for (function_name, callback) in callbacks { let suffix_less_function_name = handle_function_suffix(function_name); @@ -185,7 +189,10 @@ impl Functionality for GuiSnippet { fn set_custom_callbacks( &self, - callbacks: Vec<(&str, Box<CustomCallback<ControllerButton, bool>>)>, + callbacks: Vec<( + &str, + Box<dyn Fn(&mut World, ControllerButton) -> Result<bool> + Send + Sync>, + )>, ) -> Result<()> { for (function_name, callback) in callbacks { let suffix_less_function_name = handle_function_suffix(function_name); diff --git a/src/elements/button.rs b/src/elements/button.rs index 1c315ff..156f87a 100644 --- a/src/elements/button.rs +++ b/src/elements/button.rs @@ -5,6 +5,7 @@ use crate::{ use anyhow::{Result, anyhow}; use assetpath::AssetPath; +use ecs::World; use utilities::prelude::*; use vulkan_rs::prelude::*; @@ -314,23 +315,23 @@ impl Button { pub fn set_callback<F>(&self, callback: F) where - F: Fn(&mut GuiHandler) -> Result<()> + Send + Sync + 'static, + F: Fn(&mut World) -> Result<()> + Send + Sync + 'static, { self.click_executable - .set_callback(move |gui_handler: &mut GuiHandler, _: ()| callback(gui_handler)); + .set_callback(move |world: &mut World, _: ()| callback(world)); } pub fn set_select_callback<F>(&self, callback: F) where - F: Fn(bool) -> Result<()> + Send + Sync + 'static, + F: Fn(&mut World, bool) -> Result<()> + Send + Sync + 'static, { self.on_select_executable - .set_callback(move |_: &mut GuiHandler, select: bool| callback(select)); + .set_callback(move |world: &mut World, select: bool| callback(world, select)); } pub fn set_custom_callback<F>(&self, callback: F) where - F: Fn(ControllerButton) -> Result<bool> + Send + Sync + 'static, + F: Fn(&mut World, ControllerButton) -> Result<bool> + Send + Sync + 'static, { self.selectable.set_custom_callback(callback); } @@ -578,8 +579,10 @@ impl Button { fn create_selected_changed_callback(button: Arc<Button>) { let button_weak = Arc::downgrade(&button); - let selected_changed = move |gui_handler: &mut GuiHandler, selected: bool| { + let selected_changed = move |world: &mut World, selected: bool| { if let Some(button) = button_weak.upgrade() { + let gui_handler = world.resources.get_mut::<GuiHandler>(); + if selected { button.set_button_state(gui_handler, ButtonState::Selected)?; } else { diff --git a/src/elements/callback_builder.rs b/src/elements/callback_builder.rs index ee68db0..45156d9 100644 --- a/src/elements/callback_builder.rs +++ b/src/elements/callback_builder.rs @@ -1,6 +1,7 @@ use std::any::Any; use anyhow::Result; +use ecs::World; use super::ControllerButton; use crate::prelude::*; @@ -27,18 +28,21 @@ macro_rules! callbacks { }; } -callbacks!(ClickCallbacks, Fn(&mut GuiHandler) -> Result<()> + Send + Sync); -callbacks!(SelectCallbacks, Fn(bool) -> anyhow::Result<()> + Send + Sync); -callbacks!(CustomCallbacks, Fn(ControllerButton) -> anyhow::Result<bool> + Send + Sync); -callbacks!(VecCallbacks, Fn(&dyn Any) -> Result<()> + Send + Sync); +callbacks!(ClickCallbacks, Fn(&mut World, &mut GuiHandler) -> Result<()> + Send + Sync); +callbacks!(SelectCallbacks, Fn(&mut World, &mut GuiHandler, bool) -> anyhow::Result<()> + Send + Sync); +callbacks!(CustomCallbacks, Fn(&mut World, &mut GuiHandler, ControllerButton) -> anyhow::Result<bool> + Send + Sync); +callbacks!( + VecCallbacks, + Fn(&mut World, &mut GuiHandler, &dyn Any) -> Result<()> + Send + Sync +); #[cfg(test)] mod test { use super::*; use anyhow::Result; - fn normal_fn() -> impl Fn(&mut GuiHandler) -> Result<()> + Send + Sync { - |_| Ok(()) + fn normal_fn() -> impl Fn(&mut World, &mut GuiHandler) -> Result<()> + Send + Sync { + |_, _| Ok(()) } #[test] @@ -50,7 +54,7 @@ mod test { &self, _callbacks: Vec<( &str, - Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>, + Box<dyn Fn(&mut World, &mut GuiHandler) -> Result<()> + Send + Sync>, )>, ) -> Result<()> { Ok(()) diff --git a/src/elements/multi_line_textfield.rs b/src/elements/multi_line_textfield.rs index 86cbc92..56988f7 100644 --- a/src/elements/multi_line_textfield.rs +++ b/src/elements/multi_line_textfield.rs @@ -10,6 +10,7 @@ use std::sync::{ }; use anyhow::{Context, Result}; +use ecs::World; use utilities::prelude::*; use super::fill_type::InnerFillType; @@ -96,9 +97,9 @@ impl MultiLineTextFieldBuilder { multi_line_text_field.text_changed_exec.set_callback({ let weak_tf = Arc::downgrade(&multi_line_text_field); - move |gui_handler: &mut GuiHandler, _text| { + move |world: &mut World, _text| { if let Some(tf) = weak_tf.upgrade() { - tf.update_text(gui_handler)?; + tf.update_text(world.resources.get_mut::<GuiHandler>())?; } Ok(()) diff --git a/src/elements/textfield.rs b/src/elements/textfield.rs index 283c685..3a987a4 100644 --- a/src/elements/textfield.rs +++ b/src/elements/textfield.rs @@ -1,5 +1,6 @@ use crate::{builder::validator::textfieldinfo::TextFieldInfo, prelude::*}; use anyhow::Result; +use ecs::World; use utilities::prelude::*; use std::sync::{ @@ -169,7 +170,7 @@ impl TextField { pub fn set_text_changed_callback<F>(&self, f: F) where - F: Fn(&mut GuiHandler, Option<String>) -> Result<()> + Send + Sync + 'static, + F: Fn(&mut World, Option<String>) -> Result<()> + Send + Sync + 'static, { self.text_changed_executable.set_callback(f); } diff --git a/src/elements/traits.rs b/src/elements/traits.rs index 7f201f5..9a20c79 100644 --- a/src/elements/traits.rs +++ b/src/elements/traits.rs @@ -1,5 +1,6 @@ use crate::prelude::*; use anyhow::Result; +use ecs::World; use std::{any::Any, collections::HashMap, sync::Arc}; @@ -26,25 +27,31 @@ where pub trait Functionality { fn set_click_callbacks( &self, - callbacks: Vec<( - &str, - Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>, - )>, + callbacks: Vec<(&str, Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>)>, ) -> Result<()>; fn set_select_callbacks( &self, - callbacks: Vec<(&str, Box<CustomCallback<bool, ()>>)>, + callbacks: Vec<( + &str, + Box<dyn Fn(&mut World, bool) -> Result<()> + Send + Sync>, + )>, ) -> Result<()>; fn set_custom_callbacks( &self, - callbacks: Vec<(&str, Box<CustomCallback<ControllerButton, bool>>)>, + callbacks: Vec<( + &str, + Box<dyn Fn(&mut World, ControllerButton) -> Result<bool> + Send + Sync>, + )>, ) -> Result<()>; fn set_vec_callbacks( &self, - callbacks: Vec<(&str, Box<dyn Fn(&dyn Any) -> Result<()> + Send + Sync>)>, + callbacks: Vec<( + &str, + Box<dyn Fn(&mut World, &dyn Any) -> Result<()> + Send + Sync>, + )>, ) -> Result<()>; } diff --git a/src/gui_handler/gui/executable.rs b/src/gui_handler/gui/executable.rs index ad8f231..a334b73 100644 --- a/src/gui_handler/gui/executable.rs +++ b/src/gui_handler/gui/executable.rs @@ -1,6 +1,7 @@ //! `Executable` is a property to execute a closure use anyhow::Result; +use ecs::World; use std::sync::{Arc, RwLock}; @@ -8,7 +9,7 @@ use crate::prelude::*; /// `Executable` holds a closure which can be executed pub struct Executable<I: Send + Sync> { - callback: RwLock<Option<Arc<dyn Fn(&mut GuiHandler, I) -> Result<()> + Send + Sync>>>, + callback: RwLock<Option<Arc<dyn Fn(&mut World, I) -> Result<()> + Send + Sync>>>, } impl<I: Send + Sync + 'static> Executable<I> { @@ -26,7 +27,7 @@ impl<I: Send + Sync + 'static> Executable<I> { /// * `callback` is a `Option<Callback>` closure pub fn set_callback<F>(&self, callback: impl Into<Option<F>>) where - F: Fn(&mut GuiHandler, I) -> Result<()> + Send + Sync + 'static, + F: Fn(&mut World, I) -> Result<()> + Send + Sync + 'static, { let mut function = self.callback.write().unwrap(); diff --git a/src/gui_handler/gui/selectable.rs b/src/gui_handler/gui/selectable.rs index a4bfa80..9fb49fa 100644 --- a/src/gui_handler/gui/selectable.rs +++ b/src/gui_handler/gui/selectable.rs @@ -3,6 +3,7 @@ use super::executable::Executable; use crate::prelude::*; use anyhow::Result; +use ecs::World; use std::sync::{ Arc, RwLock, Weak, @@ -39,7 +40,8 @@ pub struct Selectable { on_select_executable: Arc<Executable<bool>>, // used for custom buttons - custom_callback: RwLock<Option<Box<dyn Fn(ControllerButton) -> Result<bool> + Send + Sync>>>, + custom_callback: + RwLock<Option<Box<dyn Fn(&mut World, ControllerButton) -> Result<bool> + Send + Sync>>>, } impl Selectable { @@ -115,7 +117,7 @@ impl Selectable { pub fn set_custom_callback<F>(&self, custom_callback: F) where - F: Fn(ControllerButton) -> Result<bool> + Send + Sync + 'static, + F: Fn(&mut World, ControllerButton) -> Result<bool> + Send + Sync + 'static, { *self.custom_callback.write().unwrap() = Some(Box::new(custom_callback)); } @@ -176,9 +178,13 @@ impl Selectable { Ok(()) } - pub(crate) fn custom_click_event(&self, button: ControllerButton) -> Result<bool> { + pub(crate) fn custom_click_event( + &self, + world: &mut World, + button: ControllerButton, + ) -> Result<bool> { if let Some(custom_callback) = self.custom_callback.read().unwrap().as_ref() { - if custom_callback(button)? { + if custom_callback(world, button)? { #[cfg(feature = "audio")] { if let Some(audible) = self.click_audible.read().unwrap().as_ref() { diff --git a/src/gui_handler/gui/topgui.rs b/src/gui_handler/gui/topgui.rs index 929d76e..c7532a9 100644 --- a/src/gui_handler/gui/topgui.rs +++ b/src/gui_handler/gui/topgui.rs @@ -1,21 +1,20 @@ /// A trait that is used by the gui handler as the target for input use anyhow::Result; - -use crate::prelude::*; +use ecs::World; pub trait TopGui: Send + Sync { /// Decline method which is executed on `InputMap::B` press - fn decline(&self, gui_handler: &mut GuiHandler) -> Result<()>; + fn decline(&self, world: &mut World) -> Result<()>; /// Method which is executed on `InputMap::RightButton` press /// /// # Arguments /// * `second_level` adds support for multiple tab layers, e.g. RB and RT press on controller - fn next_tab(&self, gui_handler: &mut GuiHandler, second_level: bool) -> Result<()>; + fn next_tab(&self, world: &mut World, second_level: bool) -> Result<()>; /// Method which is executed on `InputMap::LeftButton` press /// /// /// # Arguments /// * `second_level` adds support for multiple tab layers, e.g. RB and RT press on controller - fn previous_tab(&self, gui_handler: &mut GuiHandler, second_level: bool) -> Result<()>; + fn previous_tab(&self, world: &mut World, second_level: bool) -> Result<()>; } diff --git a/src/gui_handler/gui_handler.rs b/src/gui_handler/gui_handler.rs index 5fc7636..badd9fe 100644 --- a/src/gui_handler/gui_handler.rs +++ b/src/gui_handler/gui_handler.rs @@ -2,6 +2,7 @@ use crate::prelude::*; use anyhow::{Result, anyhow}; use assetpath::AssetPath; +use ecs::World; use serde::{Deserialize, Serialize}; use utilities::{impl_reprc, prelude::*}; use vulkan_rs::{prelude::*, render_target::sub_pass::InputAttachmentInfo}; @@ -243,7 +244,7 @@ pub struct GuiHandler { text_sample_count: VkSampleCountFlags, - callback_list: Vec<Box<dyn FnOnce(&mut Self) -> Result<()> + Send + Sync>>, + callback_list: Vec<Box<dyn FnOnce(&mut World) -> Result<()> + Send + Sync>>, } impl GuiHandler { @@ -726,9 +727,13 @@ impl GuiHandler { Ok(false) } - pub fn accept_custom_selection(&self, button: ControllerButton) -> Result<bool> { + pub fn accept_custom_selection( + &self, + world: &mut World, + button: ControllerButton, + ) -> Result<bool> { if let Some(current_selectable) = &self.current_selectable { - if current_selectable.custom_click_event(button)? { + if current_selectable.custom_click_event(world, button)? { return Ok(true); } } @@ -736,36 +741,36 @@ impl GuiHandler { Ok(false) } - pub fn decline_topgui(&mut self) -> Result<bool> { + pub fn decline_topgui(&mut self, world: &mut World) -> Result<bool> { // workaround for unwanted borrowing behaviour inside decline function let opt_topgui = self.top_ui.as_ref().cloned(); if let Some(topgui) = opt_topgui { - topgui.decline(self)?; + topgui.decline(world)?; return Ok(true); } Ok(false) } - pub fn next_tab_topgui(&mut self, second_level: bool) -> Result<bool> { + pub fn next_tab_topgui(&mut self, world: &mut World, second_level: bool) -> Result<bool> { // workaround for unwanted borrowing behaviour inside decline function let opt_topgui = self.top_ui.as_ref().cloned(); if let Some(topgui) = opt_topgui { - topgui.next_tab(self, second_level)?; + topgui.next_tab(world, second_level)?; return Ok(true); } Ok(false) } - pub fn previous_tab_topgui(&mut self, second_level: bool) -> Result<bool> { + pub fn previous_tab_topgui(&mut self, world: &mut World, second_level: bool) -> Result<bool> { // workaround for unwanted borrowing behaviour inside decline function let opt_topgui = self.top_ui.as_ref().cloned(); if let Some(topgui) = opt_topgui { - topgui.previous_tab(self, second_level)?; + topgui.previous_tab(world, second_level)?; return Ok(true); } @@ -860,19 +865,19 @@ impl GuiHandler { self.tooltip_ui = tooltip; } - pub(crate) fn add_callback<F: FnOnce(&mut Self) -> Result<()> + Send + Sync + 'static>( + pub(crate) fn add_callback<F: FnOnce(&mut World) -> Result<()> + Send + Sync + 'static>( &mut self, f: F, ) { self.callback_list.push(Box::new(f)); } - pub fn process_callbacks(&mut self) -> Result<()> { + pub fn process_callbacks(&mut self, world: &mut World) -> Result<()> { let callbacks = mem::take(&mut self.callback_list); callbacks .into_iter() - .try_for_each(|callback| callback(self)) + .try_for_each(|callback| callback(world)) } fn render( diff --git a/src/keyboard/mod.rs b/src/keyboard/mod.rs index 39c6f18..c7131cd 100644 --- a/src/keyboard/mod.rs +++ b/src/keyboard/mod.rs @@ -1,5 +1,6 @@ use crate::prelude::*; use anyhow::Result; +use ecs::World; use std::any::Any; use std::collections::HashMap; @@ -25,8 +26,9 @@ pub struct Keyboard { mode: Arc<RwLock<KeyboardMode>>, - decline_callback: Arc<RwLock<Option<Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>>>>, - accept_callback: Arc<RwLock<Option<Box<dyn Fn(&dyn Any) -> Result<()> + Send + Sync>>>>, + decline_callback: Arc<RwLock<Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>>>, + accept_callback: + Arc<RwLock<Option<Box<dyn Fn(&mut World, &dyn Any) -> Result<()> + Send + Sync>>>>, elements: HashMap<String, UiElement>, } @@ -39,11 +41,11 @@ impl Keyboard { let text_field: Arc<TextField> = text_field_gui.element("field")?; let decline_callback: Arc< - RwLock<Option<Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>>>, + RwLock<Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>>, > = Arc::new(RwLock::new(None)); let accept_callback: Arc< - RwLock<Option<Box<dyn Fn(&dyn Any) -> Result<()> + Send + Sync>>>, + RwLock<Option<Box<dyn Fn(&mut World, &dyn Any) -> Result<()> + Send + Sync>>>, > = Arc::new(RwLock::new(None)); let mode = Arc::new(RwLock::new(KeyboardMode::UpperCase)); @@ -84,10 +86,10 @@ impl Keyboard { gui_handler: &mut GuiHandler, textfield: Arc<TextField>, mode: &Arc<RwLock<KeyboardMode>>, - decline_callback: Arc< - RwLock<Option<Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>>>, + decline_callback: Arc<RwLock<Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>>>, + accept_callback: Arc< + RwLock<Option<Box<dyn Fn(&mut World, &dyn Any) -> Result<()> + Send + Sync>>>, >, - accept_callback: Arc<RwLock<Option<Box<dyn Fn(&dyn Any) -> Result<()> + Send + Sync>>>>, ) -> Result<(Arc<GuiBuilder>, Arc<GuiBuilder>, Arc<GuiBuilder>)> { let lower_case = GuiBuilder::from_str(gui_handler, include_str!("lower_case.xml"))?; let upper_case = GuiBuilder::from_str(gui_handler, include_str!("upper_case.xml"))?; @@ -193,9 +195,9 @@ impl Keyboard { Self::set_text_callback(&specials, ".", textfield.clone())?; Self::set_text_callback(&specials, "_", textfield.clone())?; - let back = Box::new(move |gui_handler: &mut GuiHandler| { + let back = Box::new(move |world: &mut World| { if let Some(callback) = decline_callback.read().unwrap().as_ref() { - (callback)(gui_handler)?; + (callback)(world)?; } Ok(()) @@ -205,12 +207,12 @@ impl Keyboard { let weak_textfield = Arc::downgrade(&textfield); let weak_accept = Arc::downgrade(&accept_callback); - Box::new(move |_: &mut GuiHandler| { + Box::new(move |world: &mut World| { if let Some(textfield) = weak_textfield.upgrade() { if let Some(accept_callback) = weak_accept.upgrade() { if let Some(text) = textfield.text() { if let Some(callback) = accept_callback.read().unwrap().as_ref() { - (callback)(&text)?; + (callback)(world, &text)?; } } } @@ -225,13 +227,16 @@ impl Keyboard { let weak_lower = Arc::downgrade(&lower_case); let weak_upper = Arc::downgrade(&upper_case); - Box::new(move |gui_handler: &mut GuiHandler| { + Box::new(move |world: &mut World| { if let Some(lower) = weak_lower.upgrade() { if let Some(upper) = weak_upper.upgrade() { let mut mode = mode.write().unwrap(); if let KeyboardMode::LowerCase = mode.deref() { *mode = KeyboardMode::UpperCase; + + let gui_handler = world.resources.get_mut::<GuiHandler>(); + lower.disable(gui_handler)?; upper.enable(gui_handler)?; } @@ -247,13 +252,16 @@ impl Keyboard { let weak_upper = Arc::downgrade(&upper_case); let weak_specials = Arc::downgrade(&specials); - Box::new(move |gui_handler: &mut GuiHandler| { + Box::new(move |world: &mut World| { if let Some(specials) = weak_specials.upgrade() { if let Some(upper) = weak_upper.upgrade() { let mut mode = mode.write().unwrap(); if let KeyboardMode::UpperCase = mode.deref() { *mode = KeyboardMode::Specials; + + let gui_handler = world.resources.get_mut::<GuiHandler>(); + upper.disable(gui_handler)?; specials.enable(gui_handler)?; } @@ -269,13 +277,16 @@ impl Keyboard { let weak_lower = Arc::downgrade(&lower_case); let weak_specials = Arc::downgrade(&specials); - Box::new(move |gui_handler: &mut GuiHandler| { + Box::new(move |world: &mut World| { if let Some(lower) = weak_lower.upgrade() { if let Some(specials) = weak_specials.upgrade() { let mut mode = mode.write().unwrap(); if let KeyboardMode::Specials = mode.deref() { *mode = KeyboardMode::LowerCase; + + let gui_handler = world.resources.get_mut::<GuiHandler>(); + specials.disable(gui_handler)?; lower.enable(gui_handler)?; } @@ -289,8 +300,9 @@ impl Keyboard { let space_bar = { let weak_textfield = Arc::downgrade(&textfield); - Box::new(move |gui_handler: &mut GuiHandler| { + Box::new(move |world: &mut World| { if let Some(text_field) = weak_textfield.upgrade() { + let gui_handler = world.resources.get_mut::<GuiHandler>(); text_field.add_letter(gui_handler, ' ')?; } @@ -330,10 +342,11 @@ impl Keyboard { let weak_textfield = Arc::downgrade(&textfield); let weak_button = Arc::downgrade(&button_ref); - button_ref.set_callback(Box::new(move |gui_handler: &mut GuiHandler| { + button_ref.set_callback(Box::new(move |world: &mut World| { if let Some(textfield) = weak_textfield.upgrade() { if let Some(button) = weak_button.upgrade() { if let Some(text) = button.text()? { + let gui_handler = world.resources.get_mut::<GuiHandler>(); textfield.add_letter(gui_handler, text.as_bytes()[0] as char)?; } } @@ -347,21 +360,22 @@ impl Keyboard { } impl TopGui for Keyboard { - fn decline(&self, gui_handler: &mut GuiHandler) -> Result<()> { + fn decline(&self, world: &mut World) -> Result<()> { if let Some(callback) = self.decline_callback.read().unwrap().as_ref() { - (callback)(gui_handler)?; + (callback)(world)?; } Ok(()) } - fn next_tab(&self, _gui_handler: &mut GuiHandler, _: bool) -> Result<()> { + fn next_tab(&self, _world: &mut World, _: bool) -> Result<()> { Ok(()) } - fn previous_tab(&self, gui_handler: &mut GuiHandler, second_level: bool) -> Result<()> { + fn previous_tab(&self, world: &mut World, second_level: bool) -> Result<()> { // abuse event if !second_level { + let gui_handler = world.resources.get_mut::<GuiHandler>(); self.text_field.remove_last(gui_handler)?; } @@ -449,21 +463,22 @@ impl TopLevelGui for Keyboard { impl Functionality for Keyboard { fn set_click_callbacks( &self, - callbacks: Vec<( - &str, - Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>, - )>, + callbacks: Vec<(&str, Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>)>, ) -> Result<()> { - for (name, callback) in callbacks { - if name == "decline" { - *self.decline_callback.write().unwrap() = Some(callback); - } + if let Some((_, callback)) = callbacks.into_iter().find(|(name, _)| *name == "decline") { + *self.decline_callback.write().unwrap() = Some(callback); } Ok(()) } - fn set_select_callbacks(&self, _: Vec<(&str, Box<CustomCallback<bool, ()>>)>) -> Result<()> { + fn set_select_callbacks( + &self, + _: Vec<( + &str, + Box<dyn Fn(&mut World, bool) -> Result<()> + Send + Sync>, + )>, + ) -> Result<()> { Ok(()) } @@ -471,7 +486,7 @@ impl Functionality for Keyboard { &self, _: Vec<( &str, - Box<dyn Fn(ControllerButton) -> Result<bool> + Send + Sync>, + Box<dyn Fn(&mut World, ControllerButton) -> Result<bool> + Send + Sync>, )>, ) -> Result<()> { Ok(()) @@ -479,7 +494,10 @@ impl Functionality for Keyboard { fn set_vec_callbacks( &self, - callbacks: Vec<(&str, Box<dyn Fn(&dyn Any) -> Result<()> + Send + Sync>)>, + callbacks: Vec<( + &str, + Box<dyn Fn(&mut World, &dyn Any) -> Result<()> + Send + Sync>, + )>, ) -> Result<()> { for (name, callback) in callbacks { if name == "accept" { diff --git a/src/lib.rs b/src/lib.rs index 72e4a27..f91f018 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,5 +13,3 @@ mod element_creator; mod gui_direction; mod mouse_button; pub mod prelude; - -pub type CustomCallback<I, O> = dyn Fn(I) -> anyhow::Result<O> + Send + Sync; diff --git a/src/prelude.rs b/src/prelude.rs index fcc0468..5ec1c36 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -9,5 +9,3 @@ pub use super::keyboard::Keyboard; pub use super::mouse_button::MouseButton; pub use super::states::*; pub use super::tab_control::TabControl; - -pub use super::CustomCallback; diff --git a/src/states.rs b/src/states.rs index 9762944..3b3ae0b 100644 --- a/src/states.rs +++ b/src/states.rs @@ -1,18 +1,20 @@ use crate::prelude::*; use anyhow::Result; use assetpath::AssetPath; +use ecs::World; +use utilities::prelude::remove_life_time_mut; use std::any::Any; use std::collections::HashMap; use std::sync::{Arc, Mutex, RwLock, Weak}; -pub trait FutureStateChange: Fn(&mut GuiHandler) -> Result<()> + Send + Sync { +pub trait FutureStateChange: Fn(&mut World) -> Result<()> + Send + Sync { fn clone_box<'a>(&self) -> Box<dyn 'a + FutureStateChange> where Self: 'a; - fn as_fn(&self) -> &(dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync) + fn as_fn(&self) -> &(dyn Fn(&mut World) -> Result<()> + Send + Sync) where Self: Sized, { @@ -21,7 +23,7 @@ pub trait FutureStateChange: Fn(&mut GuiHandler) -> Result<()> + Send + Sync { fn as_static( &'static self, - ) -> &'static (dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync + 'static) + ) -> &'static (dyn Fn(&mut World) -> Result<()> + Send + Sync + 'static) where Self: Sized, { @@ -29,7 +31,7 @@ pub trait FutureStateChange: Fn(&mut GuiHandler) -> Result<()> + Send + Sync { } } -impl<F: Fn(&mut GuiHandler) -> Result<()> + Clone + Send + Sync> FutureStateChange for F { +impl<F: Fn(&mut World) -> Result<()> + Clone + Send + Sync> FutureStateChange for F { fn clone_box<'a>(&self) -> Box<dyn 'a + FutureStateChange> where Self: 'a, @@ -49,12 +51,12 @@ struct State { top_level_gui: Arc<dyn TopLevelGui>, - on_activate: RwLock<Option<Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>>>, - on_deactivate: RwLock<Option<Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>>>, + on_activate: RwLock<Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>>, + on_deactivate: RwLock<Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>>, - next_tab: RwLock<Option<Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>>>, - previous_tab: RwLock<Option<Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>>>, - decline: RwLock<Option<Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>>>, + next_tab: RwLock<Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>>, + previous_tab: RwLock<Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>>, + decline: RwLock<Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>>, } /// Opaque handle for a State @@ -150,36 +152,46 @@ impl GetElement<ProgressBar> for StateHandle { /// Update type pub enum StateUpdateType<'a> { /// Updates the callback which is executed on next tab event - NextTab(Option<Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>>), + NextTab(Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>), /// Updates the callback which is executed on previous tab event - PreviousTab(Option<Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>>), + PreviousTab(Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>), /// Updates the callback which is executed on decline event - Decline(Option<Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>>), + Decline(Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>), /// Updates callbacks by identifier - ClickCallbacks( + ClickCallbacks(Vec<(&'a str, Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>)>), + + /// Updates callbacks by identifier + SelectCallbacks( Vec<( &'a str, - Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>, + Box<dyn Fn(&mut World, bool) -> Result<()> + Send + Sync>, )>, ), /// Updates callbacks by identifier - SelectCallbacks(Vec<(&'a str, Box<CustomCallback<bool, ()>>)>), - - /// Updates callbacks by identifier - CustomClickCallbacks(Vec<(&'a str, Box<CustomCallback<ControllerButton, bool>>)>), + CustomClickCallbacks( + Vec<( + &'a str, + Box<dyn Fn(&mut World, ControllerButton) -> Result<bool> + Send + Sync>, + )>, + ), /// Updates callbacks by identifier with input parameters - VecCallbacks(Vec<(&'a str, Box<dyn Fn(&dyn Any) -> Result<()> + Send + Sync>)>), + VecCallbacks( + Vec<( + &'a str, + Box<dyn Fn(&mut World, &dyn Any) -> Result<()> + Send + Sync>, + )>, + ), /// Updates the callback which is executed when this state gets activated on state change - OnActivate(Option<Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>>), + OnActivate(Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>), /// Updates the callback which is executed when this state gets deactivated on state change - OnDeactivate(Option<Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>>), + OnDeactivate(Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>), } /// Type of the state to be created @@ -274,14 +286,13 @@ impl States { /// # Arguments /// /// * `id` - Set state with the given identifier or None - pub fn set_state<'b>( - &self, - gui_handler: &mut GuiHandler, - id: impl Into<Option<&'b str>>, - ) -> Result<()> { + pub fn set_state<'b>(&self, world: &mut World, id: impl Into<Option<&'b str>>) -> Result<()> { + let gui_handler = unsafe { remove_life_time_mut(world.resources.get_mut::<GuiHandler>()) }; + Self::_set_state( id.into().map(|id| self.get_state(id)).transpose()?, &mut *self.current_state.lock().unwrap(), + world, gui_handler, self.log_state_change, ) @@ -290,6 +301,7 @@ impl States { fn _set_state( state: Option<Arc<State>>, current: &mut Option<Arc<State>>, + world: &mut World, gui_handler: &mut GuiHandler, logging: bool, ) -> Result<()> { @@ -302,13 +314,13 @@ impl States { } // execute deactivate on old state - old_state.deactivate(gui_handler)?; + old_state.deactivate(world, gui_handler)?; } // set new state, either no state or requested state match state { Some(state) => { - state.activate(gui_handler)?; + state.activate(world, gui_handler)?; gui_handler.set_top_gui(Some(state.clone())); if logging { @@ -354,11 +366,15 @@ impl States { let weak_current_state = Arc::downgrade(&self.current_state); let logging = self.log_state_change; - Ok(Box::new(move |gui_handler: &mut GuiHandler| { + Ok(Box::new(move |world: &mut World| { if let Some(current) = weak_current_state.upgrade() { + let gui_handler = + unsafe { remove_life_time_mut(world.resources.get_mut::<GuiHandler>()) }; + Self::_set_state( weak_state.as_ref().map(|w| w.upgrade()).flatten(), &mut *current.lock().unwrap(), + world, gui_handler, logging, )?; @@ -429,21 +445,21 @@ impl State { } } - fn activate(&self, gui_handler: &mut GuiHandler) -> Result<()> { + fn activate(&self, world: &mut World, gui_handler: &mut GuiHandler) -> Result<()> { self.top_level_gui.enable(gui_handler)?; if let Some(activate) = self.on_activate.read().unwrap().as_ref() { - (activate)(gui_handler)?; + (activate)(world)?; } Ok(()) } - fn deactivate(&self, gui_handler: &mut GuiHandler) -> Result<()> { + fn deactivate(&self, world: &mut World, gui_handler: &mut GuiHandler) -> Result<()> { self.top_level_gui.disable(gui_handler)?; if let Some(deactivate) = self.on_deactivate.read().unwrap().as_ref() { - (deactivate)(gui_handler)?; + (deactivate)(world)?; } Ok(()) @@ -451,49 +467,43 @@ impl State { fn set_on_activate( &self, - on_activate: Option<Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>>, + on_activate: Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>, ) { *self.on_activate.write().unwrap() = on_activate; } fn set_on_deactivate( &self, - on_deactivate: Option<Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>>, + on_deactivate: Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>, ) { *self.on_deactivate.write().unwrap() = on_deactivate; } fn set_previous_tab( &self, - previous_tab: Option<Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>>, + previous_tab: Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>, ) { *self.previous_tab.write().unwrap() = previous_tab; } - fn set_next_tab( - &self, - next_tab: Option<Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>>, - ) { + fn set_next_tab(&self, next_tab: Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>) { *self.next_tab.write().unwrap() = next_tab; } - fn set_decline( - &self, - decline: Option<Box<dyn Fn(&mut GuiHandler) -> Result<()> + Send + Sync>>, - ) { + fn set_decline(&self, decline: Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>) { *self.decline.write().unwrap() = decline; } } impl TopGui for State { - fn decline(&self, gui_handler: &mut GuiHandler) -> Result<()> { + fn decline(&self, world: &mut World) -> Result<()> { match self.decline.read().unwrap().as_ref() { Some(decline) => { - (decline)(gui_handler)?; + (decline)(world)?; } None => { if let Some(top_gui) = self.top_level_gui.top_gui() { - top_gui.decline(gui_handler)?; + top_gui.decline(world)?; } } } @@ -501,14 +511,14 @@ impl TopGui for State { Ok(()) } - fn next_tab(&self, gui_handler: &mut GuiHandler, second_level: bool) -> Result<()> { + fn next_tab(&self, world: &mut World, second_level: bool) -> Result<()> { match self.next_tab.read().unwrap().as_ref() { Some(next_tab) => { - (next_tab)(gui_handler)?; + (next_tab)(world)?; } None => { if let Some(top_gui) = self.top_level_gui.top_gui() { - top_gui.next_tab(gui_handler, second_level)?; + top_gui.next_tab(world, second_level)?; } } } @@ -516,14 +526,14 @@ impl TopGui for State { Ok(()) } - fn previous_tab(&self, gui_handler: &mut GuiHandler, second_level: bool) -> Result<()> { + fn previous_tab(&self, world: &mut World, second_level: bool) -> Result<()> { match self.previous_tab.read().unwrap().as_ref() { Some(previous_tab) => { - (previous_tab)(gui_handler)?; + (previous_tab)(world)?; } None => { if let Some(top_gui) = self.top_level_gui.top_gui() { - top_gui.previous_tab(gui_handler, second_level)?; + top_gui.previous_tab(world, second_level)?; } } }