use std::sync::LazyLock;
use crate::TreeHelper;
use crate::interface::{view, Action, Element, Model, View};
use crate::interface::controller::{self, controls, Controls};
use crate::tree::{Tree, NodeId};
use super::{Widget, Frame};
pub use self::builder::NumboxBuilder as Builder;
pub type Numbox <'element> =
Widget <'element, controller::component::Numeral, f64, view::component::Body>;
pub static CONTROLS : LazyLock <Controls> = LazyLock::new (||
controls::Builder::new()
.buttons ([
controls::button::Builtin::NumboxIncrease,
controls::button::Builtin::NumboxDecrease,
controls::button::Builtin::NumboxIncrease10x,
controls::button::Builtin::NumboxDecrease10x
].into_iter().map (Into::into).collect::<Vec <_>>().into())
.build()
);
pub fn decrease (
_ : &controls::button::Release,
elements : &Tree <Element>,
node_id : &NodeId,
action_buffer : &mut Vec <(NodeId, Action)>
) {
log::trace!("decrease...");
let Widget (numeral, value, _) = Numbox::try_get (elements, node_id).unwrap();
let Widget (_, _, canvas) =
Frame::try_get (elements, elements.get_parent_id (node_id)).unwrap();
let new_value = numeral.modify (*value, -1.0);
let new_body = body (canvas, numeral, new_value);
{ let update_value =
Box::new (move |model : &mut Model| model.component = new_value.into());
action_buffer.push ((node_id.clone(), Action::ModifyModel (update_value)));
}
{ let update_body = Box::new (|view : &mut View| view.component = new_body.into());
action_buffer.push ((node_id.clone(), Action::ModifyView (update_body)));
}
log::trace!("...decrease");
}
pub fn increase (
_ : &controls::button::Release,
elements : &Tree <Element>,
node_id : &NodeId,
action_buffer : &mut Vec <(NodeId, Action)>
) {
log::trace!("increase...");
let Widget (numeral, value, _) = Numbox::try_get (elements, node_id).unwrap();
let Widget (_, _, canvas) =
Frame::try_get (elements, elements.get_parent_id (node_id)).unwrap();
let new_value = numeral.modify (*value, 1.0);
let new_body = body (canvas, numeral, new_value);
{ let update_value =
Box::new (move |model : &mut Model| model.component = new_value.into());
action_buffer.push ((node_id.clone(), Action::ModifyModel (update_value)));
}
{ let update_body = Box::new (|view : &mut View| view.component = new_body.into());
action_buffer.push ((node_id.clone(), Action::ModifyView (update_body)));
}
log::trace!("...increase");
}
pub fn decrease_10x (
_ : &controls::button::Release,
elements : &Tree <Element>,
node_id : &NodeId,
action_buffer : &mut Vec <(NodeId, Action)>
) {
log::trace!("decrease_10x...");
let Widget (numeral, value, _) = Numbox::try_get (elements, node_id).unwrap();
let Widget (_, _, canvas) =
Frame::try_get (elements, elements.get_parent_id (node_id)).unwrap();
let new_value = numeral.modify (*value, -10.0);
let new_body = body (canvas, numeral, new_value);
{ let update_value =
Box::new (move |model : &mut Model| model.component = new_value.into());
action_buffer.push ((node_id.clone(), Action::ModifyModel (update_value)));
}
{ let update_body = Box::new (|view : &mut View| view.component = new_body.into());
action_buffer.push ((node_id.clone(), Action::ModifyView (update_body)));
}
log::trace!("...decrease_10x");
}
pub fn increase_10x (
_ : &controls::button::Release,
elements : &Tree <Element>,
node_id : &NodeId,
action_buffer : &mut Vec <(NodeId, Action)>
) {
log::trace!("increase_10x...");
let Widget (numeral, value, _) = Numbox::try_get (elements, node_id).unwrap();
let Widget (_, _, canvas) =
Frame::try_get (elements, elements.get_parent_id (node_id)).unwrap();
let new_value = numeral.modify (*value, 10.0);
let new_body = body (canvas, numeral, new_value);
{ let update_value =
Box::new (move |model : &mut Model| model.component = new_value.into());
action_buffer.push ((node_id.clone(), Action::ModifyModel (update_value)));
}
{ let update_body = Box::new (|view : &mut View| view.component = new_body.into());
action_buffer.push ((node_id.clone(), Action::ModifyView (update_body)));
}
log::trace!("...increase_10x");
}
pub fn set_value (
elements : &Tree <Element>,
numbox_id : &NodeId,
value : f64,
action_buffer : &mut Vec <(NodeId, Action)>
) {
let Widget (numeral, _, _) = Numbox::try_get (elements, numbox_id).unwrap();
let Widget (_, _, canvas) =
Frame::try_get (elements, elements.get_parent_id (numbox_id)).unwrap();
let new_body = body (canvas, numeral, value);
{ let update_value =
Box::new (move |model : &mut Model| model.component = value.into());
action_buffer.push ((numbox_id.clone(), Action::ModifyModel (update_value)));
}
{ let update_body = Box::new (|view : &mut View| view.component = new_body.into());
action_buffer.push ((numbox_id.clone(), Action::ModifyView (update_body)));
}
}
pub (crate) fn body (
canvas : &view::component::Canvas,
numeral : &controller::component::Numeral,
value : f64
) -> view::component::Body {
use controller::alignment::Horizontal;
log::trace!("body...");
let body = {
let body_width = canvas.body_wh().0 as usize;
let mut string = numeral.format.format_number (value);
let digits = match numeral.alignment {
Horizontal::Left => {
string.truncate (body_width);
string
}
Horizontal::Right => string.split_off (string.len().saturating_sub (body_width)),
Horizontal::Center => if string.len() > body_width {
let extra = string.len() - body_width;
string.truncate (string.len() - extra / 2);
let remaining = extra - extra / 2;
string.split_off (remaining)
} else {
string
}
};
debug_assert!(digits.len() <= body_width);
match numeral.alignment {
Horizontal::Left => view::component::Body (digits),
Horizontal::Right => view::component::Body (format!("{digits:>body_width$}")),
Horizontal::Center => view::component::Body (format!("{:>1$}", digits,
body_width - (body_width - digits.len() / 2)))
}
};
log::trace!("...body");
body
}
fn canvas_size (frame_border : Option <&view::Border>, length : u32)
-> controller::Size
{
let (border_w, border_h) = frame_border.map_or ((0, 0), view::Border::total_wh);
let width = (border_w as u32 + length).into();
let height = (border_h as u32 + 1).into();
controller::Size { width, height }
}
mod builder {
use derive_builder::Builder;
use crate::prelude::*;
use super::canvas_size;
#[derive(Builder)]
#[builder(public, pattern="owned", build_fn(private), setter(strip_option))]
struct Numbox <'a, A : Application> {
elements : &'a Tree <Element>,
parent_id : &'a NodeId,
#[builder(default)]
appearances : Appearances,
#[builder(default)]
bindings : Option <&'a Bindings <A>>,
#[builder(default)]
frame_anchor : Alignment,
#[builder(default)]
frame_appearances : Appearances,
#[builder(default)]
frame_area : Area,
#[builder(default)]
frame_bindings : Option <&'a Bindings <A>>,
#[builder(default)]
frame_border : Option <Border>,
#[builder(default)]
frame_clear_color : Option <canvas::ClearColor>,
#[builder(default)]
frame_offset : Offset,
#[builder(default = "12")]
length : u32,
#[builder(default)]
name : Option <String>,
#[builder(default)]
numeral : Numeral,
#[builder(default)]
value : Option <f64>
}
impl <'a, A : Application> NumboxBuilder <'a, A> {
pub const fn new (elements : &'a Tree <Element>, parent_id : &'a NodeId) -> Self {
NumboxBuilder {
elements: Some (elements),
parent_id: Some (parent_id),
appearances: None,
bindings: None,
frame_anchor: None,
frame_appearances: None,
frame_area: None,
frame_bindings: None,
frame_border: None,
frame_clear_color: None,
frame_offset: None,
length: None,
name: None,
numeral: None,
value: None
}
}
}
impl <A : Application> BuildActions for NumboxBuilder <'_, A> {
fn build_actions (self) -> Vec<(NodeId, Action)> {
use std::convert::TryInto;
use crate::tree::{InsertBehavior, Node};
use controller::component::layout;
log::trace!("build actions...");
let Numbox {
elements, parent_id, appearances, bindings, frame_anchor, frame_appearances,
frame_area, frame_bindings, frame_border, frame_clear_color, frame_offset,
length, name, numeral, value
} = self.build()
.map_err(|err| log::error!("frame builder error: {err:?}"))
.unwrap();
let bindings_empty = Bindings::empty();
let bindings = bindings.unwrap_or (&bindings_empty)
.get_bindings (&super::CONTROLS);
let frame_bindings = frame_bindings.unwrap_or (&bindings_empty);
if length == 0 {
log::warn!("zero length number box field");
}
{ let Widget (_, _, canvas) = Frame::try_get (elements, parent_id).unwrap();
assert!(canvas.coordinates.kind() == coordinates::Kind::Tile);
}
let value = value.unwrap_or (0.0);
let mut out = vec![];
let (mut subtree, order) = {
let layout = {
let anchor = frame_anchor;
let offset = frame_offset;
let size = canvas_size (frame_border.as_ref(), length);
layout::Variant::from ((layout::Free { anchor, offset, size }, frame_area))
};
let mut actions = {
let mut frame = frame::Builder::new (elements, parent_id)
.appearances (frame_appearances)
.bindings (frame_bindings)
.layout (layout.into());
set_option!(frame, border, frame_border);
set_option!(frame, clear_color, frame_clear_color);
frame.build_actions()
};
out.extend (actions.drain (1..));
debug_assert_eq!(actions.len(), 1);
actions.pop().unwrap().1.try_into().unwrap()
};
let frame_id = subtree.root_node_id().unwrap().clone();
let numbox = {
let controller = {
let component = numeral.clone().into();
let mut controller = Controller::with_bindings (&bindings);
controller.component = component;
controller.appearances = appearances;
controller
};
let model = Model { component: value.into(), .. Model::default() };
let view = {
let Widget (_, _, canvas) = Frame::try_get (&subtree, &frame_id).unwrap();
view::Component::from (super::body (canvas, &numeral, value)).into()
};
let name = name.unwrap_or ("Numbox".to_string());
Element::new (name, controller, model, view)
};
let _ = subtree
.insert (Node::new (numbox), InsertBehavior::UnderNode (&frame_id))
.unwrap();
out.push ((parent_id.clone(), Action::Create (subtree, order)));
log::trace!("...build actions");
out
}
}
}