ui/src/states.rs

280 lines
7.9 KiB
Rust
Raw Normal View History

2023-01-16 09:53:52 +00:00
use crate::prelude::*;
use anyhow::Result;
use assetpath::AssetPath;
2025-03-04 17:18:08 +00:00
use ecs::World;
use utilities::prelude::remove_life_time_mut;
2023-01-16 09:53:52 +00:00
use std::any::Any;
use std::collections::HashMap;
2025-03-08 06:24:08 +00:00
use std::sync::{Arc, Mutex};
2023-01-16 09:53:52 +00:00
2025-03-04 17:18:08 +00:00
pub trait FutureStateChange: Fn(&mut World) -> Result<()> + Send + Sync {
2024-04-04 06:23:46 +00:00
fn clone_box<'a>(&self) -> Box<dyn 'a + FutureStateChange>
where
Self: 'a;
2024-04-11 11:35:44 +00:00
2025-03-04 17:18:08 +00:00
fn as_fn(&self) -> &(dyn Fn(&mut World) -> Result<()> + Send + Sync)
2024-04-11 11:35:44 +00:00
where
Self: Sized,
{
self
}
2024-04-16 08:19:58 +00:00
2025-03-04 13:25:35 +00:00
fn as_static(
&'static self,
2025-03-04 17:18:08 +00:00
) -> &'static (dyn Fn(&mut World) -> Result<()> + Send + Sync + 'static)
2024-04-16 08:19:58 +00:00
where
Self: Sized,
{
self
}
2024-04-04 06:23:46 +00:00
}
2025-03-04 17:18:08 +00:00
impl<F: Fn(&mut World) -> Result<()> + Clone + Send + Sync> FutureStateChange for F {
2024-04-04 06:23:46 +00:00
fn clone_box<'a>(&self) -> Box<dyn 'a + FutureStateChange>
where
Self: 'a,
{
Box::new(self.clone())
}
}
impl<'a> Clone for Box<dyn 'a + FutureStateChange> {
fn clone(&self) -> Self {
(**self).clone_box()
}
}
2023-01-16 09:53:52 +00:00
/// Update type
pub enum StateUpdateType<'a> {
/// Updates the callback which is executed on next tab event
2025-03-04 17:18:08 +00:00
NextTab(Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>),
2023-01-16 09:53:52 +00:00
/// Updates the callback which is executed on previous tab event
2025-03-04 17:18:08 +00:00
PreviousTab(Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>),
2023-01-16 09:53:52 +00:00
/// Updates the callback which is executed on decline event
2025-03-04 17:18:08 +00:00
Decline(Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>),
2023-01-16 09:53:52 +00:00
/// Updates callbacks by identifier
2025-03-04 17:18:08 +00:00
ClickCallbacks(Vec<(&'a str, Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>)>),
/// Updates callbacks by identifier
SelectCallbacks(
2025-03-04 13:25:35 +00:00
Vec<(
&'a str,
2025-03-04 17:18:08 +00:00
Box<dyn Fn(&mut World, bool) -> Result<()> + Send + Sync>,
2025-03-04 13:25:35 +00:00
)>,
),
2023-01-16 09:53:52 +00:00
/// Updates callbacks by identifier
2025-03-04 17:18:08 +00:00
CustomClickCallbacks(
Vec<(
&'a str,
Box<dyn Fn(&mut World, ControllerButton) -> Result<bool> + Send + Sync>,
)>,
),
2023-01-16 09:53:52 +00:00
/// Updates callbacks by identifier with input parameters
2025-03-04 17:18:08 +00:00
VecCallbacks(
Vec<(
&'a str,
Box<dyn Fn(&mut World, &dyn Any) -> Result<()> + Send + Sync>,
)>,
),
2023-01-16 09:53:52 +00:00
/// Updates the callback which is executed when this state gets activated on state change
2025-03-04 17:18:08 +00:00
OnActivate(Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>),
2023-01-16 09:53:52 +00:00
/// Updates the callback which is executed when this state gets deactivated on state change
2025-03-04 17:18:08 +00:00
OnDeactivate(Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>),
2023-01-16 09:53:52 +00:00
}
/// Type of the state to be created
pub enum CreationType<'a> {
/// Path to an xml-gui file, that state is created internally
File(&'a AssetPath),
/// A Arc<> that is already created and implements `TopLevelGui`
TopGui(Arc<dyn TopLevelGui>),
}
impl<'a> From<&'a AssetPath> for CreationType<'a> {
fn from(s: &'a AssetPath) -> Self {
Self::File(s)
}
}
impl<'a, T: TopLevelGui + 'static> From<Arc<T>> for CreationType<'a> {
fn from(top_level_gui: Arc<T>) -> Self {
Self::TopGui(top_level_gui)
}
}
impl<'a, T: TopLevelGui + 'static> From<T> for CreationType<'a> {
fn from(top_level_gui: T) -> Self {
Self::TopGui(Arc::new(top_level_gui))
}
}
/// Collection and handler for your UI (basically a state machine)
pub struct States {
2024-03-27 12:02:53 +00:00
states: HashMap<String, Arc<State>>,
2024-03-27 12:49:22 +00:00
current_state: Arc<Mutex<Option<Arc<State>>>>,
2023-01-16 09:53:52 +00:00
2024-03-27 12:49:22 +00:00
control_top_gui: bool,
log_state_change: bool,
2023-01-16 09:53:52 +00:00
}
impl States {
/// Creates an empty States struct
2025-03-04 13:25:35 +00:00
pub fn new() -> Result<Self> {
2023-01-16 09:53:52 +00:00
Ok(States {
2024-03-27 12:02:53 +00:00
states: HashMap::new(),
2024-03-27 12:49:22 +00:00
current_state: Arc::new(Mutex::new(None)),
2023-01-16 09:53:52 +00:00
2024-03-27 12:49:22 +00:00
control_top_gui: true,
log_state_change: false,
2023-01-16 09:53:52 +00:00
})
}
2024-03-27 12:49:22 +00:00
pub fn change_logging(mut self, logging: bool) -> Self {
self.log_state_change = logging;
self
2023-01-16 09:53:52 +00:00
}
/// Sets a flag if states should set and unset top gui of the gui handler
2024-03-27 12:49:22 +00:00
pub fn set_control_top_gui(mut self, control_top_gui: bool) -> Self {
self.control_top_gui = control_top_gui;
self
2023-01-16 09:53:52 +00:00
}
2024-08-26 09:46:43 +00:00
/// lists available states
pub fn states(&self) -> impl Iterator<Item = &str> {
self.states.keys().map(|s| s.as_str())
}
2023-01-16 09:53:52 +00:00
/// Adds a single state
pub fn add_state<'a>(
2024-03-27 12:02:53 +00:00
&mut self,
2025-03-05 06:59:38 +00:00
world: &mut World,
2023-01-16 09:53:52 +00:00
id: &str,
creation_type: impl Into<CreationType<'a>>,
) -> Result<()> {
2025-03-05 06:59:38 +00:00
self.states
.insert(id.to_string(), State::new(world, id, creation_type.into())?);
2023-01-16 09:53:52 +00:00
Ok(())
}
/// Returns the name of the current state if possible
pub fn current_state(&self) -> Result<Option<String>> {
match self.current_state.lock().unwrap().as_ref() {
Some(state) => Ok(Some(state.name.clone())),
None => Ok(None),
}
}
/// Changes the state
///
/// # Arguments
///
/// * `id` - Set state with the given identifier or None
2025-03-04 17:18:08 +00:00
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>()) };
2024-03-27 12:49:22 +00:00
Self::_set_state(
id.into().map(|id| self.get_state(id)).transpose()?,
&mut *self.current_state.lock().unwrap(),
2025-03-04 17:18:08 +00:00
world,
2025-03-04 13:25:35 +00:00
gui_handler,
2024-03-27 12:49:22 +00:00
self.log_state_change,
)
}
2023-01-16 09:53:52 +00:00
2024-03-27 12:49:22 +00:00
fn _set_state(
state: Option<Arc<State>>,
current: &mut Option<Arc<State>>,
2025-03-04 17:18:08 +00:00
world: &mut World,
2025-03-04 13:25:35 +00:00
gui_handler: &mut GuiHandler,
2024-03-27 12:49:22 +00:00
logging: bool,
) -> Result<()> {
if let Some(old_state) = current {
2023-01-16 09:53:52 +00:00
// check if requested state is already in use
2024-03-27 12:49:22 +00:00
if let Some(new_state) = &state {
if new_state.name == old_state.name {
2023-01-16 09:53:52 +00:00
return Ok(());
}
}
// execute deactivate on old state
2025-03-08 06:24:08 +00:00
old_state.disable(world)?;
2023-01-16 09:53:52 +00:00
}
// set new state, either no state or requested state
2024-03-27 12:49:22 +00:00
match state {
Some(state) => {
2025-03-08 06:24:08 +00:00
state.enable(world)?;
gui_handler.set_top_gui(world, Some(state.clone()))?;
2023-01-16 09:53:52 +00:00
2024-03-27 12:49:22 +00:00
if logging {
println!("Change UI State to {}", state.name);
2023-01-16 09:53:52 +00:00
}
2024-03-27 12:49:22 +00:00
*current = Some(state);
2023-01-16 09:53:52 +00:00
}
None => {
2025-03-08 06:24:08 +00:00
gui_handler.set_top_gui(world, None)?;
2023-01-16 09:53:52 +00:00
2024-03-27 12:49:22 +00:00
if logging {
2023-01-16 09:53:52 +00:00
println!("Change UI State to None");
}
2024-03-27 12:49:22 +00:00
*current = None;
2023-01-16 09:53:52 +00:00
}
}
Ok(())
}
/// Retrieve a StateHandle
pub fn state_handle(&self, id: &str) -> Result<StateHandle> {
2025-03-08 06:24:08 +00:00
Ok(self.get_state(id)?.into())
2023-01-16 09:53:52 +00:00
}
fn get_state(&self, id: &str) -> Result<Arc<State>> {
2024-03-27 12:02:53 +00:00
match self.states.get(id) {
2023-01-16 09:53:52 +00:00
Some(state) => Ok(state.clone()),
None => Err(anyhow::Error::msg(format!("UiState not found: {}", id))),
}
}
2024-03-27 12:49:22 +00:00
pub fn future_state_change<'b>(
&self,
id: impl Into<Option<&'b str>>,
2024-04-04 06:23:46 +00:00
) -> Result<Box<dyn FutureStateChange>> {
2024-04-04 12:58:54 +00:00
let state: Option<Arc<State>> = id.into().map(|id| self.get_state(id)).transpose()?;
let weak_state = state.map(|s| Arc::downgrade(&s));
let weak_current_state = Arc::downgrade(&self.current_state);
2024-03-27 12:49:22 +00:00
let logging = self.log_state_change;
2025-03-04 17:18:08 +00:00
Ok(Box::new(move |world: &mut World| {
if let Some(current) = weak_current_state.upgrade() {
2025-03-04 17:18:08 +00:00
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(),
2025-03-04 17:18:08 +00:00
world,
2025-03-04 13:25:35 +00:00
gui_handler,
logging,
)?;
}
Ok(())
2024-04-04 06:23:46 +00:00
}))
2024-03-27 12:49:22 +00:00
}
2023-01-16 09:53:52 +00:00
}