Skip to main content

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  pub(crate) 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)]
112pub(crate) struct 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.as_ref() {
284              any = true;
285              Some (control)
286            } else if let Ok (index) = self.input_map.buttons
287              .binary_search_by_key (&button, |(b, _)| b)
288            {
289              // NOTE: input::Button has special PartialEq implementation where
290              // Modifiers::ANY in either lhs or rhs means the buttons are considered
291              // equal if their input::button::Variant are equal
292              let (b, control) = &self.input_map.buttons[index];
293              if b.modifiers.contains (input::Modifiers::ANY) {
294                any = true;
295              }
296              Some (control)
297            } else {
298              None
299            };
300            #[expect(clippy::unnecessary_literal_unwrap)]
301            if let Some (control) = control {
302              let release = Some (RefCell::new (SmallVec::new()));
303              control.fun::<A::ButtonControls>().0
304                (&release, elements, node_id, action_buffer);
305              let controls = release.unwrap().into_inner();
306              if !controls.is_empty() {
307                let mut button = *button;
308                if any {
309                  button.modifiers.set (input::Modifiers::ANY, true);
310                }
311                button_release = Some (ButtonRelease::Insert (button, controls));
312              }
313            }
314          }
315          input::button::State::Released => {
316            let control = if let Ok (index) = self.input_map.release_buttons
317              .binary_search_by_key (&button, |(b, _)| b)
318            {
319              let (_, control) = &self.input_map.release_buttons[index];
320              Some (control)
321            } else {
322              None
323            };
324            if let Some (control) = control {
325              control.fun::<A::ButtonControls>().0
326                (&None, elements, node_id, action_buffer);
327              button_release =
328                Some (ButtonRelease::Remove (*button, node_id.clone()));
329            }
330          }
331        }
332      }
333      Input::Axis (axis) => {
334        if let Ok (index) = self.input_map.axes
335          .binary_search_by_key (&axis.axis, |(a, _)| *a)
336        {
337          let (_, control) = &self.input_map.axes[index];
338          control.fun::<A::AxisControls>().0
339            (&axis.value, elements, node_id, action_buffer)
340        }
341      }
342      Input::Motion (motion) => {
343        if let Some (control) = self.input_map.motion.as_ref() {
344          control.fun::<A::MotionControls>().0
345            (motion, elements, node_id, action_buffer)
346        }
347      }
348      Input::Pointer (pointer) => {
349        if let Some (control) = self.input_map.pointer.as_ref() {
350          control.fun::<A::PointerControls>().0
351            (pointer, elements, node_id, action_buffer)
352        }
353      }
354      Input::System (system) => {
355        if let Some (control) = self.input_map.system.as_ref() {
356          control.fun::<A::SystemControls>().0
357            (system, elements, node_id, action_buffer)
358        }
359      }
360      Input::Text (text) => {
361        if let Some (control) = self.input_map.text.as_ref() {
362          control.fun::<A::TextControls>().0
363            (text, elements, node_id, action_buffer)
364        }
365      }
366      Input::Wheel (wheel) => {
367        if let Some (control) = self.input_map.wheel.as_ref() {
368          control.fun::<A::WheelControls>().0
369            (wheel, elements, node_id, action_buffer)
370        }
371      }
372    }
373    log::trace!("...handle_input");
374    if action_buffer.is_empty() && button_release.is_none() {
375      Err (input)
376    } else {
377      Ok (button_release)
378    }
379  }
380
381  pub (crate) fn release_buttons (&mut self) -> Vec <controls::Button> {
382    self.input_map.release_buttons.drain (..).map (|(_, control)| control)
383      .collect()
384  }
385
386  pub (crate) fn release_button_insert (&mut self,
387    input : input::Button, control : controls::Button
388  ) {
389    if self.input_map.release_buttons.insert (input, control).is_some() {
390      log::debug!("button release control already exists: {:?}", (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().copied()
450      .map (|(button, control)| controls::button::Binding::new (control.into(), button))
451      .collect();
452    let any_button = self.button_any.map (Into::into);
453    let system     = self.system.map (Into::into);
454    let text       = self.text.map (Into::into);
455    let motion     = self.motion.map (Into::into);
456    let pointer    = self.pointer.map (Into::into);
457    Bindings { buttons, any_button, system, text, motion, pointer }
458  }
459
460  /// Add new `Bindings` to the `InputMap`.
461  ///
462  /// Panics if there is a conflict with current:
463  ///
464  /// ```should_panic
465  /// use gooey::application;
466  /// use gooey::interface::controller::{bindings, controls, Controller};
467  /// use gooey::interface::view::input;
468  /// let bindings = bindings::Builder::<application::Default>::new()
469  ///   .buttons (vec![
470  ///     ( controls::button::Builtin::FormSubmitCallback.into(),
471  ///       input::button::Keycode::Enter.into()
472  ///     ).into()
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().map (|binding| (binding.1, binding.0.0)));
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().map (|binding| (binding.1, binding.0.0)));
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()
567      .map (|binding| (binding.1, binding.0.0)));
568    let button_any = any_button.clone().map (Into::into);
569    let system     = system.clone().map (Into::into);
570    let text       = text.clone().map (Into::into);
571    let motion     = motion.clone().map (Into::into);
572    let pointer    = pointer.clone().map (Into::into);
573    InputMap {
574      buttons, button_any, system, text, motion, pointer, .. InputMap::default()
575    }
576  }
577}
578
579impl State {
580  /// Changes state to Focused and issues a warning if state was not Enabled
581  #[inline]
582  pub fn focus (&mut self) {
583    if self != &State::Enabled {
584      log::warn!("focus state not enabled: {self:?}");
585    }
586    debug_assert_eq!(self, &State::Enabled);
587    *self = State::Focused;
588  }
589  /// Changes state to Enabled and issues a warning if state was not Focused
590  #[inline]
591  pub fn defocus (&mut self) {
592    if self != &State::Focused {
593      log::warn!("defocus state not focused: {self:?}");
594    }
595    debug_assert_eq!(self, &State::Focused);
596    *self = State::Enabled;
597  }
598  /// Changes state to Enabled and issues a warning if state was not Disabled
599  #[inline]
600  pub fn enable (&mut self) {
601    if self != &State::Disabled {
602      log::warn!("enable state not disabled: {self:?}");
603    }
604    debug_assert_eq!(self, &State::Disabled);
605    *self = State::Enabled;
606  }
607  /// Changes state to Disabled and issues a warning if state was not Enabled
608  #[inline]
609  pub fn disable (&mut self) {
610    if self != &State::Enabled {
611      log::warn!("disable state not enabled: {self:?}");
612    }
613    debug_assert_eq!(self, &State::Enabled);
614    *self = State::Disabled;
615  }
616}
617
618
619impl Appearances {
620  #[inline]
621  pub const fn get (&self, state : State) -> &Appearance {
622    &self.0[state as usize]
623  }
624
625  #[inline]
626  pub const fn get_mut (&mut self, state : State) -> &mut Appearance {
627    &mut self.0[state as usize]
628  }
629}
630
631impl AppearancesBuilder {
632  pub fn transparent() -> Self {
633    AppearancesBuilder::default()
634      .style_fg (State::Focused,  Color::TRANSPARENT)
635      .style_bg (State::Focused,  Color::TRANSPARENT)
636      .style_fg (State::Enabled,  Color::TRANSPARENT)
637      .style_bg (State::Enabled,  Color::TRANSPARENT)
638      .style_fg (State::Disabled, Color::TRANSPARENT)
639      .style_bg (State::Disabled, Color::TRANSPARENT)
640  }
641
642  #[inline]
643  pub const fn state (mut self, state : State, appearance : Appearance) -> Self {
644    self.0[state as usize] = appearance;
645    self
646  }
647  #[inline]
648  pub const fn style (mut self, state : State, style : Style) -> Self {
649    self.0[state as usize].style = Some (style);
650    self
651  }
652  #[inline]
653  pub fn style_default (mut self, state : State) -> Self {
654    self.0[state as usize].style = Some (Style::default());
655    self
656  }
657  #[inline]
658  pub const fn sound (mut self, state : State, sound : Sound) -> Self {
659    self.0[state as usize].sound = Some (sound);
660    self
661  }
662  #[inline]
663  pub const fn pointer (mut self, state : State, pointer : Pointer) -> Self {
664    self.0[state as usize].pointer = Some (pointer);
665    self
666  }
667  #[inline]
668  pub fn style_fg (mut self, state : State, color : Color) -> Self {
669    let state = state as usize;
670    let mut style = self.0[state].style.take().unwrap_or_default();
671    style.fg = color;
672    self.0[state].style = Some (style);
673    self
674  }
675  #[inline]
676  pub fn style_bg (mut self, state : State, color : Color) -> Self {
677    let state = state as usize;
678    let mut style = self.0[state].style.take().unwrap_or_default();
679    style.bg = color;
680    self.0[state].style = Some (style);
681    self
682  }
683  #[inline]
684  pub fn style_lo (mut self, state : State, color : Color) -> Self {
685    let state = state as usize;
686    let mut style = self.0[state].style.take().unwrap_or_default();
687    style.lo = color;
688    self.0[state].style = Some (style);
689    self
690  }
691  #[inline]
692  pub fn style_hi (mut self, state : State, color : Color) -> Self {
693    let state = state as usize;
694    let mut style = self.0[state].style.take().unwrap_or_default();
695    style.hi = color;
696    self.0[state].style = Some (style);
697    self
698  }
699  #[inline]
700  pub const fn build (self) -> Appearances {
701    Appearances (self.0)
702  }
703}
704
705
706impl Orientation {
707  pub const fn toggle (self) -> Self {
708    match self {
709      Orientation::Horizontal => Orientation::Vertical,
710      Orientation::Vertical   => Orientation::Horizontal
711    }
712  }
713}
714