gooey/interface/controller/
mod.rs

1//! Interaction components
2
3use std::cell::RefCell;
4use std::convert::TryFrom;
5use std::iter::FromIterator;
6use log;
7use bitflags::bitflags;
8use key_vec::KeyVec;
9use smallvec::SmallVec;
10use strum::EnumCount;
11
12use crate::prelude::*;
13
14// control trait and types
15pub mod controls;
16pub use self::controls::Controls;
17// serializable bindings and builder
18pub mod bindings;
19pub use self::bindings::Bindings;
20// controller components
21pub mod component;
22pub use self::component::Component;
23// data types
24pub mod alignment;
25pub mod offset;
26pub mod size;
27pub use self::alignment::Alignment;
28pub use self::offset::Offset;
29pub use self::size::Size;
30
31/// A component that holds control bindings and interaction state.
32///
33/// Controllers handle the translation of `view::Input` events to interface
34/// `Action`s via `Control` bindings.
35// TODO: builder pattern? because input_map is private, we can't use .. syntax
36#[derive(Clone, Debug, Default)]
37pub struct Controller {
38  pub component   : Component,
39  /// Defines behavior and appearance
40  pub state       : State,
41  /// View appearance selection for each state
42  pub appearances : Appearances,
43  /// Controls whether the node will be moved to the last sibling position
44  /// ("top") when processed by a `Focus` action (either as the target node or
45  /// an ancestor of the target node).
46  ///
47  /// Defaults to `false`. Note that some widget builders may set this to true
48  /// unless overridden (e.g. free frame widgets).
49  pub focus_top   : bool,
50  /// Controls whether unhandled input is bubbled up to parent: when true
51  /// unhandled input will be trapped (not bubbled), when false unhandled input
52  /// will be passed to the parent node.
53  ///
54  /// Defaults to `InputMask::empty()`
55  pub bubble_trap : InputMask,
56  /// Defines mappings from inputs to controls
57  input_map       : InputMap
58}
59
60/// Determines appearance and behavior
61#[derive(Clone, Debug, Default, Eq, PartialEq, EnumCount)]
62pub enum State {
63  #[default]
64  Enabled,
65  Focused,
66  Disabled
67}
68
69#[derive(Clone, Debug, Default, Eq, PartialEq)]
70pub enum Area {
71  /// Specifies the area inside any border (default)
72  #[default]
73  Interior,
74  /// Specifies the total area including any border
75  Exterior
76}
77
78/// Flow of child content
79#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
80pub enum Orientation {
81  #[default]
82  Horizontal,
83  Vertical
84}
85
86/// Selection of an appearance for each state
87#[derive(Clone, Debug, Default, Eq, PartialEq)]
88pub struct Appearances (pub [Appearance; State::COUNT]);
89#[derive(Default)]
90pub struct AppearancesBuilder ([Appearance; State::COUNT]);
91
92bitflags! {
93  #[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
94  pub struct InputMask : u8 {
95    const AXIS    = 0b0000_0001;
96    const BUTTON  = 0b0000_0010;
97    const MOTION  = 0b0000_0100;
98    const POINTER = 0b0000_1000;
99    const SYSTEM  = 0b0001_0000;
100    const TEXT    = 0b0010_0000;
101    const WHEEL   = 0b0100_0000;
102  }
103}
104
105/// Concrete control bindings for all input types.
106///
107/// Given an input event, if a corresponding control is not contained in this
108/// struct, the input will either be trapped (discarded) or else passed up to
109/// the parent node (depending on the state of the Controller `bubble_trap`
110/// flag).
111#[derive(Clone, Debug, Default, Eq, PartialEq)]
112struct InputMap {
113  pub button_any      : Option <controls::Button>,
114  pub buttons         : KeyVec <input::Button, controls::Button>,
115  pub release_buttons : KeyVec <input::Button, controls::Button>,
116  pub axes            : KeyVec <u32, controls::Axis>,
117  pub motion          : Option <controls::Motion>,
118  pub pointer         : Option <controls::Pointer>,
119  pub system          : Option <controls::System>,
120  pub text            : Option <controls::Text>,
121  pub wheel           : Option <controls::Wheel>
122}
123
124/// An input result for registering or removing button release controls.
125///
126/// This is optionally returned by `controller.handle_input()` when a button release
127/// control needs to be registered or removed from the focused node.
128#[derive(Debug)]
129pub (crate) enum ButtonRelease {
130  Insert (input::Button, SmallVec <[(controls::Button, NodeId); 1]>),
131  Remove (input::Button, NodeId)
132}
133
134/// Allows focus directed to a parent frame to be redirected to a different node:
135///
136/// - Fields
137/// - Menus: if the first child of the focused Frame is a Menu (Selection) node, focus
138///   will be redirected to the appropriate item node
139pub (crate) fn refocus (node_id : &NodeId, elements : &Tree <Element>)
140  -> Option <NodeId>
141{
142  let node    = elements.get (node_id).unwrap();
143  let element = node.data();
144  if element.controller.state != State::Focused {
145    log::warn!("refocus state not focused: {:?}", element.controller.state);
146    debug_assert!(false);
147  }
148  let mut refocus_id = None;
149  // frame
150  if Frame::try_from (element).is_ok() {
151    let mut children_ids = node.children().iter();
152    // first child
153    if let Some (child_id) = children_ids.next() {
154      let child = elements.get_element (child_id);
155      if Field::try_from (child).is_ok() || Numbox::try_from (child).is_ok() {
156        refocus_id = Some (child_id.clone());
157      } else if let Ok (Widget (selection, _, _)) = Menu::try_from (child) {
158        // refocus menu selection
159        refocus_id = selection.current.clone()
160          .or_else (|| menu::find_first_item (elements, children_ids));
161      }
162    }
163  }
164  refocus_id
165}
166
167impl Controller {
168  #[inline]
169  pub fn with_bindings <A : Application> (bindings : &Bindings <A>) -> Self {
170    Controller { input_map: bindings.into(), .. Controller::default() }
171  }
172
173  #[inline]
174  pub fn get_appearance (&self) -> &Appearance {
175    self.appearances.get (self.state.clone())
176  }
177
178  #[inline]
179  pub fn get_bindings <A : Application> (&self) -> Bindings <A> {
180    self.input_map.to_bindings()
181  }
182
183  /// Replaces all existing bindings
184  #[inline]
185  pub fn set_bindings <A : Application> (&mut self, bindings : &Bindings <A>) {
186    self.clear_bindings();
187    self.add_bindings (bindings);
188  }
189
190  /// Must be new bindings or else panics
191  #[inline]
192  pub fn add_bindings <A : Application> (&mut self, bindings : &Bindings <A>) {
193    self.input_map.add_bindings (bindings)
194  }
195
196  /// Replaces existing bindings; should not fail
197  #[inline]
198  pub fn insert_bindings <A : Application> (&mut self, bindings : &Bindings <A>) {
199    self.input_map.insert_bindings (bindings)
200  }
201
202  /// Remove matching controls
203  #[inline]
204  pub fn remove_bindings (&mut self, controls : &Controls) {
205    self.input_map.remove_bindings (controls)
206  }
207
208  #[inline]
209  pub fn clear_buttons (&mut self) {
210    self.input_map.buttons.clear()
211  }
212
213  #[inline]
214  pub const fn remove_any_button (&mut self) {
215    self.input_map.button_any = None
216  }
217
218  #[inline]
219  pub const fn remove_system (&mut self) {
220    self.input_map.system = None
221  }
222
223  #[inline]
224  pub const fn remove_text (&mut self) {
225    self.input_map.text = None
226  }
227
228  #[inline]
229  pub const fn remove_motion (&mut self) {
230    self.input_map.motion = None
231  }
232
233  #[inline]
234  pub const fn remove_pointer (&mut self) {
235    self.input_map.pointer = None
236  }
237
238  #[inline]
239  pub fn clear_bindings (&mut self) {
240    self.input_map.clear()
241  }
242
243  pub (crate) fn handle_input <A : Application> (&self,
244    input         : Input,
245    elements      : &Tree <Element>,
246    node_id       : &NodeId,
247    action_buffer : &mut Vec <(NodeId, Action)>
248  ) -> Result <Option <ButtonRelease>, Input> {
249    use controls::Control;
250    log::trace!("handle_input...");
251    let mut button_release = None;
252    match &input {
253      Input::Button (button, state) => {
254        if let Component::Cursor (cursor) = &self.component &&
255          let input::button::Variant::Keycode (keycode) = button.variant &&
256          !button.modifiers.intersects (
257            input::Modifiers::ALT | input::Modifiers::CTRL | input::Modifiers::SUPER)
258        {
259          if cursor.ignore.contains (&keycode) {
260            // if the cursor is configured to ignore this keycode, allow the input to
261            // bubble
262            return Err (input)
263          } else if keycode.is_printable() {
264            // if the keycode is printible, consume the input and the cursor will handle
265            // the text input
266            return Ok (None)
267          }
268        }
269        match state {
270          input::button::State::Pressed => {
271            // if the button matched was an any key or has Modifiers::ANY set, we need
272            // to set the Modifier::ANY flag if a release control is bound so that it
273            // will still be matched if modifiers change
274            let mut any = false;
275            // in backends that don't report key repeat events (Winit), if there is a
276            // release event bound to the button then assumes the press was a repeat and
277            // ignore
278            if self.input_map.release_buttons
279              .binary_search_by_key (&button, |(b, _)| b).is_ok()
280            {
281              return Ok (None)
282            }
283            let control = if let Some (control) = self.input_map.button_any
284              .as_ref()
285            {
286              any = true;
287              Some (control.clone())
288            } else if let Ok (index) = self.input_map.buttons
289              .binary_search_by_key (&button, |(b, _)| b)
290            {
291              let (b, control) = &self.input_map.buttons[index];
292              if b.modifiers.contains (input::Modifiers::ANY) {
293                any = true;
294              }
295              Some (control.clone())
296            } else {
297              None
298            };
299            #[expect(clippy::unnecessary_literal_unwrap)]
300            if let Some (control) = control {
301              let release = Some (RefCell::new (SmallVec::new()));
302              control.fun::<A::ButtonControls>().0
303                (&release, elements, node_id, action_buffer);
304              let controls = release.unwrap().into_inner();
305              if !controls.is_empty() {
306                let mut button = *button;
307                if any {
308                  button.modifiers.set (input::Modifiers::ANY, true);
309                }
310                button_release = Some (ButtonRelease::Insert (button, controls));
311              }
312            }
313          }
314          input::button::State::Released => {
315            let control = if let Ok (index) = self.input_map.release_buttons
316              .binary_search_by_key (&button, |(b, _)| b)
317            {
318              let (_, control) = &self.input_map.release_buttons[index];
319              Some (control.clone())
320            } else {
321              None
322            };
323            if let Some (control) = control {
324              control.fun::<A::ButtonControls>().0
325                (&None, elements, node_id, action_buffer);
326              button_release =
327                Some (ButtonRelease::Remove (*button, node_id.clone()));
328            }
329          }
330        }
331      }
332      Input::Axis (axis) => {
333        if let Ok (index) = self.input_map.axes
334          .binary_search_by_key (&axis.axis, |(a, _)| *a)
335        {
336          let (_, control) = &self.input_map.axes[index];
337          control.clone().fun::<A::AxisControls>().0
338            (&axis.value, elements, node_id, action_buffer)
339        }
340      }
341      Input::Motion (motion) => {
342        if let Some (control) = self.input_map.motion.as_ref() {
343          control.clone().fun::<A::MotionControls>().0
344            (motion, elements, node_id, action_buffer)
345        }
346      }
347      Input::Pointer (pointer) => {
348        if let Some (control) = self.input_map.pointer.as_ref() {
349          control.clone().fun::<A::PointerControls>().0
350            (pointer, elements, node_id, action_buffer)
351        }
352      }
353      Input::System (system) => {
354        if let Some (control) = self.input_map.system.as_ref() {
355          control.clone().fun::<A::SystemControls>().0
356            (system, elements, node_id, action_buffer)
357        }
358      }
359      Input::Text (text) => {
360        if let Some (control) = self.input_map.text.as_ref() {
361          control.clone().fun::<A::TextControls>().0
362            (text, elements, node_id, action_buffer)
363        }
364      }
365      Input::Wheel (wheel) => {
366        if let Some (control) = self.input_map.wheel.as_ref() {
367          control.clone().fun::<A::WheelControls>().0
368            (wheel, elements, node_id, action_buffer)
369        }
370      }
371    }
372    log::trace!("...handle_input");
373    if action_buffer.is_empty() && button_release.is_none() {
374      Err (input)
375    } else {
376      Ok (button_release)
377    }
378  }
379
380  pub (crate) fn release_buttons (&mut self) -> Vec <controls::Button> {
381    self.input_map.release_buttons.drain (..).map (|(_, control)| control)
382      .collect()
383  }
384
385  pub (crate) fn release_button_insert (&mut self,
386    input : input::Button, control : controls::Button
387  ) {
388    if self.input_map.release_buttons.insert (input, control.clone()).is_some() {
389      log::debug!("button release control already exists: {:?}",
390        (input, control));
391    }
392  }
393
394  pub (crate) fn release_button_remove (&mut self, input : input::Button) {
395    match self.input_map.release_buttons
396      .binary_search_by_key (&&input, |(b, _)| b)
397    {
398      Ok  (index) => {
399        let _ = self.input_map.release_buttons.remove_index (index);
400      }
401      Err (_) => {
402        log::warn!("remove release button not present: {input:?}");
403        debug_assert!(false);
404      }
405    }
406  }
407
408  pub (crate) fn update_view_focus (&self, view : &mut View) {
409    view.appearance = self.get_appearance().clone();
410    if let view::Component::Body (body) = &mut view.component &&
411      let Component::Cursor (cursor) = &self.component
412    {
413      match self.state {
414        State::Focused  => {
415          let caret = char::try_from (cursor.caret).unwrap();
416          body.0.push (caret);
417        }
418        State::Enabled  => {
419          let _ = body.0.pop().unwrap();
420        }
421        State::Disabled => {}
422      }
423    }
424  }
425}
426
427impl From <Component> for Controller {
428  fn from (component : Component) -> Self {
429    Controller { component, .. Controller::default() }
430  }
431}
432
433impl From<&Input> for InputMask {
434  fn from (input : &Input) -> Self {
435    match input {
436      Input::Axis   (_)    => InputMask::AXIS,
437      Input::Button (_, _) => InputMask::BUTTON,
438      Input::Motion (_)    => InputMask::MOTION,
439      Input::Pointer(_)    => InputMask::POINTER,
440      Input::System (_)    => InputMask::SYSTEM,
441      Input::Text   (_)    => InputMask::TEXT,
442      Input::Wheel  (_)    => InputMask::WHEEL
443    }
444  }
445}
446
447impl InputMap {
448  pub(crate) fn to_bindings <A : Application> (&self) -> Bindings <A> {
449    let buttons    = self.buttons.iter().cloned().map (Into::into).collect();
450    let any_button = self.button_any.clone().map (Into::into);
451    let system     = self.system.clone().map (Into::into);
452    let text       = self.text.clone().map (Into::into);
453    let motion     = self.motion.clone().map (Into::into);
454    let pointer    = self.pointer.clone().map (Into::into);
455    Bindings { buttons, any_button, system, text, motion, pointer }
456  }
457
458  /// Add new `Bindings` to the `InputMap`.
459  ///
460  /// Panics if there is a conflict with current:
461  ///
462  /// ```should_panic
463  /// use gooey::application;
464  /// use gooey::interface::controller::{bindings, controls, Controller};
465  /// use gooey::interface::view::input;
466  /// let bindings = bindings::Builder::<application::Default>::new()
467  ///   .buttons (vec![
468  ///     controls::button::Binding (
469  ///       controls::button::Builtin::FormSubmitCallback.into(),
470  ///       input::button::Keycode::Enter.into(),
471  ///       Default::default()
472  ///     )
473  ///   ])
474  ///   .build();
475  /// let mut controller = Controller::with_bindings(&bindings);
476  /// controller.add_bindings (&bindings);  // panic! duplicate bindings
477  /// ```
478  pub(crate) fn add_bindings <A : Application> (&mut self, bindings : &Bindings <A>) {
479    let Bindings { buttons, any_button, system, text, motion, pointer } = bindings;
480    // buttons
481    let buttons_len  = self.buttons.len();
482    let bindings_len = buttons.len();
483    self.buttons.extend (buttons.iter().cloned().map (Into::into));
484    assert_eq!(self.buttons.len(), buttons_len + bindings_len);
485    // any button
486    any_button.clone().map (|button| {
487      assert!(self.button_any.is_none());
488      self.button_any = Some (button.into());
489    });
490    // system
491    system.clone().map (|system| {
492      assert!(self.system.is_none());
493      self.system = Some (system.into());
494    });
495    // text
496    text.clone().map (|text| {
497      assert!(self.text.is_none());
498      self.text = Some (text.into());
499    });
500    // motion
501    motion.clone().map (|motion| {
502      assert!(self.motion.is_none());
503      self.motion = Some (motion.into());
504    });
505    // pointer
506    pointer.clone().map (|pointer| {
507      assert!(self.pointer.is_none());
508      self.pointer = Some (pointer.into());
509    });
510  }
511
512  /// Insert Bindings to the `InputMap`, replacing any existing `Bindings`
513  pub(crate) fn insert_bindings <A : Application> (&mut self, bindings : &Bindings <A>) {
514    let Bindings { buttons, any_button, system, text, motion, pointer } = bindings;
515    // buttons
516    self.buttons.extend (buttons.iter().cloned().map (Into::into));
517    // any button
518    any_button.clone().map (|button| self.button_any = Some (button.into()));
519    // system
520    system.clone().map (|system| self.system = Some (system.into()));
521    // text
522    text.clone().map (|text| self.text = Some (text.into()));
523    // motion
524    motion.clone().map (|motion| self.motion = Some (motion.into()));
525    // pointer
526    pointer.clone().map (|pointer| self.pointer = Some (pointer.into()));
527  }
528
529  /// Remove matching controls
530  pub(crate) fn remove_bindings (&mut self, controls : &Controls) {
531    let Controls { buttons, any_button, system, text, motion, pointer } = controls;
532    // buttons
533    self.buttons.retain (|(_, button)| !buttons.contains (button));
534    // any button
535    if &self.button_any == any_button {
536      self.button_any = None;
537    }
538    // system
539    if &self.system == system {
540      self.system = None;
541    }
542    // text
543    if &self.text == text {
544      self.text = None;
545    }
546    // motion
547    if &self.motion == motion {
548      self.motion = None;
549    }
550    // pointer
551    if &self.pointer == pointer {
552      self.pointer = None;
553    }
554  }
555
556  #[inline]
557  pub(crate) fn clear (&mut self) {
558    *self = InputMap::default()
559  }
560}
561
562impl <A : Application> From <&Bindings <A>> for InputMap {
563  /// Constructs an `InputMap` with the given `Bindings`
564  fn from (bindings : &Bindings <A>) -> Self {
565    let Bindings { buttons, any_button, system, text, motion, pointer } = bindings;
566    let buttons    = KeyVec::from_iter (buttons.iter().cloned().map (Into::into));
567    let button_any = any_button.clone().map (Into::into);
568    let system     = system.clone().map (Into::into);
569    let text       = text.clone().map (Into::into);
570    let motion     = motion.clone().map (Into::into);
571    let pointer    = pointer.clone().map (Into::into);
572    InputMap {
573      buttons, button_any, system, text, motion, pointer, .. InputMap::default()
574    }
575  }
576}
577
578impl State {
579  /// Changes state to Focused and issues a warning if state was not Enabled
580  #[inline]
581  pub fn focus (&mut self) {
582    if self != &State::Enabled {
583      log::warn!("focus state not enabled: {self:?}");
584    }
585    debug_assert_eq!(self, &State::Enabled);
586    *self = State::Focused;
587  }
588  /// Changes state to Enabled and issues a warning if state was not Focused
589  #[inline]
590  pub fn defocus (&mut self) {
591    if self != &State::Focused {
592      log::warn!("defocus state not focused: {self:?}");
593    }
594    debug_assert_eq!(self, &State::Focused);
595    *self = State::Enabled;
596  }
597  /// Changes state to Enabled and issues a warning if state was not Disabled
598  #[inline]
599  pub fn enable (&mut self) {
600    if self != &State::Disabled {
601      log::warn!("enable state not disabled: {self:?}");
602    }
603    debug_assert_eq!(self, &State::Disabled);
604    *self = State::Enabled;
605  }
606  /// Changes state to Disabled and issues a warning if state was not Enabled
607  #[inline]
608  pub fn disable (&mut self) {
609    if self != &State::Enabled {
610      log::warn!("disable state not enabled: {self:?}");
611    }
612    debug_assert_eq!(self, &State::Enabled);
613    *self = State::Disabled;
614  }
615}
616
617
618impl Appearances {
619  #[inline]
620  pub const fn get (&self, state : State) -> &Appearance {
621    &self.0[state as usize]
622  }
623
624  #[inline]
625  pub const fn get_mut (&mut self, state : State) -> &mut Appearance {
626    &mut self.0[state as usize]
627  }
628}
629
630impl AppearancesBuilder {
631  pub fn transparent() -> Self {
632    AppearancesBuilder::default()
633      .style_fg (State::Focused,  color::TRANSPARENT.into())
634      .style_bg (State::Focused,  color::TRANSPARENT.into())
635      .style_fg (State::Enabled,  color::TRANSPARENT.into())
636      .style_bg (State::Enabled,  color::TRANSPARENT.into())
637      .style_fg (State::Disabled, color::TRANSPARENT.into())
638      .style_bg (State::Disabled, color::TRANSPARENT.into())
639  }
640
641  #[inline]
642  pub const fn state (mut self, state : State, appearance : Appearance) -> Self {
643    self.0[state as usize] = appearance;
644    self
645  }
646  #[inline]
647  pub const fn style (mut self, state : State, style : Style) -> Self {
648    self.0[state as usize].style = Some (style);
649    self
650  }
651  #[inline]
652  pub fn style_default (mut self, state : State) -> Self {
653    self.0[state as usize].style = Some (Style::default());
654    self
655  }
656  #[inline]
657  pub const fn sound (mut self, state : State, sound : Sound) -> Self {
658    self.0[state as usize].sound = Some (sound);
659    self
660  }
661  #[inline]
662  pub const fn pointer (mut self, state : State, pointer : Pointer) -> Self {
663    self.0[state as usize].pointer = Some (pointer);
664    self
665  }
666  #[inline]
667  pub fn style_fg (mut self, state : State, color : Color) -> Self {
668    let state = state as usize;
669    let mut style = self.0[state].style.take().unwrap_or_default();
670    style.fg = color;
671    self.0[state].style = Some (style);
672    self
673  }
674  #[inline]
675  pub fn style_bg (mut self, state : State, color : Color) -> Self {
676    let state = state as usize;
677    let mut style = self.0[state].style.take().unwrap_or_default();
678    style.bg = color;
679    self.0[state].style = Some (style);
680    self
681  }
682  #[inline]
683  pub fn style_lo (mut self, state : State, color : Color) -> Self {
684    let state = state as usize;
685    let mut style = self.0[state].style.take().unwrap_or_default();
686    style.lo = color;
687    self.0[state].style = Some (style);
688    self
689  }
690  #[inline]
691  pub fn style_hi (mut self, state : State, color : Color) -> Self {
692    let state = state as usize;
693    let mut style = self.0[state].style.take().unwrap_or_default();
694    style.hi = color;
695    self.0[state].style = Some (style);
696    self
697  }
698  #[inline]
699  pub const fn build (self) -> Appearances {
700    Appearances (self.0)
701  }
702}
703
704
705impl Orientation {
706  pub const fn toggle (self) -> Self {
707    match self {
708      Orientation::Horizontal => Orientation::Vertical,
709      Orientation::Vertical   => Orientation::Horizontal
710    }
711  }
712}
713