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
42/// parameters 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
56/// element node.
57///
58/// To allow a component to range over any type, each 'Variant' also implements
59/// 'Kind'.
60#[derive(Clone, Debug, PartialEq)]
61pub struct Widget <'element, C, M, V> (
62  pub &'element C,
63  pub &'element M,
64  pub &'element V
65) where
66  C : controller::component::Kind,
67  M : model::component::Kind,
68  V : view::component::Kind;
69
70impl <'element, C, M, V> TryFrom <&'element Element>
71  for Widget <'element, C, M, V>
72where
73  C : controller::component::Kind,
74  M : model::component::Kind,
75  V : view::component::Kind
76{
77  type Error = &'element Element;
78  fn try_from (element : &'element Element) -> Result <Self, Self::Error> {
79    let controller = C::try_ref (&element.controller.component).ok_or (element)?;
80    let model      = M::try_ref (&element.model.component).ok_or (element)?;
81    let view       = V::try_ref (&element.view.component).ok_or (element)?;
82    Ok (Widget::<'element, C, M, V> (controller, model, view))
83  }
84}
85
86impl <'element, C, M, V> Widget <'element, C, M, V> where
87  C : controller::component::Kind,
88  M : model::component::Kind,
89  V : view::component::Kind
90{
91  /// Try to cast the target Element to the Widget type. Target node must exist.
92  pub fn try_get (elements: &'element Tree <Element>, node_id : &NodeId)
93    -> Result <Self, &'element Element>
94  {
95    let element = elements.get_element (node_id);
96    Self::try_from (element)
97  }
98}
99
100/// Trait for widget `Element` builders
101pub trait BuildElement {
102  fn build_element (self) -> Element;
103}
104
105/// Trait for widgets that are built from a list of `Action`s
106pub trait BuildActions {
107  fn build_actions (self) -> Vec <(NodeId, Action)>;
108}
109
110/// Process the build actions and return the Nth created node ID (starting from
111/// 0)
112pub macro build_and_return_node_id {
113  ($interface:expr, $builder:expr) => {
114    build_and_return_node_id!($interface, $builder, 0)
115  },
116  ($interface:expr, $builder:expr, $nth:expr) => {{
117    let actions    = $builder.build_actions();
118    let mut events = $interface.actions (actions);
119    let mut count  = 0;
120    loop {
121      match events.next().unwrap() {
122        (_, $crate::interface::model::Event::Create (_, new_id, _)) =>
123          if count == $nth {
124            break new_id
125          } else {
126            count += 1;
127          }
128        _ => {}
129      }
130    }
131  }}
132}
133
134/// Process the build actions and return the created node IDs with the given
135/// names
136pub macro build_and_get_node_ids {
137  ($interface:expr, $builder:expr, $names:expr) => {{
138    let actions   = $builder.build_actions();
139    let events    = $interface.actions (actions).collect::<Vec <_>>();
140    let mut names = $names.map (|name| Some (name));
141    let mut ids   = $names.map (|_| None);
142    for event in events {
143      match event {
144        (_, $crate::interface::model::Event::Create (_, new_id, _)) => {
145          let name = $interface.get_element (&new_id).name.as_str();
146          if let Some (i) = names.iter().position (|n| n == &Some (name)) {
147            names[i] = None;
148            ids[i]   = Some (new_id);
149          }
150        }
151        _ => {}
152      }
153    }
154    ids.map (Option::unwrap)
155  }}
156}