308 lines
9.1 KiB
Rust
308 lines
9.1 KiB
Rust
|
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),
|
||
|
}
|
||
|
}
|
||
|
}
|