gooey/interface/controller/component/
switch.rs

1use strum::EnumCount;
2use crate::Application;
3use crate::tree::NodeId;
4use crate::interface::view;
5use crate::interface::controller::{self, controls};
6use super::impl_kind;
7
8// TODO: do we really need separate enter/exit actions for both On/Off states?
9#[derive(Clone, Debug, Default)]
10pub struct Switch {
11  pub state       : State,
12  pub appearances : Appearances,
13  pub enter       : Triggers,
14  pub exit        : Triggers,
15  pub toggle      : bool
16  // TODO: trigger while
17  //pub while_ : [Option <Trigger>; State::COUNT]
18}
19impl_kind!(Switch);
20
21#[derive(Clone, Debug, Default, Eq, PartialEq, EnumCount)]
22pub enum State {
23  On,
24  #[default]
25  Off
26}
27
28/// (appearances, label) for each state
29#[derive(Clone, Debug, Default)]
30pub struct Appearances (
31  pub [(controller::Appearances, Option <String>); State::COUNT]);
32
33#[derive(Clone, Debug, Default)]
34pub struct Triggers (pub [Option <Trigger>; State::COUNT]);
35
36#[derive(Clone, Debug)]
37pub struct Trigger {
38  pub control_fun : controls::Fun <controls::button::Release>,
39  pub target      : Option <NodeId>
40}
41
42#[derive(Clone, Default)]
43pub struct Builder {
44  pub appearances : Appearances,
45  pub enter       : Triggers,
46  pub exit        : Triggers,
47  pub toggle      : bool
48}
49
50impl Switch {
51  #[inline]
52  pub fn enter (&self) -> &Option <Trigger> {
53    &self.enter.0[self.state.clone() as usize]
54  }
55  #[inline]
56  pub fn exit (&self) -> &Option <Trigger> {
57    &self.exit.0[self.state.clone() as usize]
58  }
59  #[inline]
60  pub fn appearances (&self) -> &controller::Appearances {
61    &self.appearances.0[self.state.clone() as usize].0
62  }
63  #[inline]
64  pub fn label (&self) -> &Option <String> {
65    &self.appearances.0[self.state.clone() as usize].1
66  }
67
68  /// Returns (exit, (enter, appearances, label))
69  #[inline]
70  pub fn on (&mut self) -> (
71    Option <Trigger>,
72    (Option <Trigger>, controller::Appearances, Option <String>)
73  ) {
74    if cfg!(debug_assertions) && self.state != State::Off {
75      log::debug!("switch on state not off: {:?}", self.state);
76    }
77    let from   = self.exit().clone();
78    self.state = State::On;
79    let to     =
80      (self.enter().clone(), self.appearances().clone(), self.label().clone());
81    (from, to)
82  }
83
84  /// Returns (exit, (enter, appearances, label))
85  #[inline]
86  pub fn off (&mut self) -> (
87    Option <Trigger>,
88    (Option <Trigger>, controller::Appearances, Option <String>)
89  ) {
90    if cfg!(debug_assertions) && self.state != State::On {
91      log::debug!("switch off state not on: {:?}", self.state);
92    }
93    let from   = self.exit().clone();
94    self.state = State::Off;
95    let to     =
96      (self.enter().clone(), self.appearances().clone(), self.label().clone());
97    (from, to)
98  }
99}
100
101impl Builder {
102  #[inline]
103  pub const fn appearance (mut self,
104    state      : (State, controller::State),
105    appearance : view::Appearance
106  ) -> Self {
107    *self.appearances.appearance_mut (state) = appearance;
108    self
109  }
110  /// Overrides labels set with `label_on`, `label_off`
111  #[inline]
112  pub fn label (mut self, label : String) -> Self {
113    for (_, l) in self.appearances.0.iter_mut() {
114      *l = Some (label.clone());
115    }
116    self
117  }
118  #[inline]
119  pub fn label_on (mut self, label : String) -> Self {
120    self.appearances.0[State::On as usize].1 = Some (label);
121    self
122  }
123  #[inline]
124  pub fn label_off (mut self, label : String) -> Self {
125    self.appearances.0[State::Off as usize].1 = Some (label);
126    self
127  }
128  #[inline]
129  pub const fn enter (mut self, state : State, trigger : Trigger) -> Self {
130    *self.enter.get_mut (state) = Some (trigger);
131    self
132  }
133  #[inline]
134  pub const fn exit (mut self, state : State, trigger : Trigger) -> Self {
135    *self.exit.get_mut (state) = Some (trigger);
136    self
137  }
138  #[inline]
139  pub const fn style (mut self,
140    state : (State, controller::State), style : view::Style
141  ) -> Self {
142    self.appearances.appearance_mut (state).style = Some(style);
143    self
144  }
145  #[inline]
146  pub fn style_default (mut self, state : (State, controller::State)) -> Self {
147    let style = Some (view::Style::default());
148    self.appearances.appearance_mut (state).style = style;
149    self
150  }
151  #[inline]
152  pub const fn sound (mut self,
153    state : (State, controller::State), sound : view::Sound
154  ) -> Self {
155    self.appearances.appearance_mut (state).sound = Some (sound);
156    self
157  }
158  #[inline]
159  pub fn style_fg (mut self,
160    state : (State, controller::State), color : view::Color
161  ) -> Self {
162    let style = &mut self.appearances.appearance_mut (state).style;
163    let mut s = style.take().unwrap_or_default();
164    s.fg      = color;
165    *style    = Some (s);
166    self
167  }
168  #[inline]
169  pub fn style_bg (mut self,
170    state : (State, controller::State), color : view::Color
171  ) -> Self {
172    let appearance   = &mut self.appearances.0[state.0 as usize]
173      .0.0[state.1 as usize];
174    let mut style    = appearance.style.take().unwrap_or_default();
175    style.bg         = color;
176    appearance.style = Some (style);
177    self
178  }
179  #[inline]
180  pub fn style_lo (mut self,
181    state : (State, controller::State), color : view::Color
182  ) -> Self {
183    let appearance   = &mut self.appearances.0[state.0 as usize]
184      .0.0[state.1 as usize];
185    let mut style    = appearance.style.take().unwrap_or_default();
186    style.lo         = color;
187    appearance.style = Some (style);
188    self
189  }
190  #[inline]
191  pub fn style_hi (mut self,
192    state : (State, controller::State), color : view::Color
193  ) -> Self {
194    let appearance   = &mut self.appearances.0[state.0 as usize]
195      .0.0[state.1 as usize];
196    let mut style    = appearance.style.take().unwrap_or_default();
197    style.hi         = color;
198    appearance.style = Some (style);
199    self
200  }
201  pub const fn toggle (mut self, toggle : bool) -> Self {
202    self.toggle = toggle;
203    self
204  }
205  #[inline]
206  pub fn build (self) -> Switch {
207    Switch {
208      appearances: self.appearances,
209      enter:       self.enter,
210      exit:        self.exit,
211      toggle:      self.toggle,
212      .. Switch::default()
213    }
214  }
215}
216
217impl Appearances {
218  #[inline]
219  pub const fn appearance (&self, state : (State, controller::State))
220    -> &view::Appearance
221  {
222    self.0[state.0 as usize].0.get (state.1)
223  }
224
225  #[inline]
226  pub const fn appearance_mut (&mut self, state : (State, controller::State))
227    -> &mut view::Appearance
228  {
229    self.0[state.0 as usize].0.get_mut (state.1)
230  }
231}
232
233impl Triggers {
234  #[inline]
235  pub const fn get (&self, state : State) -> &Option <Trigger> {
236    &self.0[state as usize]
237  }
238
239  #[inline]
240  pub const fn get_mut (&mut self, state : State) -> &mut Option <Trigger> {
241    &mut self.0[state as usize]
242  }
243}
244
245impl Trigger {
246  /// Creates a switch that triggers a control function when set to 'On'
247  #[inline]
248  pub fn new <A : Application>
249    (control : controls::Button, target : Option <NodeId>) -> Self
250  {
251    use controller::controls::Control;
252    let control_fun = control.fun::<A::ButtonControls>();
253    Trigger { control_fun, target }
254  }
255}
256