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