1use std::sync::LazyLock;
14
15use crate::TreeHelper;
16use crate::interface::{view, Action, Element, Model, View};
17use crate::interface::controller::{self, controls, Controls};
18use crate::tree::{Tree, NodeId};
19
20use super::{Widget, Frame};
21
22pub use self::builder::NumboxBuilder as Builder;
23
24pub type Numbox <'element> = Widget <'element,
25 controller::component::Numeral, f64, view::component::Body>;
26
27pub static CONTROLS : LazyLock <Controls> = LazyLock::new (||
32 controls::Builder::new()
33 .buttons (vec![
34 controls::button::Builtin::NumboxIncrease,
35 controls::button::Builtin::NumboxDecrease,
36 controls::button::Builtin::NumboxIncrease10x,
37 controls::button::Builtin::NumboxDecrease10x
38 ].into_iter().map (Into::into).collect::<Vec <_>>().into())
39 .build()
40);
41
42pub fn decrease (
44 _ : &controls::button::Release,
45 elements : &Tree <Element>,
46 node_id : &NodeId,
47 action_buffer : &mut Vec <(NodeId, Action)>
48) {
49 log::trace!("decrease...");
50 let Widget (numeral, value, _) = Numbox::try_get (elements, node_id).unwrap();
51 let Widget (_, _, canvas) =
52 Frame::try_get (elements, elements.get_parent_id (node_id)).unwrap();
53 let new_value = numeral.modify (*value, -1.0);
54 let new_body = body (canvas, numeral, new_value);
55 { let update_value =
57 Box::new (move |model : &mut Model| model.component = new_value.into());
58 action_buffer.push ((node_id.clone(), Action::ModifyModel (update_value)));
59 }
60 { let update_body =
62 Box::new (|view : &mut View| view.component = new_body.into());
63 action_buffer.push ((node_id.clone(), Action::ModifyView (update_body)));
64 }
65 log::trace!("...decrease");
66}
67
68pub fn increase (
70 _ : &controls::button::Release,
71 elements : &Tree <Element>,
72 node_id : &NodeId,
73 action_buffer : &mut Vec <(NodeId, Action)>
74) {
75 log::trace!("increase...");
76 let Widget (numeral, value, _) = Numbox::try_get (elements, node_id).unwrap();
77 let Widget (_, _, canvas) =
78 Frame::try_get (elements, elements.get_parent_id (node_id)).unwrap();
79 let new_value = numeral.modify (*value, 1.0);
80 let new_body = body (canvas, numeral, new_value);
81 { let update_value =
83 Box::new (move |model : &mut Model| model.component = new_value.into());
84 action_buffer.push ((node_id.clone(), Action::ModifyModel (update_value)));
85 }
86 { let update_body =
88 Box::new (|view : &mut View| view.component = new_body.into());
89 action_buffer.push ((node_id.clone(), Action::ModifyView (update_body)));
90 }
91 log::trace!("...increase");
92}
93
94pub fn decrease_10x (
96 _ : &controls::button::Release,
97 elements : &Tree <Element>,
98 node_id : &NodeId,
99 action_buffer : &mut Vec <(NodeId, Action)>
100) {
101 log::trace!("decrease_10x...");
102 let Widget (numeral, value, _) = Numbox::try_get (elements, node_id).unwrap();
103 let Widget (_, _, canvas) =
104 Frame::try_get (elements, elements.get_parent_id (node_id)).unwrap();
105 let new_value = numeral.modify (*value, -10.0);
106 let new_body = body (canvas, numeral, new_value);
107 { let update_value =
109 Box::new (move |model : &mut Model| model.component = new_value.into());
110 action_buffer.push ((node_id.clone(), Action::ModifyModel (update_value)));
111 }
112 { let update_body =
114 Box::new (|view : &mut View| view.component = new_body.into());
115 action_buffer.push ((node_id.clone(), Action::ModifyView (update_body)));
116 }
117 log::trace!("...decrease_10x");
118}
119
120pub fn increase_10x (
122 _ : &controls::button::Release,
123 elements : &Tree <Element>,
124 node_id : &NodeId,
125 action_buffer : &mut Vec <(NodeId, Action)>
126) {
127 log::trace!("increase_10x...");
128 let Widget (numeral, value, _) = Numbox::try_get (elements, node_id).unwrap();
129 let Widget (_, _, canvas) =
130 Frame::try_get (elements, elements.get_parent_id (node_id)).unwrap();
131 let new_value = numeral.modify (*value, 10.0);
132 let new_body = body (canvas, numeral, new_value);
133 { let update_value =
135 Box::new (move |model : &mut Model| model.component = new_value.into());
136 action_buffer.push ((node_id.clone(), Action::ModifyModel (update_value)));
137 }
138 { let update_body =
140 Box::new (|view : &mut View| view.component = new_body.into());
141 action_buffer.push ((node_id.clone(), Action::ModifyView (update_body)));
142 }
143 log::trace!("...increase_10x");
144}
145
146pub fn set_value (
152 elements : &Tree <Element>,
153 numbox_id : &NodeId,
154 value : f64,
155 action_buffer : &mut Vec <(NodeId, Action)>
156) {
157 let Widget (numeral, _, _) = Numbox::try_get (elements, numbox_id).unwrap();
158 let Widget (_, _, canvas) =
159 Frame::try_get (elements, elements.get_parent_id (numbox_id)).unwrap();
160 let new_body = body (canvas, numeral, value);
161 { let update_value =
163 Box::new (move |model : &mut Model| model.component = value.into());
164 action_buffer.push ((numbox_id.clone(), Action::ModifyModel (update_value)));
165 }
166 { let update_body = Box::new (|view : &mut View| view.component = new_body.into());
168 action_buffer.push ((numbox_id.clone(), Action::ModifyView (update_body)));
169 }
170}
171
172pub (crate) fn body (
177 canvas : &view::component::Canvas,
178 numeral : &controller::component::Numeral,
179 value : f64
180) -> view::component::Body {
181 use controller::alignment::Horizontal;
182 log::trace!("body...");
183 let body = {
184 let body_width = canvas.body_wh().0 as usize;
185 let mut string = numeral.format.format_number (value);
186 let digits = match numeral.alignment {
187 Horizontal::Left => {
188 string.truncate (body_width);
189 string
190 }
191 Horizontal::Right => string.split_off (string.len().saturating_sub (body_width)),
192 Horizontal::Center => if string.len() > body_width {
193 let extra = string.len() - body_width;
194 string.truncate (string.len() - extra / 2);
195 let remaining = extra - extra / 2;
196 string.split_off (remaining)
197 } else {
198 string
199 }
200 };
201 debug_assert!(digits.len() <= body_width);
202 match numeral.alignment {
203 Horizontal::Left => view::component::Body (digits),
204 Horizontal::Right => view::component::Body (format!("{digits:>body_width$}")),
205 Horizontal::Center => view::component::Body (format!("{:>1$}", digits,
206 body_width - (body_width - digits.len() / 2)))
207 }
208 };
209 log::trace!("...body");
210 body
211}
212
213fn canvas_size (frame_border : Option <&view::Border>, length : u32)
218 -> controller::Size
219{
220 let (border_w, border_h) = frame_border.map_or ((0, 0), view::Border::total_wh);
221 let width = (border_w as u32 + length).into();
222 let height = (border_h as u32 + 1).into();
223 controller::Size { width, height }
224}
225
226mod builder {
231 use derive_builder::Builder;
232 use crate::prelude::*;
233 use super::canvas_size;
234
235 #[derive(Builder)]
236 #[builder(public, pattern="owned", build_fn(private), setter(strip_option))]
237 struct Numbox <'a, A : Application> {
238 elements : &'a Tree <Element>,
239 parent_id : &'a NodeId,
240 #[builder(default)]
241 appearances : Appearances,
242 #[builder(default)]
243 bindings : Option <&'a Bindings <A>>,
244 #[builder(default)]
245 frame_anchor : Alignment,
246 #[builder(default)]
247 frame_appearances : Appearances,
248 #[builder(default)]
249 frame_area : Area,
250 #[builder(default)]
251 frame_bindings : Option <&'a Bindings <A>>,
252 #[builder(default)]
253 frame_border : Option <Border>,
254 #[builder(default)]
255 frame_clear_color : Option <canvas::ClearColor>,
256 #[builder(default)]
257 frame_offset : Offset,
258 #[builder(default = "12")]
259 length : u32,
260 #[builder(default)]
261 numeral : Numeral,
262 #[builder(default)]
263 value : Option <f64>
264 }
265
266 impl <'a, A : Application> NumboxBuilder <'a, A> {
267 pub const fn new (elements : &'a Tree <Element>, parent_id : &'a NodeId) -> Self {
268 NumboxBuilder {
269 elements: Some (elements),
270 parent_id: Some (parent_id),
271 appearances: None,
272 bindings: None,
273 frame_anchor: None,
274 frame_appearances: None,
275 frame_area: None,
276 frame_bindings: None,
277 frame_border: None,
278 frame_clear_color: None,
279 frame_offset: None,
280 length: None,
281 numeral: None,
282 value: None
283 }
284 }
285 }
286
287 impl <A : Application> BuildActions for NumboxBuilder <'_, A> {
288 fn build_actions (self) -> Vec<(NodeId, Action)> {
289 use std::convert::TryInto;
290 use crate::tree::{InsertBehavior, Node};
291 use controller::component::layout;
292 log::trace!("build actions...");
293 let Numbox {
294 elements, parent_id, appearances, bindings, frame_anchor,
295 frame_appearances, frame_area, frame_bindings, frame_border,
296 frame_clear_color, frame_offset, length, numeral, value
297 } = self.build()
298 .map_err(|err| log::error!("frame builder error: {err:?}"))
299 .unwrap();
300 let bindings_empty = Bindings::empty();
301 let bindings = bindings.unwrap_or (&bindings_empty)
302 .get_bindings (&super::CONTROLS);
303 let frame_bindings = frame_bindings.unwrap_or (&bindings_empty);
304 if length == 0 {
305 log::warn!("zero length number box field");
306 }
307 { let Widget (_, _, canvas) = Frame::try_get (elements, parent_id)
309 .unwrap();
310 assert!(canvas.coordinates.kind() == coordinates::Kind::Tile);
311 }
312 let value = value.unwrap_or (0.0);
313 let mut out = vec![];
314 let (mut subtree, order) = {
315 let layout = {
316 let anchor = frame_anchor;
317 let offset = frame_offset;
318 let size = canvas_size (frame_border.as_ref(), length);
319 layout::Variant::from (
320 (layout::Free { anchor, offset, size }, frame_area)
321 )
322 };
323 let mut actions = {
324 let mut frame = frame::Builder::new (elements, parent_id)
325 .appearances (frame_appearances)
326 .bindings (frame_bindings)
327 .layout (layout.into());
328 set_option!(frame, border, frame_border);
329 set_option!(frame, clear_color, frame_clear_color);
330 frame.build_actions()
331 };
332 out.extend (actions.drain (1..));
333 debug_assert_eq!(actions.len(), 1);
334 actions.pop().unwrap().1.try_into().unwrap()
335 };
336 let frame_id = subtree.root_node_id().unwrap().clone();
337 let numbox = {
338 let controller = {
339 let component = numeral.clone().into();
340 let mut controller = Controller::with_bindings (&bindings);
341 controller.component = component;
342 controller.appearances = appearances;
343 controller
344 };
345 let model = Model { component: value.into(), .. Model::default() };
346 let view = {
347 let Widget (_, _, canvas) =
348 Frame::try_get (&subtree, &frame_id).unwrap();
349 view::Component::from (super::body (canvas, &numeral, value)).into()
350 };
351 Element::new ("Numbox".to_string(), controller, model, view)
352 };
353 let _ = subtree
354 .insert (Node::new (numbox), InsertBehavior::UnderNode (&frame_id))
355 .unwrap();
356 out.push ((parent_id.clone(), Action::Create (subtree, order)));
357 log::trace!("...build actions");
358 out
359 }
360 }
361}