use std::convert::TryFrom;
use std::sync::LazyLock;
use log;
use crate::prelude::*;
use super::set_layout;
pub use self::builder::TiledBuilder as Builder;
pub static CONTROLS : LazyLock <Controls> = LazyLock::new (||
controls::Builder::new()
.buttons ({
let mut v : Vec <controls::Button> = vec![
controls::button::Builtin::FrameTiledIncreaseSize,
controls::button::Builtin::FrameTiledDecreaseSize
].into_iter().map (Into::into).collect();
v.extend (super::CONTROLS.buttons.iter().copied());
v.into()
})
.build()
);
pub fn increase_size (
_ : &controls::button::Release,
elements : &Tree <Element>,
node_id : &NodeId,
action_buffer : &mut Vec <(NodeId, Action)>
) {
log::trace!("increase_size...");
let Widget (layout, _, canvas) = Frame::try_get (elements, node_id).unwrap();
let coordinate_kind = Some (canvas.coordinates.kind());
let layout = {
use controller::component::{layout, Layout};
let mut tiled = layout::Tiled::try_from (layout.variant.clone()).unwrap();
tiled.modify_size (1);
Layout { variant: tiled.into(), .. layout.clone() }
};
set_layout (elements, node_id, layout, coordinate_kind, action_buffer);
log::trace!("...increase_size");
}
pub fn decrease_size (
_ : &controls::button::Release,
elements : &Tree <Element>,
node_id : &NodeId,
action_buffer : &mut Vec <(NodeId, Action)>
) {
log::trace!("decrease_size...");
let Widget (layout, _, canvas) = Frame::try_get (elements, node_id).unwrap();
let coordinate_kind = Some (canvas.coordinates.kind());
let layout = {
use controller::component::{layout, Layout};
let mut tiled = layout::Tiled::try_from (layout.variant.clone()).unwrap();
tiled.modify_size (-1);
Layout { variant: tiled.into(), .. layout.clone() }
};
set_layout (elements, node_id, layout, coordinate_kind, action_buffer);
log::trace!("...decrease_size");
}
pub (crate) fn new_coordinates (
elements : &Tree <Element>,
frame_id : &NodeId,
layout : &layout::Tiled
) -> Vec <(NodeId, Coordinates)> {
use controller::component::layout;
log::trace!("coordinates...");
let parent_id = elements.get_parent_id (frame_id);
let Widget (parent_layout, _, parent_canvas) = Frame::try_get (elements, parent_id)
.unwrap();
let parent_node = elements.get (parent_id).unwrap();
let mut total_weights = 0;
let mut total_absolute = 0;
let mut add_layout = |layout : &layout::Tiled| match layout {
layout::Tiled::Weighted (weight) => total_weights += weight.get(),
layout::Tiled::Absolute (size) => total_absolute += size.get()
};
let tiled_children = parent_node.children().iter().filter_map (|child_id| {
let Widget (child_layout, _, _) = Frame::try_get (elements, child_id).ok()?;
if child_id == frame_id {
add_layout (layout);
Some ((child_id.clone(), layout))
} else if let controller::component::layout::Variant::Tiled (tiled) =
&child_layout.variant
{
add_layout (tiled);
Some ((child_id.clone(), tiled))
} else {
None
}
}).collect::<Vec<_>>();
let mut out = vec![];
let mut start_weight = 0;
let mut start_absolute = 0;
for (id, layout) in tiled_children.into_iter() {
let coordinates = coordinates (
&parent_layout.orientation, parent_canvas, layout, &mut start_weight,
&mut start_absolute, total_weights, total_absolute);
out.push ((id, coordinates));
}
log::trace!("...coordinates");
out
}
pub (crate) fn destroy_coordinates (
elements : &Tree <Element>,
frame_id : &NodeId
) -> Vec <(NodeId, Coordinates)> {
use controller::component::layout;
log::trace!("destroy_coordinates...");
let parent_id = elements.get_parent_id (frame_id);
let Widget (parent_layout, _, parent_canvas) = Frame::try_get (elements, parent_id)
.unwrap();
let parent_node = elements.get (parent_id).unwrap();
let mut total_weights = 0;
let mut total_absolute = 0;
let mut add_layout = |layout : &layout::Tiled| match layout {
layout::Tiled::Weighted (weight) => total_weights += weight.get(),
layout::Tiled::Absolute (size) => total_absolute += size.get()
};
let tiled_children = parent_node.children().iter().filter_map (|child_id| {
if child_id == frame_id {
None
} else {
let Widget (child_layout, _, _) = Frame::try_get (elements, child_id).ok()?;
if let controller::component::layout::Variant::Tiled (tiled) =
&child_layout.variant
{
add_layout (tiled);
Some ((child_id.clone(), tiled))
} else {
None
}
}
}).collect::<Vec <_>>();
let mut out = vec![];
let mut start_weight = 0;
let mut start_absolute = 0;
for (id, layout) in tiled_children.into_iter() {
let coordinates = coordinates (
&parent_layout.orientation, parent_canvas, layout, &mut start_weight,
&mut start_absolute, total_weights, total_absolute);
out.push ((id, coordinates));
}
log::trace!("...destroy_coordinates");
out
}
pub (crate) fn child_coordinates (
orientation : &(Orientation, Area),
canvas : &Canvas,
layouts : Vec <layout::Tiled>
) -> Vec <Coordinates> {
use controller::component::layout;
let mut child_coordinates = vec![];
let mut total_weights = 0;
let mut total_absolute = 0;
let add_layout = |layout : &layout::Tiled| match layout {
layout::Tiled::Weighted (weight) => total_weights += weight.get(),
layout::Tiled::Absolute (size) => total_absolute += size.get()
};
layouts.iter().for_each (add_layout);
let mut start_weight = 0;
let mut start_absolute = 0;
for layout in layouts {
child_coordinates.push (
coordinates (orientation, canvas, &layout, &mut start_weight,
&mut start_absolute, total_weights, total_absolute));
}
child_coordinates
}
fn coordinates (
(parent_orientation, parent_area) : &(Orientation, Area),
parent_canvas : &Canvas,
child_layout : &layout::Tiled,
start_weight : &mut u32,
start_absolute : &mut u32,
total_weights : u32,
total_absolute : u32
) -> Coordinates {
use controller::component::layout;
use view::{coordinates, dimensions, position};
log::trace!("coordinates...");
let parent_coordinates = match parent_area {
Area::Exterior => parent_canvas.coordinates,
Area::Interior => parent_canvas.body_coordinates()
};
let (position_horizontal, position_vertical, width, height) =
match parent_orientation {
Orientation::Horizontal => {
let height = parent_coordinates.dimensions_vertical();
let parent_width = parent_coordinates.dimensions_horizontal();
let position_vertical = parent_coordinates.position_vertical();
let parent_position_horizontal = parent_coordinates.position_horizontal();
let position_horizontal = parent_position_horizontal + *start_absolute as i32 +
( (*start_weight as f64 / total_weights as f64) *
parent_width.saturating_sub (total_absolute) as f64
) as i32;
let width = match child_layout {
layout::Tiled::Weighted (weight) => {
let weight = weight.get();
*start_weight += weight;
let next_position = parent_position_horizontal + *start_absolute as i32 +
( (*start_weight as f64 / total_weights as f64) *
parent_width.saturating_sub (total_absolute) as f64
) as i32;
(next_position - position_horizontal) as u32
}
layout::Tiled::Absolute (size) => {
let size = size.get();
*start_absolute += size;
size
}
};
(position_horizontal, position_vertical, width, height)
}
Orientation::Vertical => {
let width = parent_coordinates.dimensions_horizontal();
let parent_height = parent_coordinates.dimensions_vertical();
let position_horizontal = parent_coordinates.position_horizontal();
let parent_position_vertical = parent_coordinates.position_vertical();
let position_vertical = parent_position_vertical + *start_absolute as i32 +
( (*start_weight as f64 / total_weights as f64) *
parent_height.saturating_sub (total_absolute) as f64
) as i32;
let height = match child_layout {
layout::Tiled::Weighted (weight) => {
let weight = weight.get();
*start_weight += weight;
let next_position = parent_position_vertical + *start_absolute as i32 +
( (*start_weight as f64 / total_weights as f64) *
parent_height.saturating_sub (total_absolute) as f64
) as i32;
(next_position - position_vertical) as u32
}
layout::Tiled::Absolute (size) => {
let size = size.get();
*start_absolute += size;
size
}
};
(position_horizontal, position_vertical, width, height)
}
};
let out = if parent_coordinates.kind() == coordinates::Kind::Tile {
( position::Tile::new_rc (position_vertical, position_horizontal),
dimensions::Tile::new_rc (height, width)
).into()
} else {
debug_assert!(parent_coordinates.kind() == coordinates::Kind::Pixel);
( position::Pixel::new_xy (position_horizontal, position_vertical),
dimensions::Pixel::new_wh (width, height)
).into()
};
log::trace!("...coordinates");
out
}
mod builder {
use derive_builder::Builder;
use crate::prelude::*;
use frame::set_coordinates;
#[derive(Builder)]
#[builder(public, pattern="owned", build_fn(private), setter(strip_option))]
struct Tiled <'a, A : Application> {
elements : &'a Tree <Element>,
parent_id : &'a NodeId,
#[builder(default)]
appearances : Appearances,
#[builder(default)]
bindings : Option <&'a Bindings <A>>,
#[builder(default)]
border : Option <Border>,
#[builder(default)]
clear_color : canvas::ClearColor,
#[builder(default)]
create_order : CreateOrder,
#[builder(default)]
disabled : bool,
#[builder(default)]
layout : layout::Tiled,
#[builder(default)]
name : Option <String>,
#[builder(default)]
orientation : (Orientation, Area),
}
impl <'a, A : Application> TiledBuilder <'a, A> {
pub const fn new (elements : &'a Tree <Element>, parent_id : &'a NodeId) -> Self {
TiledBuilder {
elements: Some (elements),
parent_id: Some (parent_id),
appearances: None,
bindings: None,
border: None,
clear_color: None,
create_order: None,
disabled: None,
layout: None,
name: None,
orientation: None
}
}
}
impl <A : Application> BuildActions for TiledBuilder <'_, A> {
fn build_actions (self) -> Vec <(NodeId, Action)> {
use view::component::Canvas;
log::trace!("build actions...");
let Tiled {
elements, parent_id, appearances, bindings, border, clear_color,
create_order, disabled, layout, name, orientation
} = self.build()
.map_err(|err| log::error!("frame free builder error: {err:?}"))
.unwrap();
let bindings_empty = Bindings::empty();
let bindings = bindings.unwrap_or (&bindings_empty)
.get_bindings (&super::CONTROLS);
let (new_coordinates, sibling_coordinates) =
create_coordinates (elements, parent_id, &layout, create_order);
let frame = {
use controller::component::Layout;
let controller = {
let mut controller = Controller::with_bindings (&bindings);
controller.component = Layout {
orientation, variant: layout.into()
}.into();
controller.appearances = appearances;
controller
};
let view = {
let coordinates = new_coordinates;
let appearance = controller.get_appearance().clone();
let component = Canvas { coordinates, clear_color, border }.into();
View { component, appearance, .. View::default() }
};
let name = name.unwrap_or ("Frame".to_string());
let mut frame = Element::new (name, controller, Model::default(), view);
let parent_node = elements.get (parent_id).unwrap();
if parent_node.data().controller.state == State::Disabled || disabled {
frame.disable()
}
frame
};
let mut out = vec![
(parent_id.clone(), Action::create_singleton (frame, create_order))
];
for (id, coordinates) in sibling_coordinates.into_iter() {
set_coordinates (elements, &id, &coordinates, None, &mut out);
}
log::trace!("...build actions");
out
}
}
fn create_coordinates (
elements : &Tree <Element>,
parent_frame_id : &NodeId,
layout : &layout::Tiled,
order : CreateOrder
) -> (Coordinates, Vec <(NodeId, Coordinates)>) {
use controller::component::layout;
log::trace!("create_coordinates...");
let Widget (parent_layout, _, parent_canvas) =
Frame::try_get (elements, parent_frame_id).unwrap();
let parent_node = elements.get (parent_frame_id).unwrap();
let mut total_weights = 0;
let mut total_absolute = 0;
let mut add_layout = |layout : &layout::Tiled| match layout {
layout::Tiled::Weighted (weight) => total_weights += weight.get(),
layout::Tiled::Absolute (size) => total_absolute += size.get()
};
add_layout (layout);
let sibling_layouts = parent_node.children().iter().map (|child_id| {
let Widget (child_layout, _, _) = Frame::try_get (elements, child_id).ok()?;
if let layout::Variant::Tiled (tiled) = &child_layout.variant {
add_layout (tiled);
Some ((child_id, tiled))
} else {
None
}
}).collect::<Vec <_>>();
let order_index = match order {
CreateOrder::Prepend => Some (0),
CreateOrder::NthSibling (n) => Some (n),
CreateOrder::Append => None
};
let mut sibling_coordinates = vec![];
let mut start_weight = 0;
let mut start_absolute = 0;
let mut new_coordinates = None;
let mut index = 0;
for (i, maybe_tiled) in sibling_layouts.into_iter().enumerate() {
if order_index == Some (i as u32) {
new_coordinates = Some (super::coordinates (
&parent_layout.orientation, parent_canvas, layout, &mut start_weight,
&mut start_absolute, total_weights, total_absolute
));
}
if let Some ((id, tiled)) = maybe_tiled {
let coordinates = super::coordinates (
&parent_layout.orientation, parent_canvas, tiled, &mut start_weight,
&mut start_absolute, total_weights, total_absolute);
sibling_coordinates.push ((id.clone(), coordinates));
}
index = i + 1;
}
if new_coordinates.is_none() {
if cfg!(debug_assertions) && let Some (n) = order_index {
debug_assert_eq!(n, index as u32);
}
new_coordinates = Some (super::coordinates (
&parent_layout.orientation, parent_canvas, layout,
&mut start_weight, &mut start_absolute, total_weights, total_absolute
));
}
log::trace!("...create_coordinates");
(new_coordinates.unwrap(), sibling_coordinates)
}
}