gooey/widget/
mod.rs

1//! Contextual interface elements.
2//!
3//! `Widget`s are parameterized references to combinations of possibly concrete
4//! component types. Each widget also defines its *control functions*.
5//!
6//! Most widgets are graphical in some way and are parented to a `Frame` widget
7//! that provides the primary container and layout functionalities.
8//!
9//! Non-graphical widgets are the `Form` widget, which is completely generic,
10//! and the `Playback` widget which has at least an `Sfx` view component and can
11//! be parented to any other widget.
12
13use std::convert::TryFrom;
14use crate::{Tree, TreeHelper};
15use crate::interface::{controller, model, view, Action, Element};
16use crate::tree::NodeId;
17
18pub mod button;
19pub mod field;
20pub mod form;
21pub mod frame;
22pub mod menu;
23pub mod numbox;
24pub mod picture;
25pub mod playback;
26pub mod textbox;
27pub use self::button::Button;
28pub use self::field::Field;
29pub use self::form::Form;
30pub use self::frame::Frame;
31pub use self::menu::Menu;
32pub use self::numbox::Numbox;
33pub use self::picture::Picture;
34pub use self::playback::Playback;
35pub use self::textbox::Textbox;
36
37pub mod composite;
38
39/// Helper for widget builders.
40///
41/// Because the `"owned"` builder pattern is used, propagating optional parameters
42/// requires the syntax:
43/// ```ignore
44/// if let Some (some) = some_option {
45///    some_builder = some_builder.some_setter (some);
46/// }
47/// ```
48/// and this macro reduces the syntax to a one-liner.
49pub (crate) macro set_option ($builder:ident, $setter:ident, $option:expr) {
50  if let Some (inner) = $option {
51    $builder = $builder.$setter (inner);
52  }
53}
54
55/// Defines a reference to a particular combination of component kinds in an element
56/// node.
57///
58/// To allow a component to range over any type, each 'Variant' also implements 'Kind'.
59#[derive(Clone, Debug, Eq, PartialEq)]
60pub struct Widget <'element, C, M, V> (
61  pub &'element C,
62  pub &'element M,
63  pub &'element V
64) where
65  C : controller::component::Kind,
66  M : model::component::Kind,
67  V : view::component::Kind;
68
69impl <'element, C, M, V> TryFrom <&'element Element> for Widget <'element, C, M, V> where
70  C : controller::component::Kind,
71  M : model::component::Kind,
72  V : view::component::Kind
73{
74  type Error = &'element Element;
75  fn try_from (element : &'element Element) -> Result <Self, Self::Error> {
76    let controller = C::try_ref (&element.controller.component).ok_or (element)?;
77    let model      = M::try_ref (&element.model.component).ok_or (element)?;
78    let view       = V::try_ref (&element.view.component).ok_or (element)?;
79    Ok (Widget::<'element, C, M, V> (controller, model, view))
80  }
81}
82
83impl <'element, C, M, V> Widget <'element, C, M, V> where
84  C : controller::component::Kind,
85  M : model::component::Kind,
86  V : view::component::Kind
87{
88  /// Try to cast the target Element to the Widget type. Target node must exist.
89  pub fn try_get (elements: &'element Tree <Element>, node_id : &NodeId)
90    -> Result <Self, &'element Element>
91  {
92    let element = elements.get_element (node_id);
93    Self::try_from (element)
94  }
95}
96
97/// Trait for widget `Element` builders
98pub trait BuildElement {
99  fn build_element (self) -> Element;
100}
101
102/// Trait for widgets that are built from a list of `Action`s
103pub trait BuildActions {
104  fn build_actions (self) -> Vec <(NodeId, Action)>;
105}
106
107/// Process the build actions and return the Nth created node ID (starting from
108/// 0)
109pub macro build_and_return_node_id {
110  ($interface:expr, $builder:expr) => {
111    build_and_return_node_id!($interface, $builder, 0)
112  },
113  ($interface:expr, $builder:expr, $nth:expr) => {{
114    let actions    = $builder.build_actions();
115    let mut events = $interface.actions (actions);
116    let mut count  = 0;
117    loop {
118      match events.next().unwrap() {
119        (_, $crate::interface::model::Event::Create (_, new_id, _)) =>
120          if count == $nth {
121            break new_id
122          } else {
123            count += 1;
124          }
125        _ => {}
126      }
127    }
128  }}
129}
130
131/// Process the build actions and return the created node IDs with the given
132/// names
133pub macro build_and_get_node_ids {
134  ($interface:expr, $builder:expr, $names:expr) => {{
135    let actions   = $builder.build_actions();
136    let events    = $interface.actions (actions).collect::<Vec <_>>();
137    let mut names = $names.map (|name| Some (name));
138    let mut ids   = $names.map (|_| None);
139    for event in events {
140      match event {
141        (_, $crate::interface::model::Event::Create (_, new_id, _)) => {
142          let name = $interface.get_element (&new_id).name.as_str();
143          if let Some (i) = names.iter().position (|n| n == &Some (name)) {
144            names[i] = None;
145            ids[i]   = Some (new_id);
146          }
147        }
148        _ => {}
149      }
150    }
151    ids.map (Option::unwrap)
152  }}
153}