Skip to main content

gooey/interface/
mod.rs

1//! Interface layer.
2//!
3//! The `Interface` is the primary structure representing the user interface and
4//! contains all the data for the interface including the `Presentation`
5//! instance.
6//!
7//! The internal representation of the interface is a `Tree` of `Element` nodes.
8//! An `Element` is a combination of a `Model` component, a `View` component,
9//! and a `Controller` component. The kinds of components in `Elements` and
10//! their relations with parent/child nodes are contextualized in various
11//! `Widget` definitions.
12//!
13//! `Action` events define the internal language of the `Interface` layer and
14//! result from user `Input` events, or directly submitted from application
15//! code. Each `Action` is directed at a target `Element` node. `Action` events
16//! include creating/destroying/modifying `Element` nodes and their components,
17//! changing focus, or submitting callbacks.
18
19use std::convert::TryFrom;
20use std::sync::{Arc, LazyLock, RwLock};
21use key_vec::KeyVec;
22use log;
23use derive_more::{From, TryInto};
24
25use crate::prelude::*;
26
27pub mod controller;
28pub mod model;
29pub mod view;
30pub use self::controller::Controller;
31pub use self::model::Model;
32pub use self::view::View;
33
34/// Pointer position
35pub static POINTER : LazyLock <Arc <RwLock <input::Pointer>>> = LazyLock::new (||
36  Arc::new (RwLock::new (input::Pointer::default())));
37
38/// Parameterized interface represented by a tree of `Element`s
39#[derive(Debug)]
40pub struct Interface <A=application::Default, P=presentation::Headless> where
41  A : Application,
42  P : Presentation
43{
44  pub presentation : P,
45  elements         : Tree <Element>,
46  focused_id       : NodeId,
47  input_buffer     : Option <Vec <Input>>,
48  action_buffer    : Option <Vec <(NodeId, Action)>>,
49  display_buffer   : Vec <(NodeId, Display)>,
50  event_buffer     : Vec <(NodeId, Event)>,
51  _phantom         : std::marker::PhantomData <A>
52}
53
54/// An interface node consisting of one each of `Model`, `View`, and `Controller`
55/// components.
56///
57/// See `Widget`s for construction of contextualzed combinations of different
58/// components.
59#[derive(Clone, Debug)]
60pub struct Element {
61  pub name       : String,
62  pub controller : Controller,
63  pub model      : Model,
64  pub view       : View
65}
66
67/// An interface-level event
68#[derive(From, TryInto)]
69pub enum Action {
70  Create           (Tree <Element>, CreateOrder),
71  ModifyController (Box <dyn FnOnce (&mut Controller)>),
72  ModifyModel      (Box <dyn FnOnce (&mut Model)>),
73  ModifyView       (Box <dyn FnOnce (&mut View)>),
74  /// This will submit produce an event with the Model containing this callback ID, but
75  /// will *not* modify the current Model callback ID.
76  ///
77  /// See the Form widget control `FormSubmitCallback` for a control function that
78  /// submits a model with the currently set callback ID.
79  SubmitCallback   (application::CallbackId),
80  /// Change the view state from Enabled to Focused; it is a debug error if the view
81  /// state is not Enabled.
82  ///
83  /// If `top` is true, node will be moved to the last sibling position ("top"),
84  /// otherwise position relative to siblings will remain unchanged
85  Focus,
86  /// Change `view.state` from Disabled to Enabled; it is a debug error if the view
87  /// state is not Disabled
88  Enable,
89  /// Change the `view.state` from Enabled to Disabled; it is a debug error if the view
90  /// state is not Enabled
91  Disable,
92  /// Destroy the target element and children.
93  ///
94  /// If the node was focused, then focus is shifted to the parent. If it was the root
95  /// node that was focused, then this is always an error since the root node is always
96  /// focused as the common ancestor of all nodes.
97  Destroy,
98  /// Trigger any release buttons that exist in the current input map of the controller
99  /// of the target node.
100  ///
101  /// This can be used when focus is changing and input state should not be orphaned.
102  ReleaseButtons
103}
104
105/// Either append or prepend newly created element
106#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
107pub enum CreateOrder {
108  #[default]
109  Append,
110  Prepend,
111  NthSibling (u32)
112}
113
114impl <A, P> Interface <A, P> where
115  A : Application,
116  P : Presentation
117{
118  pub fn with_root (mut root : Element) -> Self {
119    log::trace!("with_root...");
120    root.focus();
121    let elements       = TreeBuilder::new().with_root (Node::new (root.clone())).build();
122    let focused_id     = elements.root_node_id().unwrap().clone();
123    let presentation   = P::with_root (root.view, focused_id.clone());
124    let input_buffer   = Some (vec![]);
125    let action_buffer  = Some (vec![]);
126    let display_buffer = vec![];
127    let event_buffer   = vec![];
128    log::trace!("...with_root");
129    Interface {
130      elements, focused_id, presentation, input_buffer, display_buffer, action_buffer,
131      event_buffer, _phantom: std::marker::PhantomData
132    }
133  }
134
135  /// Handle an application request.
136  ///
137  /// Does not update presentation, must call `display()` to refresh display.
138  #[inline]
139  #[must_use]
140  #[expect(mismatched_lifetime_syntaxes)]
141  pub fn action (&mut self, node_id : &NodeId, action : Action)
142    -> std::vec::Drain <(NodeId, Event)>
143  {
144    self.handle_action (action, node_id);
145    self.event_buffer.drain(..)
146  }
147
148  /// Handles a batch of an application requests.
149  ///
150  /// Does not update presentation, must call `display()` to refresh display.
151  #[inline]
152  #[must_use]
153  #[expect(mismatched_lifetime_syntaxes)]
154  pub fn actions (&mut self, actions : Vec <(NodeId, Action)>)
155    -> std::vec::Drain <(NodeId, Event)>
156  {
157    for (node_id, action) in actions.into_iter() {
158      self.handle_action (action, &node_id);
159    }
160    self.event_buffer.drain(..)
161  }
162
163  /// Get and handle user input
164  #[must_use]
165  #[expect(mismatched_lifetime_syntaxes)]
166  pub fn update (&mut self) -> std::vec::Drain <(NodeId, Event)> {
167    log::trace!("update...");
168    // handle input
169    let mut action_buffer = self.action_buffer.take().unwrap();
170    let mut input_buffer  = self.input_buffer.take().unwrap();
171    debug_assert!(action_buffer.is_empty());
172    debug_assert!(input_buffer.is_empty());
173    self.presentation.get_input (&mut input_buffer);
174    #[expect(clippy::iter_with_drain)]
175    for input in input_buffer.drain(..) {
176      log::trace!("input: {input:?}");
177      // update global POINTER
178      if let Input::Pointer (pointer) = &input {
179        *POINTER.write().unwrap() = pointer.clone()
180      }
181      let focused_id = self.focused_element_id().clone();
182      let focused = self.focused_element();
183      // TODO: possibly allow a node to bubble even if input was handled ?
184      let button_release = match focused.controller.handle_input::<A> (
185        input, &self.elements, &focused_id, &mut action_buffer
186      ) {
187        Ok  (result) => result,
188        Err (mut input) => {
189          // bubble up to ancestors
190          debug_assert!(action_buffer.is_empty());
191          let mut button_release = None;
192          let input_mask = (&input).into();
193          if !focused.controller.bubble_trap.intersects (input_mask) {
194            for ancestor_id in self.elements.ancestor_ids (&focused_id).unwrap() {
195              let ancestor = self.get_element (ancestor_id);
196              input = match ancestor.controller.handle_input::<A> (
197                input, &self.elements, ancestor_id, &mut action_buffer
198              ) {
199                Err (input) =>
200                  if ancestor.controller.bubble_trap.intersects (input_mask) {
201                    break
202                  } else {
203                    input
204                  }
205                Ok (result) => {
206                  button_release = result;
207                  break
208                }
209              };
210            }
211          }
212          button_release
213        }
214      };
215      // handle actions
216      for (node_id, action) in action_buffer.drain(..) {
217        self.handle_action (action, &node_id);
218      }
219      // TODO: this was originally before handling the actions so that if focus changes,
220      // the release button is inserted into the newly focused node; however if the
221      // button release payload is a remove, we may want that to be processed by the
222      // original node before focus changes
223      if let Some (button_release) = button_release {
224        match button_release {
225          ButtonRelease::Insert (input, controls) =>
226            for (control, node_id) in controls {
227              let element = self.get_element_mut (&node_id);
228              element.controller.release_button_insert (input, control);
229            }
230          ButtonRelease::Remove (input, node_id) => {
231            let element = self.get_element_mut (&node_id);
232            element.controller.release_button_remove (input);
233          }
234        }
235      }
236    }
237    debug_assert!(action_buffer.is_empty());
238    debug_assert!(input_buffer.is_empty());
239    self.action_buffer = Some (action_buffer);
240    self.input_buffer  = Some (input_buffer);
241
242    log::trace!("...update");
243    // return application events
244    self.event_buffer.drain(..)
245  }
246
247  /// Presentation update
248  #[inline]
249  pub fn display (&mut self) {
250    log::trace!("display...");
251    self.presentation.display_view (&self.elements, self.display_buffer.drain(..));
252    log::trace!("...display");
253  }
254
255  #[inline]
256  pub const fn elements (&self) -> &Tree <Element> {
257    &self.elements
258  }
259
260  #[inline]
261  pub fn root (&self) -> &Element {
262    self.root_node().data()
263  }
264
265  pub fn root_node (&self) -> &Node <Element> {
266    self.elements.get (self.root_id()).unwrap()
267  }
268
269  #[inline]
270  pub fn root_id (&self) -> &NodeId {
271    self.elements.root_node_id().unwrap()
272  }
273
274  #[inline]
275  pub fn get_element (&self, node_id : &NodeId) -> &Element {
276    self.elements.get_element (node_id)
277  }
278
279  #[inline]
280  pub fn get_element_mut (&mut self, node_id : &NodeId) -> &mut Element {
281    self.elements.get_element_mut (node_id)
282  }
283
284  #[inline]
285  pub const fn focused_element_id (&self) -> &NodeId {
286    &self.focused_id
287  }
288
289  #[inline]
290  pub fn focused_element (&self) -> &Element {
291    self.get_element (&self.focused_id)
292  }
293
294  #[inline]
295  pub fn focused_element_mut (&mut self) -> &mut Element {
296    let focused_id = self.focused_id.clone();
297    self.get_element_mut (&focused_id)
298  }
299
300  #[inline]
301  pub fn print_elements_tree (&self) {
302    let mut s = String::new();
303    self.elements.write_formatted (&mut s).unwrap();
304    println!("elements:\n{s}");
305  }
306
307  #[inline]
308  pub fn print_elements_tree_names (&self) {
309    let mut s = String::new();
310    self.elements.write_formatted_names (&mut s).unwrap();
311    println!("elements:\n{s}");
312  }
313
314  /// Return map of all keyboard buttons and their bindings in the current context
315  pub fn keyboard_map (&self)
316    -> KeyVec <input::Button, Option <controls::button::Control <A::ButtonControls>>>
317  {
318    use strum::IntoEnumIterator;
319    let mut out = KeyVec::new();
320    let focused = self.focused_element();
321    let focused_id = self.focused_element_id();
322    for modifier_bits in 0..16u8 {
323      let modifiers = input::Modifiers::from_bits (modifier_bits).unwrap();
324      'keycodes: for keycode in input::button::Keycode::iter() {
325        let input = input::Button::from (keycode).with_modifiers (modifiers);
326        if let Ok (index) = focused.controller.input_map.buttons
327          .binary_search_by_key (&&input, |(b, _)| b)
328        {
329          // NOTE: input::Button has special PartialEq implementation where
330          // Modifiers::ANY in either lhs or rhs means the buttons are considered equal
331          // if their input::button::Variant are equal
332          let (b, control) = &focused.controller.input_map.buttons[index];
333          if b.modifiers.contains (input::Modifiers::ANY) {
334            // TODO: merge all matches
335          }
336          out.insert (input, Some ((*control).into()));
337        } else {
338          let input_mask = (&Input::Button (input, input::button::State::Pressed)).into();
339          if !focused.controller.bubble_trap.intersects (input_mask) {
340            for ancestor_id in self.elements.ancestor_ids (focused_id).unwrap() {
341              let ancestor = self.get_element (ancestor_id);
342              if let Ok(index) = ancestor.controller.input_map.buttons
343                .binary_search_by_key (&&input, |(b, _)| b)
344              {
345                let (b, control) = &ancestor.controller.input_map.buttons[index];
346                if b.modifiers.contains (input::Modifiers::ANY) {
347                  // TODO: merge all matches
348                }
349                out.insert (input, Some ((*control).into()));
350                continue 'keycodes
351              }
352            }
353          }
354          out.insert (input, None);
355        }
356      }
357    }
358    out
359  }
360
361  /// Create a new element and return the node ID
362  pub fn create_singleton (&mut self,
363    parent_id : &NodeId,
364    element   : Element,
365    order     : CreateOrder
366  ) -> NodeId {
367    match self.action (parent_id, Action::create_singleton (element, order))
368      .next().unwrap()
369    {
370      (_, Event::Create (_, new_id, _)) => new_id,
371      _ => unreachable!()
372    }
373  }
374
375  fn handle_action (&mut self, action : Action, node_id : &NodeId) {
376    use controller::component::{Kind, Selection};
377    log::trace!("handle_action...");
378    log::trace!("action: {action:?}");
379    match action {
380      Action::Create (elements, order) => {
381        #[cfg(debug_assertions)]
382        for element in elements.traverse_level_order (elements.root_node_id().unwrap())
383          .unwrap()
384        {
385          // check that appearances match the controller state
386          debug_assert_eq!(&element.data().view.appearance,
387            element.data().controller.get_appearance());
388        }
389        self.splice_subtree (
390          &elements, elements.root_node_id().unwrap(), node_id, order);
391      }
392      Action::ModifyController (f) => {
393        let controller = &mut self.get_element_mut (node_id).controller;
394        f (controller);
395      }
396      Action::ModifyModel (f) => {
397        let model = {
398          let model = &mut self.get_element_mut (node_id).model;
399          f (model);
400          model.clone()
401        };
402        self.event_buffer.push ((node_id.clone(), Event::Update (model)));
403      }
404      Action::ModifyView (f) => {
405        let view = {
406          let view = &mut self.get_element_mut (node_id).view;
407          f (view);
408          // TODO: reset ephemeral fields (sound triggers)
409          view.clone()
410        };
411        self.display_buffer.push ((node_id.clone(), Display::Update (view.into())));
412      }
413      Action::SubmitCallback (callback_id) => {
414        let callback_id = Some (callback_id);
415        let model = Model {
416          callback_id, .. self.get_element (node_id).model.clone()
417        };
418        self.event_buffer.push ((node_id.clone(), Event::Submit (model)));
419      }
420      Action::Focus => {
421        let mut focus_id = Some (node_id.clone());
422        while let Some (id) = focus_id {
423          focus_id = self.change_focus (&id);
424          // handle re-ordering
425          if self.get_element (&id).controller.focus_top {
426            self.elements.make_last_sibling (&id).unwrap();
427            self.display_buffer.push ((id.clone(), Display::Update (Update::FocusTop)));
428          }
429          let ancestor_ids = self.elements.ancestor_ids (&id).unwrap().cloned()
430            .collect::<Vec <_>>();
431          for ancestor_id in ancestor_ids {
432            if self.get_element (&ancestor_id).controller.focus_top {
433              self.elements.make_last_sibling (&ancestor_id).unwrap();
434              self.display_buffer
435                .push ((ancestor_id.clone(), Display::Update (Update::FocusTop)));
436            }
437          }
438        }
439      }
440      Action::Enable => {
441        let view = {
442          let element = self.get_element_mut (node_id);
443          element.controller.state.enable();
444          element.view.appearance = element.controller.get_appearance().clone();
445          element.view.clone()
446        };
447        self.display_buffer.push ((node_id.clone(), Display::Update (view.into())));
448      }
449      Action::Disable => {
450        let view = {
451          let element = self.get_element_mut (node_id);
452          element.controller.state.disable();
453          element.view.appearance = element.controller.get_appearance().clone();
454          element.view.clone()
455        };
456        self.display_buffer.push ((node_id.clone(), Display::Update (view.into())));
457      }
458      Action::Destroy => {
459        let parent_id = self.elements.get_parent_id (node_id).clone();
460        let focused = self.elements.get_element (node_id).controller.state
461          == State::Focused;
462        { // widget-specific destroy logic
463          let mut children_ids = self.elements.children_ids (&parent_id).unwrap();
464          // menu: we want to update the selection here so that the destroyed
465          // node is not refocused below
466          let first_sibling_id = children_ids.next().unwrap().clone();
467          let first_sibling    = self.elements.get_element (&first_sibling_id);
468          if let Ok (Widget (selection, _, _)) = Menu::try_from (first_sibling)
469            && selection.current.as_ref() == Some (node_id)
470          {
471            let mut select = None;
472            for sibling_id in children_ids {
473              if sibling_id == node_id {
474                continue
475              }
476              let sibling = self.elements.get_element (sibling_id);
477              if sibling.controller.state == State::Enabled
478                && Frame::try_from (sibling).is_ok()
479              {
480                select = Some (sibling_id.clone());
481                break
482              }
483            }
484            let deselect = Box::new (move |controller : &mut Controller|{
485              let selection = Selection::try_ref_mut (&mut controller.component)
486                .unwrap();
487              selection.current = select;
488            });
489            self.handle_action (Action::ModifyController (deselect), &first_sibling_id);
490          }
491        }
492        if focused {
493          let mut focus_id = Some (parent_id);
494          while let Some (id) = focus_id {
495            focus_id = self.change_focus (&id);
496          }
497        }
498        for node_id in self.elements.traverse_post_order_ids (node_id).unwrap() {
499          self.event_buffer.push   ((node_id.clone(), Event::Destroy));
500          self.display_buffer.push ((node_id,         Display::Destroy));
501        }
502        let _ = self.elements
503          .remove_node (node_id.clone(), tree::RemoveBehavior::DropChildren).unwrap();
504      }
505      Action::ReleaseButtons => {
506        use controller::controls::Control;
507        let release_buttons =
508          self.elements.get_element_mut (node_id).controller.release_buttons();
509        for control in release_buttons {
510          control.fun::<A::ButtonControls>().0 (
511            &None, &self.elements, node_id, self.action_buffer.as_mut().unwrap());
512        }
513        let mut action_buffer = self.action_buffer.take().unwrap();
514        #[expect(clippy::iter_with_drain)]
515        for (node_id, action) in action_buffer.drain(..) {
516          self.handle_action (action, &node_id);
517        }
518        self.action_buffer = Some (action_buffer);
519      }
520    }
521    log::trace!("...handle_action");
522  }
523
524  /// Insert node as child of the given node ID
525  fn insert_child (&mut self,
526    parent_id : &NodeId,
527    child     : Node <Element>,
528    order     : CreateOrder
529  ) -> NodeId {
530    let child_model = child.data().model.clone();
531    let child_view  = child.data().view.clone();
532    let child_id    = self.elements.insert (
533      child, tree::InsertBehavior::UnderNode (parent_id)).unwrap();
534    match order {
535      CreateOrder::Append  => {}
536      CreateOrder::Prepend => {
537        let _ = self.elements.make_first_sibling (&child_id).unwrap();
538      }
539      CreateOrder::NthSibling (n) =>
540        self.elements.make_nth_sibling (&child_id, n as usize).unwrap()
541    }
542    self.event_buffer.push ((parent_id.clone(),
543      Event::Create (child_model, child_id.clone(), order)));
544    self.display_buffer.push ((parent_id.clone(),
545      Display::Create (child_view, child_id.clone(), order)));
546    child_id
547  }
548
549  /// Defocus the currently focused node and focuses the target.
550  ///
551  /// Returns a new focus ID if focus should be redirected.
552  fn change_focus (&mut self, target_id : &NodeId) -> Option <NodeId> {
553    use controller::component::{Kind, Selection};
554    log::trace!("change_focus...");
555    let focused_id = self.focused_element_id().clone();
556    let focused_ancestors = self.elements.ancestor_ids (&focused_id).unwrap().cloned()
557      .collect::<Vec <NodeId>>();
558    let target_ancestors = self.elements.ancestor_ids (target_id).unwrap().cloned()
559      .collect::<Vec <NodeId>>();
560    let maybe_common_ancestor_id = focused_ancestors.iter().rev()
561      .zip (target_ancestors.iter().rev())
562      .filter_map (|(a, b)|
563        if a == b {
564          Some (a)
565        } else {
566          None
567        })
568      .cloned().enumerate().last();
569    let focused_ancestor_of_target = target_ancestors.contains (&focused_id);
570    let target_ancestor_of_focused = focused_ancestors.contains (target_id);
571    { // defocus currently focused node and ancestors up to common ancestor
572      let mut defocus = |defocus_id| {
573        let view = {
574          // widget-specific defocus logic here
575          let mut actions  = vec![];
576          let focused_node = self.elements.get (&defocus_id).unwrap();
577          // release button if the focused node is a button widget and it was pressed
578          // ('on')
579          if let Some (sub_child_id) = focused_node.children().first()
580            && let Ok (Widget (switch, _, _)) =
581              Button::try_get (&self.elements, sub_child_id)
582            && switch.state == switch::State::On && !switch.toggle
583          {
584            button::release (&None, &self.elements, &defocus_id, &mut actions);
585          }
586          for (node_id, action) in actions.into_iter() {
587            self.handle_action (action, &node_id);
588          }
589          let focused = self.elements.get_mut (&defocus_id).unwrap().data_mut();
590          focused.defocus();
591          let view = focused.view.clone();
592          focused.view.appearance.sound = None;  // sounds are ephemeral events
593          view
594        };
595        self.display_buffer .push ((defocus_id, Display::Update (view.into())));
596      };
597      // do not defocus if focused node is an ancestor of the target
598      if !focused_ancestor_of_target {
599        defocus (focused_id);
600      }
601      if let Some ((_, common_ancestor_id)) = maybe_common_ancestor_id.as_ref() {
602        for ancestor_id in focused_ancestors.into_iter()
603          .take_while (|id| id != common_ancestor_id && id != target_id)
604        {
605          defocus (ancestor_id);
606        }
607      }
608    }
609    let refocus_id = {
610      // focus from common ancestor to target node
611      let mut focus = |focus_id| {
612        // widget-specific focus logic
613        if Frame::try_from (self.get_element (&focus_id)).is_ok() {
614          // update selection if frame is a menu item
615          let parent_id = self.elements.get_parent_id (&focus_id);
616          let first_sibling_id =
617            self.elements.children_ids (parent_id).unwrap().next().unwrap().clone();
618          if first_sibling_id != focus_id {
619            let first_sibling = self.elements.get_element (&first_sibling_id);
620            if let Ok (Widget (selection, _, _)) = Menu::try_from (first_sibling)
621              && selection.current.as_ref() != Some (&focus_id)
622            {
623              let select_id = focus_id.clone();
624              let select = Box::new (move |controller : &mut Controller|{
625                let selection = Selection::try_ref_mut (&mut controller.component)
626                  .unwrap();
627                selection.current = Some (select_id.clone());
628              });
629              self.handle_action (Action::ModifyController (select), &first_sibling_id);
630            }
631          }
632        }
633        let focus = self.get_element_mut (&focus_id);
634        focus.focus();
635        let view = focus.view.clone();
636        focus.view.appearance.sound = None;  // sounds are ephemeral events
637        self.display_buffer.push ((focus_id.clone(), Display::Update (view.into())));
638      };
639      let depth = if let Some ((depth, _)) = maybe_common_ancestor_id {
640        depth + 1 + usize::from (focused_ancestor_of_target)
641      } else {
642        1
643      };
644      for ancestor_id in target_ancestors.into_iter().rev().skip (depth) {
645        focus (ancestor_id);
646      }
647      // do not focus if target is ancestor of the currently focused node (the node
648      // should already be focused)
649      if !target_ancestor_of_focused {
650        focus (target_id.clone());
651      }
652      // only call refocus on the last node: intermediate nodes are not redirected
653      refocus (target_id, self.elements())
654    };
655    self.focused_id = target_id.clone();
656    log::trace!("...change_focus");
657    refocus_id
658  }
659
660  /// See also public `crate::utils::splice_subtree` for operating on subtrees before
661  /// insertion into the interface
662  fn splice_subtree (&mut self,
663    other     : &Tree <Element>,
664    other_id  : &NodeId,
665    parent_id : &NodeId,
666    order     : CreateOrder
667  ) -> NodeId {
668    // TODO: avoid clone here?
669    let subtree_root = Node::new (other.get (other_id).unwrap().data().clone());
670    let subtree_id   = self.insert_child (parent_id, subtree_root, order);
671    for child_id in other.children_ids (other_id).unwrap() {
672      self.splice_subtree (other, child_id, &subtree_id, CreateOrder::Append);
673    }
674    subtree_id
675  }
676
677  /// This is method is provided to allow initialization of composite presentations by
678  /// first initializing an interface with `G::make_interface()`--the graphical backend
679  /// creates the interface with a default initial interface, usually containing a root
680  /// screen element--and then adding a default initialized audio backend.
681  pub (crate) fn swap_presentation <P2 : Presentation> (self, f : impl FnOnce (P) -> P2)
682    -> Interface <A, P2>
683  {
684    let Interface {
685      elements, focused_id, presentation, input_buffer, display_buffer, action_buffer,
686      event_buffer, _phantom
687    } = self;
688    #[expect(clippy::used_underscore_binding)]
689    Interface {
690      presentation: f (presentation),
691      elements, focused_id, input_buffer, display_buffer, action_buffer, event_buffer,
692      _phantom
693    }
694  }
695}
696
697/// Log the elements as a tree. Default log level is Debug.
698///
699/// This is provided as a macro so that the call site is properly included in the log
700/// metadata.
701pub macro log_elements_tree ($interface:expr$(, $level:expr)?) {
702  let mut s = String::new();
703  $interface.elements.write_formatted (&mut s).unwrap();
704  $crate::interface::log_elements_string!(s$(, $level)?);
705}
706
707/// Log the element names as a tree. Default log level is Debug.
708///
709/// This is provided as a macro so that the call site is properly included in the log
710/// metadata.
711pub macro log_elements_tree_names ($interface:expr$(, $level:expr)?) {
712  let mut s = String::new();
713  $interface.elements.write_formatted_names (&mut s).unwrap();
714  $crate::interface::log_elements_string!(s$(, $level)?);
715}
716
717#[expect(unused_macros)]
718macro log_elements_string {
719  ($string:expr) => {
720    $crate::interface::log_elements_string!($string, $crate::log::Level::Debug);
721  },
722  ($string:expr, $level:expr) => {
723    $crate::log::log!($level, "elements:\n{}", $string);
724  }
725}
726
727impl Element {
728  /// Create a new element. Note that the view appearance will always be set to the
729  /// current appearance defined by the controller state.
730  pub fn new (name : String, controller : Controller, model : Model, mut view : View)
731    -> Self
732  {
733    view.appearance = controller.get_appearance().clone();
734    Element { name, controller, view, model }
735  }
736  pub fn focus (&mut self) {
737    self.controller.state.focus();
738    self.controller.update_view_focus (&mut self.view);
739  }
740  pub fn defocus (&mut self) {
741    self.controller.state.defocus();
742    self.controller.update_view_focus (&mut self.view);
743  }
744  pub fn disable (&mut self) {
745    self.controller.state.disable();
746    self.controller.update_view_focus (&mut self.view);
747  }
748}
749
750impl Default for Element {
751  fn default() -> Self {
752    Element::new (
753      "".to_string(), Controller::default(), Model::default(), View::default())
754  }
755}
756
757impl AsRef <Controller> for Element {
758  fn as_ref (&self) -> &Controller {
759    &self.controller
760  }
761}
762
763impl AsRef <Model> for Element {
764  fn as_ref (&self) -> &Model {
765    &self.model
766  }
767}
768
769impl AsRef <View> for Element {
770  fn as_ref (&self) -> &View {
771    &self.view
772  }
773}
774
775impl Action {
776  /// Produces a `Create` action that creates a singleton subtree containing the given
777  /// `Element`
778  #[inline]
779  pub fn create_singleton (element : Element, order : CreateOrder) -> Self {
780    let subtree = TreeBuilder::new().with_root (Node::new (element)).build();
781    Action::Create (subtree, order)
782  }
783
784  /// Produces a `ModifyView` action that sets the given `View` data
785  #[inline]
786  pub fn set_view (view : View) -> Self {
787    Action::ModifyView (Box::new (|v| *v = view))
788  }
789
790  /// Produces a `ModifyController` action that sets the given `Controller` data
791  #[inline]
792  pub fn set_controller (controller : Controller) -> Self {
793    Action::ModifyController (Box::new (|c| *c = controller))
794  }
795
796  /// Produces a `ModifyModel` action that sets the given `Model` data.
797  #[inline]
798  pub fn set_model (model : Model) -> Self {
799    Action::ModifyModel (Box::new (|m| *m = model))
800  }
801
802  /// Produces a `ModifyView` action that sets the given component `Kind`
803  #[inline]
804  pub fn set_view_component <V> (component : V) -> Self where
805    V : view::component::Kind + 'static
806  {
807    Action::ModifyView (Box::new (|v| v.component = component.into()))
808  }
809
810  /// Produces a `ModifyView` action that updates the current component with the given
811  /// value and panics if the current component is not the same `Kind`
812  #[inline]
813  pub fn update_view_component <V> (component : V) -> Self where
814    V : view::component::Kind + 'static
815  {
816    Action::ModifyView (Box::new (|v| {
817      let v = V::try_ref_mut (&mut v.component).unwrap();
818      *v = component;
819    }))
820  }
821
822  /// Produces a `ModifyController` action that sets the given component `Kind`
823  #[inline]
824  pub fn set_controller_component <C> (component : C) -> Self where
825    C : ControllerKind + 'static
826  {
827    Action::ModifyController (Box::new (|c| c.component = component.into()))
828  }
829
830  /// Produces a `ModifyController` action that updates the current component with the
831  /// given value and panics if the current component is not the same `Kind`
832  #[inline]
833  pub fn update_controller_component <C> (component : C) -> Self where
834    C : ControllerKind + 'static
835  {
836    Action::ModifyController (Box::new (|c| {
837      let c = C::try_ref_mut (&mut c.component).unwrap();
838      *c = component;
839    }))
840  }
841
842  /// Produces a `ModifyModel` action that sets the given component `Kind`
843  #[inline]
844  pub fn set_model_component <M> (component : M) -> Self where
845    M : model::component::Kind + 'static
846  {
847    Action::ModifyModel (Box::new (|m| m.component = component.into()))
848  }
849
850  /// Produces a `ModifyModel` action that updates the current component with the given
851  /// value and panics if the current component is not the same `Kind`
852  #[inline]
853  pub fn update_model_component <M> (component : M) -> Self where
854    M : model::component::Kind + 'static
855  {
856    Action::ModifyModel (Box::new (|m| {
857      let m = M::try_ref_mut (&mut m.component).unwrap();
858      *m = component;
859    }))
860  }
861}
862
863impl std::fmt::Debug for Action {
864  fn fmt (&self, f : &mut std::fmt::Formatter) -> Result <(), std::fmt::Error> {
865    match self {
866      Action::Create (subtree, order)        =>
867        write!(f, "Create({subtree:?}, {order:?})"),
868      Action::ModifyController (closure)     =>
869        write!(f, "ModifyController({:p})", &closure),
870      Action::ModifyModel      (closure)     =>
871        write!(f, "ModifyModel({:p})", &closure),
872      Action::ModifyView       (closure)     =>
873        write!(f, "ModifyView({:p})", &closure),
874      Action::SubmitCallback   (callback_id) =>
875        write!(f, "SubmitCallback({callback_id:?})"),
876      Action::Focus   => write!(f, "Focus"),
877      Action::Enable  => write!(f, "Enable"),
878      Action::Disable => write!(f, "Disable"),
879      Action::Destroy => write!(f, "Destroy"),
880      Action::ReleaseButtons => write!(f, "ReleaseButtons")
881    }
882  }
883}