gooey-rs 0.12.1

Tile-based UI library with audio support
Documentation
use strum::EnumCount;
use crate::Application;
use crate::tree::NodeId;
use crate::interface::view;
use crate::interface::controller::{self, controls};
use super::impl_kind;

// TODO: do we really need separate enter/exit actions for both On/Off states?
#[derive(Clone, Debug, Default)]
pub struct Switch {
  pub state       : State,
  pub appearances : Appearances,
  pub enter       : Triggers,
  pub exit        : Triggers,
  pub toggle      : bool
  // TODO: trigger while
  //pub while_ : [Option <Trigger>; State::COUNT]
}
impl_kind!(Switch);

#[derive(Clone, Debug, Default, Eq, PartialEq, EnumCount)]
pub enum State {
  On,
  #[default]
  Off
}

/// (appearances, label) for each state
#[derive(Clone, Debug, Default)]
pub struct Appearances (pub [(controller::Appearances, Option <String>); State::COUNT]);

#[derive(Clone, Debug, Default)]
pub struct Triggers (pub [Option <Trigger>; State::COUNT]);

#[derive(Clone, Debug)]
pub struct Trigger {
  pub control_fun : controls::Fun <controls::button::Release>,
  pub target      : Option <NodeId>
}

#[derive(Clone, Default)]
pub struct Builder {
  pub appearances : Appearances,
  pub enter       : Triggers,
  pub exit        : Triggers,
  pub toggle      : bool
}

impl Switch {
  #[inline]
  pub fn enter (&self) -> &Option <Trigger> {
    &self.enter.0[self.state.clone() as usize]
  }
  #[inline]
  pub fn exit (&self) -> &Option <Trigger> {
    &self.exit.0[self.state.clone() as usize]
  }
  #[inline]
  pub fn appearances (&self) -> &controller::Appearances {
    &self.appearances.0[self.state.clone() as usize].0
  }
  #[inline]
  pub fn label (&self) -> &Option <String> {
    &self.appearances.0[self.state.clone() as usize].1
  }

  /// Returns (exit, (enter, appearances, label))
  #[inline]
  pub fn on (&mut self) -> (
    Option <Trigger>,
    (Option <Trigger>, controller::Appearances, Option <String>)
  ) {
    if cfg!(debug_assertions) && self.state != State::Off {
      log::debug!("switch on state not off: {:?}", self.state);
    }
    let from = self.exit().clone();
    self.state = State::On;
    let to = (self.enter().clone(), self.appearances().clone(), self.label().clone());
    (from, to)
  }

  /// Returns (exit, (enter, appearances, label))
  #[inline]
  pub fn off (&mut self) -> (
    Option <Trigger>,
    (Option <Trigger>, controller::Appearances, Option <String>)
  ) {
    if cfg!(debug_assertions) && self.state != State::On {
      log::debug!("switch off state not on: {:?}", self.state);
    }
    let from = self.exit().clone();
    self.state = State::Off;
    let to = (self.enter().clone(), self.appearances().clone(), self.label().clone());
    (from, to)
  }
}

impl Builder {
  #[inline]
  pub const fn appearance (mut self,
    state      : (State, controller::State),
    appearance : view::Appearance
  ) -> Self {
    *self.appearances.appearance_mut (state) = appearance;
    self
  }
  /// Overrides labels set with `label_on`, `label_off`
  #[inline]
  pub fn label (mut self, label : String) -> Self {
    for (_, l) in self.appearances.0.iter_mut() {
      *l = Some (label.clone());
    }
    self
  }
  #[inline]
  pub fn label_on (mut self, label : String) -> Self {
    self.appearances.0[State::On as usize].1 = Some (label);
    self
  }
  #[inline]
  pub fn label_off (mut self, label : String) -> Self {
    self.appearances.0[State::Off as usize].1 = Some (label);
    self
  }
  #[inline]
  pub const fn enter (mut self, state : State, trigger : Trigger) -> Self {
    *self.enter.get_mut (state) = Some (trigger);
    self
  }
  #[inline]
  pub const fn exit (mut self, state : State, trigger : Trigger) -> Self {
    *self.exit.get_mut (state) = Some (trigger);
    self
  }
  #[inline]
  pub const fn style (mut self, state : (State, controller::State), style : view::Style)
    -> Self
  {
    self.appearances.appearance_mut (state).style = Some(style);
    self
  }
  #[inline]
  pub fn style_default (mut self, state : (State, controller::State)) -> Self {
    let style = Some (view::Style::default());
    self.appearances.appearance_mut (state).style = style;
    self
  }
  #[inline]
  pub const fn sound (mut self, state : (State, controller::State), sound : view::Sound)
    -> Self
  {
    self.appearances.appearance_mut (state).sound = Some (sound);
    self
  }
  #[inline]
  pub fn style_fg (mut self, state : (State, controller::State), color : view::Color)
    -> Self
  {
    let style = &mut self.appearances.appearance_mut (state).style;
    let mut s = style.take().unwrap_or_default();
    s.fg      = color;
    *style    = Some (s);
    self
  }
  #[inline]
  pub fn style_bg (mut self, state : (State, controller::State), color : view::Color)
    -> Self
  {
    let appearance   = &mut self.appearances.0[state.0 as usize].0.0[state.1 as usize];
    let mut style    = appearance.style.take().unwrap_or_default();
    style.bg         = color;
    appearance.style = Some (style);
    self
  }
  #[inline]
  pub fn style_lo (mut self, state : (State, controller::State), color : view::Color)
    -> Self
  {
    let appearance   = &mut self.appearances.0[state.0 as usize].0.0[state.1 as usize];
    let mut style    = appearance.style.take().unwrap_or_default();
    style.lo         = color;
    appearance.style = Some (style);
    self
  }
  #[inline]
  pub fn style_hi (mut self, state : (State, controller::State), color : view::Color)
    -> Self
  {
    let appearance   = &mut self.appearances.0[state.0 as usize].0.0[state.1 as usize];
    let mut style    = appearance.style.take().unwrap_or_default();
    style.hi         = color;
    appearance.style = Some (style);
    self
  }
  pub const fn toggle (mut self, toggle : bool) -> Self {
    self.toggle = toggle;
    self
  }
  #[inline]
  pub fn build (self) -> Switch {
    Switch {
      appearances: self.appearances,
      enter:       self.enter,
      exit:        self.exit,
      toggle:      self.toggle,
      .. Switch::default()
    }
  }
}

impl Appearances {
  #[inline]
  pub const fn appearance (&self, state : (State, controller::State))
    -> &view::Appearance
  {
    self.0[state.0 as usize].0.get (state.1)
  }

  #[inline]
  pub const fn appearance_mut (&mut self, state : (State, controller::State))
    -> &mut view::Appearance
  {
    self.0[state.0 as usize].0.get_mut (state.1)
  }
}

impl Triggers {
  #[inline]
  pub const fn get (&self, state : State) -> &Option <Trigger> {
    &self.0[state as usize]
  }

  #[inline]
  pub const fn get_mut (&mut self, state : State) -> &mut Option <Trigger> {
    &mut self.0[state as usize]
  }
}

impl Trigger {
  /// Creates a switch that triggers a control function when set to 'On'
  #[inline]
  pub fn new <A : Application> (control : controls::Button, target : Option <NodeId>)
    -> Self
  {
    use controller::controls::Control;
    let control_fun = control.fun::<A::ButtonControls>();
    Trigger { control_fun, target }
  }
}