gooey/widget/
button.rs

1//! Button widget.
2//!
3//! ```text
4//! +--------+
5//! | frame  |
6//! +---+----+
7//!     | \----------\
8//! +---+----+  +-----+-----+
9//! | button |  | (textbox) |
10//! +--------+  +-----------+
11//! ```
12//!
13//! If a `label` string is provided, a Textbox label will be created as a
14//! sibling to the Button element.
15//!
16//! The `push` and `release` control functions target the *frame* node, and
17//! assume that the first child is the button node.
18
19use lazy_static::lazy_static;
20
21use crate::prelude::*;
22
23pub use self::builder::ButtonBuilder as Builder;
24
25pub type Button <'element> = Widget <'element,
26  controller::component::Switch, model::Component, view::Component>;
27
28//
29//  controls
30//
31
32lazy_static!{
33  pub static ref CONTROLS : Controls = controls::Builder::new()
34    .buttons (vec![
35      controls::button::Builtin::ButtonPush,
36      controls::button::Builtin::ButtonRelease
37    ].into_iter().map (Into::into).collect::<Vec <_>>().into())
38    .build();
39}
40
41/// Builtin button control ID 'ButtonPush'
42pub fn push (
43  release       : &controls::button::Release,
44  elements      : &Tree <Element>,
45  frame_id      : &NodeId,
46  action_buffer : &mut Vec <(NodeId, Action)>
47) {
48  log::trace!("push...");
49  let frame = elements.get_element (frame_id);
50  let mut frame_children = elements.children_ids (frame_id).unwrap();
51  let button_id = frame_children.next().unwrap();
52  let label_id  = frame_children
53    .filter (|node_id| Textbox::try_get (elements, node_id).is_ok())
54    .next();
55  let Widget (switch, _, _) = Button::try_get (elements, button_id).unwrap();
56  let toggle    = switch.toggle;
57  if toggle && switch.state == switch::State::On {
58    return self::release (&None, elements, frame_id, action_buffer)
59  }
60  let mut switch = switch.clone();
61  let (exit, (enter, appearances, label)) = switch.on();
62  // NOTE: we update the switch and appearance before processing trigger events
63  // in case the trigger destroys the node
64  // switch
65  let update_switch = Box::new (
66    |controller : &mut Controller| controller.component = switch.into());
67  action_buffer
68    .push ((button_id.clone(), Action::ModifyController (update_switch)));
69  // appearance
70  let appearance = Appearance {
71    sound: None, .. appearances.get (frame.controller.state.clone()).clone()
72  };
73  let update_controller = Box::new (
74    |controller : &mut Controller| controller.appearances = appearances);
75  let update_view = Box::new (
76    |view : &mut View| view.appearance = appearance);
77  action_buffer
78    .push ((frame_id.clone(), Action::ModifyController (update_controller)));
79  action_buffer
80    .push ((frame_id.clone(), Action::ModifyView (update_view)));
81  // exit
82  if let Some (trigger) = exit {
83    let target = trigger.target.as_ref().unwrap_or (button_id);
84    trigger.control_fun.0 (release, elements, target, action_buffer)
85  }
86  // remove old label
87  if let Some (textbox_id) = label_id {
88    action_buffer.push ((textbox_id.clone(), Action::Destroy))
89  }
90  // create label
91  if let Some (label) = label {
92    let textbox =
93      textbox::WithFrameBuilder::<application::Default>::new (
94        &elements, frame_id
95      ) .text (label)
96        .build_element();
97    action_buffer.push ((frame_id.clone(),
98      Action::create_singleton (textbox, CreateOrder::Append)))
99  }
100  // enter
101  if let Some (trigger) = enter {
102    let target = trigger.target.as_ref().unwrap_or (button_id);
103    trigger.control_fun.0 (release, elements, target, action_buffer)
104  }
105  if !toggle {
106    // return release action
107    if let Some (release) = release {
108      release.borrow_mut().push (
109        (controls::button::Builtin::ButtonRelease.into(), frame_id.clone()));
110    }
111  }
112  log::trace!("...push");
113}
114
115/// Builtin button control ID 'ButtonRelease'
116pub fn release (
117  _             : &controls::button::Release,
118  elements      : &Tree <Element>,
119  frame_id      : &NodeId,
120  action_buffer : &mut Vec <(NodeId, Action)>
121) {
122  log::trace!("release...");
123  let frame = elements.get_element (frame_id);
124  let mut frame_children = elements.children_ids (frame_id).unwrap();
125  let button_id = frame_children.next().unwrap();
126  let label_id  = frame_children
127    .filter (|node_id| Textbox::try_get (elements, node_id).is_ok())
128    .next();
129  let Widget (switch, _, _) = Button::try_get (elements, button_id).unwrap();
130  let mut switch = switch.clone();
131  let (exit, (enter, appearances, label)) = switch.off();
132  // exit
133  if let Some (trigger) = exit {
134    let target = trigger.target.as_ref().unwrap_or (button_id);
135    trigger.control_fun.0 (&None, elements, target, action_buffer)
136  }
137  // remove old label
138  if let Some (textbox_id) = label_id {
139    action_buffer.push ((textbox_id.clone(), Action::Destroy))
140  }
141  // create label
142  if let Some (label) = label {
143    let textbox =
144      textbox::WithFrameBuilder::<application::Default>::new (
145        &elements, frame_id
146      ) .text (label)
147        .build_element();
148    action_buffer.push ((frame_id.clone(),
149      Action::create_singleton (textbox, CreateOrder::Append)))
150  }
151  // enter
152  if let Some (trigger) = enter {
153    let target = trigger.target.as_ref().unwrap_or (button_id);
154    trigger.control_fun.0 (&None, elements, target, action_buffer)
155  }
156  // switch
157  let update_switch = Box::new (
158    |controller : &mut Controller| controller.component = switch.into());
159  action_buffer
160    .push ((button_id.clone(), Action::ModifyController (update_switch)));
161  // appearance
162  let appearance = view::Appearance {
163    sound: None,
164    .. appearances.get (frame.controller.state.clone()).clone()
165  };
166  let update_controller = Box::new (
167    |controller : &mut Controller| controller.appearances = appearances);
168  let update_view = Box::new (
169    |view : &mut View| view.appearance = appearance);
170  action_buffer
171    .push ((frame_id.clone(), Action::ModifyController (update_controller)));
172  action_buffer
173    .push ((frame_id.clone(), Action::ModifyView (update_view)));
174  log::trace!("...release");
175}
176
177//
178//  builder
179//
180
181mod builder {
182  use derive_builder::Builder;
183  use crate::prelude::*;
184
185  #[derive(Builder)]
186  #[builder(pattern="owned", build_fn(private), setter(strip_option))]
187  pub struct Button <'a, A : Application> {
188    elements     : &'a Tree <Element>,
189    parent_id    : &'a NodeId,
190    #[builder(default)]
191    bindings     : Option <&'a controller::Bindings <A>>,
192    #[builder(default)]
193    callback_id  : Option <application::CallbackId>,
194    #[builder(default)]
195    controls     : Option <controller::Controls>,
196    #[builder(default)]
197    frame_layout : controller::component::Layout,
198    #[builder(default)]
199    frame_border : Option <view::Border>,
200    #[builder(default)]
201    model_data   : Option <model::Component>,
202    #[builder(default)]
203    switch       : controller::component::Switch
204  }
205
206  impl <'a, A : Application> ButtonBuilder <'a, A> {
207    pub fn new (elements : &'a Tree <Element>, parent_id : &'a NodeId) -> Self {
208      ButtonBuilder {
209        elements:          Some (elements),
210        parent_id:         Some (parent_id),
211        bindings:          None,
212        callback_id:       None,
213        controls:          None,
214        frame_border:      None,
215        frame_layout:      None,
216        model_data:        None,
217        switch:            None
218      }
219    }
220  }
221
222  impl <'a, A : Application> BuildActions for ButtonBuilder <'a, A> {
223    /// Creates a new button with the given switch state, frame layout and border,
224    /// and optional text label.
225    ///
226    /// The `Create` action will be returned as the last action.
227    fn build_actions (self) -> Vec<(NodeId, Action)> {
228      use std::convert::TryInto;
229      use crate::tree::InsertBehavior;
230      log::trace!("build actions...");
231      let Button {
232        elements, parent_id, bindings, callback_id, controls, frame_border,
233        frame_layout, model_data, switch
234      } = self.build()
235        .map_err(|err| log::error!("button builder error: {:?}", err)).unwrap();
236      let bindings_empty = Bindings::empty();
237      let bindings = bindings.unwrap_or (&bindings_empty);
238      let mut out = vec![];
239      let (mut subtree, order) = {
240        let mut actions = {
241          let mut frame = frame::Builder::new (elements, parent_id)
242            .appearances (switch.appearances().clone())
243            .bindings (bindings)
244            .layout (frame_layout);
245          set_option!(frame, border, frame_border);
246          frame.build_actions()
247        };
248        out.extend (actions.drain (1..));
249        debug_assert_eq!(actions.len(), 1);
250        actions.pop().unwrap().1.try_into().unwrap()
251      };
252      let frame_id = subtree.root_node_id().unwrap().clone();
253      subtree.get_mut (&frame_id).unwrap().data_mut().controller.add_bindings (
254        &bindings.get_bindings (controls.as_ref().unwrap_or (&super::CONTROLS))
255      );
256      let label    = switch.label().clone();
257      let button   = {
258        let controller = {
259          let mut controller   = Controller::default();
260          controller.component = switch.into();
261          controller.focus_top = false;
262          controller
263        };
264        let model = {
265          let component = model_data.unwrap_or_else (Default::default);
266          Model { callback_id, component }
267        };
268        Element::new ("Button".to_string(), controller, model, View::default())
269      };
270      let _ = subtree
271        .insert (Node::new (button), InsertBehavior::UnderNode (&frame_id))
272        .unwrap();
273      if let Some (label) = label {
274        let textbox = textbox::WithFrameBuilder::<A>::new (
275          &subtree, &frame_id
276        ) .text (label)
277          .build_element();
278        let _ = subtree
279          .insert (Node::new (textbox), InsertBehavior::UnderNode (&frame_id))
280          .unwrap();
281      };
282      out.push ((parent_id.clone(), Action::Create (subtree, order)));
283      log::trace!("...build actions");
284      out
285    }
286  }
287}