ui/src/state.rs

308 lines
9.1 KiB
Rust
Raw Normal View History

2025-03-08 06:24:08 +00:00
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),
}
}
}