Compare commits

...

3 commits

Author SHA1 Message Date
9265d66418 Update Rust crate quick-xml to 0.37.0
Some checks failed
Gavania Merge Build / build (pull_request) Failing after 2m36s
2025-03-08 09:05:45 +00:00
68b564fa18 Round 2 2025-03-08 07:33:45 +01:00
1383cd5c4d Fix disable behavior 2025-03-08 07:24:08 +01:00
6 changed files with 376 additions and 285 deletions

View file

@ -5,7 +5,7 @@ authors = ["hodasemi <michaelh.95@t-online.de>"]
edition = "2024" edition = "2024"
[dependencies] [dependencies]
quick-xml = "0.31.0" quick-xml = "0.37.0"
serde = { version = "1.0.203", features = ["derive"] } serde = { version = "1.0.203", features = ["derive"] }
serde_json = { version = "1.0.120" } serde_json = { version = "1.0.120" }
paste = "1.0.15" paste = "1.0.15"

View file

@ -190,8 +190,8 @@ pub struct GuiHandler {
resource_base_path: Option<AssetPath>, resource_base_path: Option<AssetPath>,
top_ui: Option<Arc<dyn TopGui>>, top_ui: Option<Arc<dyn TopLevelGui>>,
tooltip_ui: Option<Arc<dyn TopGui>>, tooltip_ui: Option<Arc<dyn TopLevelGui>>,
render_targets: TargetMode<RenderTarget>, render_targets: TargetMode<RenderTarget>,
command_buffers: TargetMode<Vec<CommandBufferState>>, command_buffers: TargetMode<Vec<CommandBufferState>>,
@ -743,11 +743,13 @@ impl GuiHandler {
pub fn decline_topgui(&mut self, world: &mut World) -> Result<bool> { pub fn decline_topgui(&mut self, world: &mut World) -> Result<bool> {
// workaround for unwanted borrowing behaviour inside decline function // workaround for unwanted borrowing behaviour inside decline function
let opt_topgui = self.top_ui.as_ref().cloned(); let opt_top_level_gui = self.top_ui.as_ref().cloned();
if let Some(topgui) = opt_topgui { if let Some(top_level_gui) = opt_top_level_gui {
topgui.decline(world)?; if let Some(top_gui) = top_level_gui.top_gui() {
return Ok(true); top_gui.decline(world)?;
return Ok(true);
}
} }
Ok(false) Ok(false)
@ -755,11 +757,13 @@ impl GuiHandler {
pub fn next_tab_topgui(&mut self, world: &mut World, 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 // workaround for unwanted borrowing behaviour inside decline function
let opt_topgui = self.top_ui.as_ref().cloned(); let opt_top_level_gui = self.top_ui.as_ref().cloned();
if let Some(topgui) = opt_topgui { if let Some(top_level_gui) = opt_top_level_gui {
topgui.next_tab(world, second_level)?; if let Some(top_gui) = top_level_gui.top_gui() {
return Ok(true); top_gui.next_tab(world, second_level)?;
return Ok(true);
}
} }
Ok(false) Ok(false)
@ -767,11 +771,13 @@ impl GuiHandler {
pub fn previous_tab_topgui(&mut self, world: &mut World, 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 // workaround for unwanted borrowing behaviour inside decline function
let opt_topgui = self.top_ui.as_ref().cloned(); let opt_top_level_gui = self.top_ui.as_ref().cloned();
if let Some(topgui) = opt_topgui { if let Some(top_level_gui) = opt_top_level_gui {
topgui.previous_tab(world, second_level)?; if let Some(top_gui) = top_level_gui.top_gui() {
return Ok(true); top_gui.previous_tab(world, second_level)?;
return Ok(true);
}
} }
Ok(false) Ok(false)
@ -857,12 +863,50 @@ impl GuiHandler {
self.needs_update = true; self.needs_update = true;
} }
pub fn set_top_gui(&mut self, top_gui: Option<Arc<dyn TopGui>>) { pub fn set_top_gui(
&mut self,
world: &mut World,
top_gui: Option<Arc<dyn TopLevelGui>>,
) -> Result<()> {
match (&self.top_ui, &top_gui) {
(Some(current), Some(incoming)) => {
if !Arc::ptr_eq(current, incoming) {
current.disable(world)?;
}
}
(Some(current), None) => {
current.disable(world)?;
}
_ => (),
}
self.top_ui = top_gui; self.top_ui = top_gui;
Ok(())
} }
pub fn set_tooltip(&mut self, tooltip: Option<Arc<dyn TopGui>>) { pub fn set_tooltip(
&mut self,
world: &mut World,
tooltip: Option<Arc<dyn TopLevelGui>>,
) -> Result<()> {
match (&self.tooltip_ui, &tooltip) {
(Some(current), Some(incoming)) => {
if !Arc::ptr_eq(current, incoming) {
current.disable(world)?;
}
}
(Some(current), None) => {
current.disable(world)?;
}
_ => (),
}
self.tooltip_ui = tooltip; self.tooltip_ui = tooltip;
Ok(())
} }
pub(crate) fn add_callback<F: FnOnce(&mut World) -> Result<()> + Send + Sync + 'static>( pub(crate) fn add_callback<F: FnOnce(&mut World) -> Result<()> + Send + Sync + 'static>(

View file

@ -13,3 +13,4 @@ mod element_creator;
mod gui_direction; mod gui_direction;
mod mouse_button; mod mouse_button;
pub mod prelude; pub mod prelude;
pub mod state;

View file

@ -7,5 +7,6 @@ pub use super::gui_direction::GuiDirection;
pub use super::gui_handler::prelude::*; pub use super::gui_handler::prelude::*;
pub use super::keyboard::Keyboard; pub use super::keyboard::Keyboard;
pub use super::mouse_button::MouseButton; pub use super::mouse_button::MouseButton;
pub use super::state::*;
pub use super::states::*; pub use super::states::*;
pub use super::tab_control::TabControl; pub use super::tab_control::TabControl;

307
src/state.rs Normal file
View file

@ -0,0 +1,307 @@
use crate::prelude::*;
use anyhow::{Result, anyhow};
use ecs::World;
use std::collections::HashMap;
use std::sync::{Arc, RwLock, Weak};
/// Opaque handle for a State
/// only used for updating callbacks
#[derive(Clone)]
pub struct StateHandle {
state: Weak<State>,
}
impl StateHandle {
pub fn update<'a>(&self, update_type: StateUpdateType<'a>) -> Result<()> {
self.state.upgrade().unwrap().update(update_type)
}
}
impl GetElement<Button> for StateHandle {
fn element(&self, id: &str) -> Result<Arc<Button>> {
let state = self.state.upgrade().unwrap();
match state.top_level_gui.elements() {
Some(elements) => elements.element(id),
None => panic!("state ({}) has no elements", state.name),
}
}
}
impl GetElement<Grid> for StateHandle {
fn element(&self, id: &str) -> Result<Arc<Grid>> {
let state = self.state.upgrade().unwrap();
match state.top_level_gui.elements() {
Some(elements) => elements.element(id),
None => panic!("state ({}) has no elements", state.name),
}
}
}
impl GetElement<Label> for StateHandle {
fn element(&self, id: &str) -> Result<Arc<Label>> {
let state = self.state.upgrade().unwrap();
match state.top_level_gui.elements() {
Some(elements) => elements.element(id),
None => panic!("state ({}) has no elements", state.name),
}
}
}
impl GetElement<TextField> for StateHandle {
fn element(&self, id: &str) -> Result<Arc<TextField>> {
let state = self.state.upgrade().unwrap();
match state.top_level_gui.elements() {
Some(elements) => elements.element(id),
None => panic!("state ({}) has no elements", state.name),
}
}
}
impl GetElement<MultiLineTextField> for StateHandle {
fn element(&self, id: &str) -> Result<Arc<MultiLineTextField>> {
let state = self.state.upgrade().unwrap();
match state.top_level_gui.elements() {
Some(elements) => elements.element(id),
None => panic!("state ({}) has no elements", state.name),
}
}
}
impl GetElement<Icon> for StateHandle {
fn element(&self, id: &str) -> Result<Arc<Icon>> {
let state = self.state.upgrade().unwrap();
match state.top_level_gui.elements() {
Some(elements) => elements.element(id),
None => panic!("state ({}) has no elements", state.name),
}
}
}
impl GetElement<ProgressBar> for StateHandle {
fn element(&self, id: &str) -> Result<Arc<ProgressBar>> {
let state = self.state.upgrade().unwrap();
match state.top_level_gui.elements() {
Some(elements) => elements.element(id),
None => panic!("state ({}) has no elements", state.name),
}
}
}
pub(crate) struct State {
pub(crate) name: String,
pub(crate) top_level_gui: Arc<dyn TopLevelGui>,
pub(crate) on_activate: RwLock<Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>>,
pub(crate) on_deactivate: RwLock<Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>>,
pub(crate) next_tab: RwLock<Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>>,
pub(crate) previous_tab: RwLock<Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>>,
pub(crate) decline: RwLock<Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>>,
}
impl State {
pub(crate) fn new<'a>(
world: &mut World,
name: &str,
creation_type: CreationType<'a>,
) -> Result<Arc<Self>> {
let gui = match creation_type {
CreationType::File(path) => GuiBuilder::new(world, path)?,
CreationType::TopGui(top_gui) => top_gui,
};
Ok(Arc::new(State {
name: name.to_string(),
top_level_gui: gui,
on_activate: RwLock::new(None),
on_deactivate: RwLock::new(None),
next_tab: RwLock::new(None),
previous_tab: RwLock::new(None),
decline: RwLock::new(None),
}))
}
pub(crate) fn update<'a>(&self, update_type: StateUpdateType<'a>) -> Result<()> {
match update_type {
StateUpdateType::NextTab(next_tab) => self.set_next_tab(next_tab),
StateUpdateType::PreviousTab(previous_tab) => self.set_previous_tab(previous_tab),
StateUpdateType::Decline(decline) => self.set_decline(decline),
StateUpdateType::OnActivate(oa) => self.set_on_activate(oa),
StateUpdateType::OnDeactivate(oda) => self.set_on_deactivate(oda),
StateUpdateType::ClickCallbacks(cbs) => {
let functionality = self.functionality().ok_or(anyhow!(
"State ({}) has no functionality implementation",
self.name
))?;
functionality.set_click_callbacks(cbs)?;
}
StateUpdateType::SelectCallbacks(cbs) => {
let functionality = self.functionality().ok_or(anyhow!(
"State ({}) has no functionality implementation",
self.name
))?;
functionality.set_select_callbacks(cbs)?;
}
StateUpdateType::CustomClickCallbacks(cbs) => {
let functionality = self.functionality().ok_or(anyhow!(
"State ({}) has no functionality implementation",
self.name
))?;
functionality.set_custom_callbacks(cbs)?;
}
StateUpdateType::VecCallbacks(vcbs) => {
let functionality = self.functionality().ok_or(anyhow!(
"State ({}) has no functionality implementation",
self.name
))?;
functionality.set_vec_callbacks(vcbs)?;
}
}
Ok(())
}
pub(crate) fn set_on_activate(
&self,
on_activate: Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>,
) {
*self.on_activate.write().unwrap() = on_activate;
}
pub(crate) fn set_on_deactivate(
&self,
on_deactivate: Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>,
) {
*self.on_deactivate.write().unwrap() = on_deactivate;
}
pub(crate) fn set_previous_tab(
&self,
previous_tab: Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>,
) {
*self.previous_tab.write().unwrap() = previous_tab;
}
pub(crate) fn set_next_tab(
&self,
next_tab: Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>,
) {
*self.next_tab.write().unwrap() = next_tab;
}
pub(crate) 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, world: &mut World) -> Result<()> {
match self.decline.read().unwrap().as_ref() {
Some(decline) => {
(decline)(world)?;
}
None => {
if let Some(top_gui) = self.top_level_gui.top_gui() {
top_gui.decline(world)?;
}
}
}
Ok(())
}
fn next_tab(&self, world: &mut World, second_level: bool) -> Result<()> {
match self.next_tab.read().unwrap().as_ref() {
Some(next_tab) => {
(next_tab)(world)?;
}
None => {
if let Some(top_gui) = self.top_level_gui.top_gui() {
top_gui.next_tab(world, second_level)?;
}
}
}
Ok(())
}
fn previous_tab(&self, world: &mut World, second_level: bool) -> Result<()> {
match self.previous_tab.read().unwrap().as_ref() {
Some(previous_tab) => {
(previous_tab)(world)?;
}
None => {
if let Some(top_gui) = self.top_level_gui.top_gui() {
top_gui.previous_tab(world, second_level)?;
}
}
}
Ok(())
}
}
impl TopLevelGui for State {
fn gui_traits(&self) -> &dyn GuiElementTraits {
self.top_level_gui.gui_traits()
}
fn top_gui(&self) -> Option<&dyn TopGui> {
Some(self)
}
fn elements(&self) -> Option<&HashMap<String, UiElement>> {
self.top_level_gui.elements()
}
fn functionality(&self) -> Option<&dyn Functionality> {
self.top_level_gui.functionality()
}
fn enable(&self, world: &mut World) -> Result<()> {
self.top_level_gui.enable(world)?;
if let Some(activate) = self.on_activate.read().unwrap().as_ref() {
(activate)(world)?;
}
Ok(())
}
fn disable(&self, world: &mut World) -> Result<()> {
self.top_level_gui.disable(world)?;
if let Some(deactivate) = self.on_deactivate.read().unwrap().as_ref() {
(deactivate)(world)?;
}
Ok(())
}
}
impl Into<StateHandle> for Arc<State> {
fn into(self) -> StateHandle {
StateHandle {
state: Arc::downgrade(&self),
}
}
}

View file

@ -7,7 +7,7 @@ use utilities::prelude::remove_life_time_mut;
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, Mutex, RwLock, Weak}; use std::sync::{Arc, Mutex};
pub trait FutureStateChange: Fn(&mut World) -> Result<()> + Send + Sync { pub trait FutureStateChange: Fn(&mut World) -> Result<()> + Send + Sync {
fn clone_box<'a>(&self) -> Box<dyn 'a + FutureStateChange> fn clone_box<'a>(&self) -> Box<dyn 'a + FutureStateChange>
@ -46,109 +46,6 @@ impl<'a> Clone for Box<dyn 'a + FutureStateChange> {
} }
} }
struct State {
name: String,
top_level_gui: Arc<dyn TopLevelGui>,
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 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
/// only used for updating callbacks
#[derive(Clone)]
pub struct StateHandle {
state: Weak<State>,
}
impl StateHandle {
pub fn update<'a>(&self, update_type: StateUpdateType<'a>) -> Result<()> {
self.state.upgrade().unwrap().update(update_type)
}
}
impl GetElement<Button> for StateHandle {
fn element(&self, id: &str) -> Result<Arc<Button>> {
let state = self.state.upgrade().unwrap();
match state.top_level_gui.elements() {
Some(elements) => elements.element(id),
None => panic!("state ({}) has no elements", state.name),
}
}
}
impl GetElement<Grid> for StateHandle {
fn element(&self, id: &str) -> Result<Arc<Grid>> {
let state = self.state.upgrade().unwrap();
match state.top_level_gui.elements() {
Some(elements) => elements.element(id),
None => panic!("state ({}) has no elements", state.name),
}
}
}
impl GetElement<Label> for StateHandle {
fn element(&self, id: &str) -> Result<Arc<Label>> {
let state = self.state.upgrade().unwrap();
match state.top_level_gui.elements() {
Some(elements) => elements.element(id),
None => panic!("state ({}) has no elements", state.name),
}
}
}
impl GetElement<TextField> for StateHandle {
fn element(&self, id: &str) -> Result<Arc<TextField>> {
let state = self.state.upgrade().unwrap();
match state.top_level_gui.elements() {
Some(elements) => elements.element(id),
None => panic!("state ({}) has no elements", state.name),
}
}
}
impl GetElement<MultiLineTextField> for StateHandle {
fn element(&self, id: &str) -> Result<Arc<MultiLineTextField>> {
let state = self.state.upgrade().unwrap();
match state.top_level_gui.elements() {
Some(elements) => elements.element(id),
None => panic!("state ({}) has no elements", state.name),
}
}
}
impl GetElement<Icon> for StateHandle {
fn element(&self, id: &str) -> Result<Arc<Icon>> {
let state = self.state.upgrade().unwrap();
match state.top_level_gui.elements() {
Some(elements) => elements.element(id),
None => panic!("state ({}) has no elements", state.name),
}
}
}
impl GetElement<ProgressBar> for StateHandle {
fn element(&self, id: &str) -> Result<Arc<ProgressBar>> {
let state = self.state.upgrade().unwrap();
match state.top_level_gui.elements() {
Some(elements) => elements.element(id),
None => panic!("state ({}) has no elements", state.name),
}
}
}
/// Update type /// Update type
pub enum StateUpdateType<'a> { pub enum StateUpdateType<'a> {
/// Updates the callback which is executed on next tab event /// Updates the callback which is executed on next tab event
@ -312,14 +209,14 @@ impl States {
} }
// execute deactivate on old state // execute deactivate on old state
old_state.deactivate(world)?; old_state.disable(world)?;
} }
// set new state, either no state or requested state // set new state, either no state or requested state
match state { match state {
Some(state) => { Some(state) => {
state.activate(world)?; state.enable(world)?;
gui_handler.set_top_gui(Some(state.clone())); gui_handler.set_top_gui(world, Some(state.clone()))?;
if logging { if logging {
println!("Change UI State to {}", state.name); println!("Change UI State to {}", state.name);
@ -328,7 +225,7 @@ impl States {
*current = Some(state); *current = Some(state);
} }
None => { None => {
gui_handler.set_top_gui(None); gui_handler.set_top_gui(world, None)?;
if logging { if logging {
println!("Change UI State to None"); println!("Change UI State to None");
@ -343,9 +240,7 @@ impl States {
/// Retrieve a StateHandle /// Retrieve a StateHandle
pub fn state_handle(&self, id: &str) -> Result<StateHandle> { pub fn state_handle(&self, id: &str) -> Result<StateHandle> {
Ok(StateHandle { Ok(self.get_state(id)?.into())
state: Arc::downgrade(&self.get_state(id)?),
})
} }
fn get_state(&self, id: &str) -> Result<Arc<State>> { fn get_state(&self, id: &str) -> Result<Arc<State>> {
@ -382,160 +277,3 @@ impl States {
})) }))
} }
} }
impl State {
fn new<'a>(
world: &mut World,
name: &str,
creation_type: CreationType<'a>,
) -> Result<Arc<Self>> {
let gui = match creation_type {
CreationType::File(path) => GuiBuilder::new(world, path)?,
CreationType::TopGui(top_gui) => top_gui,
};
Ok(Arc::new(State {
name: name.to_string(),
top_level_gui: gui,
on_activate: RwLock::new(None),
on_deactivate: RwLock::new(None),
next_tab: RwLock::new(None),
previous_tab: RwLock::new(None),
decline: RwLock::new(None),
}))
}
fn update<'a>(&self, update_type: StateUpdateType<'a>) -> Result<()> {
match update_type {
StateUpdateType::NextTab(next_tab) => self.set_next_tab(next_tab),
StateUpdateType::PreviousTab(previous_tab) => self.set_previous_tab(previous_tab),
StateUpdateType::Decline(decline) => self.set_decline(decline),
StateUpdateType::OnActivate(oa) => self.set_on_activate(oa),
StateUpdateType::OnDeactivate(oda) => self.set_on_deactivate(oda),
StateUpdateType::ClickCallbacks(cbs) => {
let functionality = self.functionality()?;
functionality.set_click_callbacks(cbs)?;
}
StateUpdateType::SelectCallbacks(cbs) => {
let functionality = self.functionality()?;
functionality.set_select_callbacks(cbs)?;
}
StateUpdateType::CustomClickCallbacks(cbs) => {
let functionality = self.functionality()?;
functionality.set_custom_callbacks(cbs)?;
}
StateUpdateType::VecCallbacks(vcbs) => {
let functionality = self.functionality()?;
functionality.set_vec_callbacks(vcbs)?;
}
}
Ok(())
}
fn functionality(&self) -> Result<&dyn Functionality> {
match self.top_level_gui.functionality() {
Some(functionality) => Ok(functionality),
None => panic!("state {}' does not implement Functionality", &self.name),
}
}
fn activate(&self, world: &mut World) -> Result<()> {
self.top_level_gui.enable(world)?;
if let Some(activate) = self.on_activate.read().unwrap().as_ref() {
(activate)(world)?;
}
Ok(())
}
fn deactivate(&self, world: &mut World) -> Result<()> {
self.top_level_gui.disable(world)?;
if let Some(deactivate) = self.on_deactivate.read().unwrap().as_ref() {
(deactivate)(world)?;
}
Ok(())
}
fn set_on_activate(
&self,
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 World) -> Result<()> + Send + Sync>>,
) {
*self.on_deactivate.write().unwrap() = on_deactivate;
}
fn set_previous_tab(
&self,
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 World) -> Result<()> + Send + Sync>>) {
*self.next_tab.write().unwrap() = next_tab;
}
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, world: &mut World) -> Result<()> {
match self.decline.read().unwrap().as_ref() {
Some(decline) => {
(decline)(world)?;
}
None => {
if let Some(top_gui) = self.top_level_gui.top_gui() {
top_gui.decline(world)?;
}
}
}
Ok(())
}
fn next_tab(&self, world: &mut World, second_level: bool) -> Result<()> {
match self.next_tab.read().unwrap().as_ref() {
Some(next_tab) => {
(next_tab)(world)?;
}
None => {
if let Some(top_gui) = self.top_level_gui.top_gui() {
top_gui.next_tab(world, second_level)?;
}
}
}
Ok(())
}
fn previous_tab(&self, world: &mut World, second_level: bool) -> Result<()> {
match self.previous_tab.read().unwrap().as_ref() {
Some(previous_tab) => {
(previous_tab)(world)?;
}
None => {
if let Some(top_gui) = self.top_level_gui.top_gui() {
top_gui.previous_tab(world, second_level)?;
}
}
}
Ok(())
}
}