use std::sync::LazyLock;
use nsys::math::Vector2;
use crate::prelude::*;
use super::set_layout;
pub use self::builder::FreeBuilder as Builder;
pub static CONTROLS : LazyLock <Controls> = LazyLock::new (||
controls::Builder::new()
.buttons ({
let mut v : Vec <controls::Button> = vec![
controls::button::Builtin::FrameFreeMoveRight,
controls::button::Builtin::FrameFreeMoveLeft,
controls::button::Builtin::FrameFreeMoveUp,
controls::button::Builtin::FrameFreeMoveDown,
controls::button::Builtin::FrameFreeGrowWidth,
controls::button::Builtin::FrameFreeShrinkWidth,
controls::button::Builtin::FrameFreeGrowHeight,
controls::button::Builtin::FrameFreeShrinkHeight
].into_iter().map (Into::into).collect();
v.extend (super::CONTROLS.buttons.iter().copied());
v.into()
})
.build()
);
pub fn move_right (
_ : &controls::button::Release,
elements : &Tree <Element>,
node_id : &NodeId,
action_buffer : &mut Vec <(NodeId, Action)>
) {
log::trace!("move_right...");
let Widget (layout, _, canvas) = Frame::try_get (elements, node_id).unwrap();
let coord_kind_override = Some (canvas.coordinates.kind());
let layout = {
use controller::component::Layout;
let (mut free, area) = layout.variant.clone().try_into().unwrap();
free.offset_move_right();
Layout { variant: (free, area).into(), .. layout.clone() }
};
set_layout (elements, node_id, layout, coord_kind_override, action_buffer);
log::trace!("...move_right");
}
pub fn move_left (
_ : &controls::button::Release,
elements : &Tree <Element>,
node_id : &NodeId,
action_buffer : &mut Vec <(NodeId, Action)>
) {
log::trace!("move_left...");
let Widget (layout, _, canvas) = Frame::try_get (elements, node_id).unwrap();
let coordinate_kind = Some (canvas.coordinates.kind());
let layout = {
use controller::component::Layout;
let (mut free, area) = layout.variant.clone().try_into().unwrap();
free.offset_move_left();
Layout { variant: (free, area).into(), .. layout.clone() }
};
set_layout (elements, node_id, layout, coordinate_kind, action_buffer);
log::trace!("...move_left");
}
pub fn move_up (
_ : &controls::button::Release,
elements : &Tree <Element>,
node_id : &NodeId,
action_buffer : &mut Vec <(NodeId, Action)>
) {
log::trace!("move_up...");
let Widget (layout, _, canvas) = Frame::try_get (elements, node_id).unwrap();
let coordinate_kind = Some (canvas.coordinates.kind());
let layout = {
use controller::component::Layout;
let (mut free, area) = layout.variant.clone().try_into().unwrap();
free.offset_move_up();
Layout { variant: (free, area).into(), .. layout.clone() }
};
set_layout (elements, node_id, layout, coordinate_kind, action_buffer);
log::trace!("...move_up");
}
pub fn move_down (
_ : &controls::button::Release,
elements : &Tree <Element>,
node_id : &NodeId,
action_buffer : &mut Vec <(NodeId, Action)>
) {
log::trace!("move_down...");
let Widget (layout, _, canvas) = Frame::try_get (elements, node_id).unwrap();
let coordinate_kind = Some (canvas.coordinates.kind());
let layout = {
use controller::component::Layout;
let (mut free, area) = layout.variant.clone().try_into().unwrap();
free.offset_move_down();
Layout { variant: (free, area).into(), .. layout.clone() }
};
set_layout (elements, node_id, layout, coordinate_kind, action_buffer);
log::trace!("...move_down");
}
pub fn grow_width (
_ : &controls::button::Release,
elements : &Tree <Element>,
node_id : &NodeId,
action_buffer : &mut Vec <(NodeId, Action)>
) {
log::trace!("grow_width...");
let Widget (layout, _, canvas) = Frame::try_get (elements, node_id).unwrap();
let coordinate_kind = Some (canvas.coordinates.kind());
let layout = {
use controller::component::Layout;
let (mut free, area) = layout.variant.clone().try_into().unwrap();
free.size_grow_width();
Layout { variant: (free, area).into(), .. layout.clone() }
};
set_layout (elements, node_id, layout, coordinate_kind, action_buffer);
log::trace!("...grow_width");
}
pub fn shrink_width (
_ : &controls::button::Release,
elements : &Tree <Element>,
node_id : &NodeId,
action_buffer : &mut Vec <(NodeId, Action)>
) {
log::trace!("shrink_width...");
let Widget (layout, _, canvas) = Frame::try_get (elements, node_id).unwrap();
let coordinate_kind = Some (canvas.coordinates.kind());
let layout = {
use controller::component::Layout;
let (mut free, area) = layout.variant.clone().try_into().unwrap();
free.size_shrink_width();
Layout { variant: (free, area).into(), .. layout.clone() }
};
set_layout (elements, node_id, layout, coordinate_kind, action_buffer);
log::trace!("...shrink_width");
}
pub fn grow_height (
_ : &controls::button::Release,
elements : &Tree <Element>,
node_id : &NodeId,
action_buffer : &mut Vec <(NodeId, Action)>
) {
log::trace!("grow_height...");
let Widget (layout, _, canvas) = Frame::try_get (elements, node_id).unwrap();
let coordinate_kind = Some (canvas.coordinates.kind());
let layout = {
use controller::component::Layout;
let (mut free, area) = layout.variant.clone().try_into().unwrap();
free.size_grow_height();
Layout { variant: (free, area).into(), .. layout.clone() }
};
set_layout (elements, node_id, layout, coordinate_kind, action_buffer);
log::trace!("...grow_height");
}
pub fn shrink_height (
_ : &controls::button::Release,
elements : &Tree <Element>,
node_id : &NodeId,
action_buffer : &mut Vec <(NodeId, Action)>
) {
log::trace!("shrink_height...");
let Widget (layout, _, canvas) = Frame::try_get (elements, node_id).unwrap();
let coordinate_kind = Some (canvas.coordinates.kind());
let layout = {
use controller::component::Layout;
let (mut free, area) = layout.variant.clone().try_into().unwrap();
free.size_shrink_height();
Layout { variant: (free, area).into(), .. layout.clone() }
};
set_layout (elements, node_id, layout, coordinate_kind, action_buffer);
log::trace!("...shrink_height");
}
pub fn rearrange_absolute (
elements : &Tree <Element>,
parent_id : &NodeId,
anchor : Alignment,
offset_hv : (i32, i32),
orientation : Orientation,
spacing : u32,
reverse : bool
) -> Vec <(NodeId, Action)> {
use controller::{offset, size, Offset, Orientation, Size};
use controller::component::{layout, Layout};
log::trace!("rearrange_absolute...");
let mut out = vec![];
let children_ids = elements.children_ids (parent_id).unwrap();
match orientation {
Orientation::Horizontal => {
let (mut horizontal, vertical) = offset_hv;
for child_id in children_ids {
if let Ok (Widget (layout, _, canvas)) = Frame::try_get (elements, child_id) {
match &layout.variant {
layout::Variant::Free (free@layout::Free {
offset: Offset { horizontal: offset::Signed::Absolute (_), .. },
size: Size { width: size::Unsigned::Absolute (_), .. },
..
}, area) => {
let coordinate_kind = Some (canvas.coordinates.kind());
let layout = {
let offset = {
let horizontal = horizontal.into();
let vertical = vertical.into();
Offset { horizontal, vertical }
};
let size = {
let size = free.size;
let w = u32::try_from (size.width).unwrap();
let next = (w + spacing) as i32;
if reverse {
horizontal -= next;
} else {
horizontal += next;
}
size
};
let free = layout::Free { anchor, offset, size };
Layout { variant: (free, area.clone()).into(), .. layout.clone() }
};
set_layout (elements, child_id, layout, coordinate_kind, &mut out);
}
_ => {}
}
}
}
}
Orientation::Vertical => {
let (horizontal, mut vertical) = offset_hv;
for child_id in children_ids {
if let Ok (Widget (layout, _, canvas)) = Frame::try_get (elements, child_id) {
match &layout.variant {
layout::Variant::Free (free@layout::Free {
offset: Offset { vertical: offset::Signed::Absolute (_), .. },
size: Size { height: size::Unsigned::Absolute (_), .. },
..
}, area) => {
let coordinate_kind = Some (canvas.coordinates.kind());
let layout = {
let offset = {
let horizontal = horizontal.into();
let vertical = vertical.into();
Offset { horizontal, vertical }
};
let size = {
let size = free.size;
let h = u32::try_from (size.height).unwrap();
let next = (h + spacing) as i32;
if reverse {
vertical -= next;
} else {
vertical += next;
}
size
};
let free = layout::Free { anchor, offset, size };
Layout { variant: (free, area.clone()).into(), .. layout.clone() }
};
set_layout (elements, child_id, layout, coordinate_kind, &mut out);
}
_ => {}
}
}
}
}
}
log::trace!("...rearrange_absolute");
out
}
pub (crate) fn coordinates (
parent_canvas : &Canvas,
layout : &layout::Free,
area : &Area,
coord_kind_override : Option <coordinates::Kind>
) -> Coordinates {
log::trace!("coordinates...");
let layout::Free { anchor, offset, size } = layout;
let parent_coordinates = match area {
Area::Exterior => parent_canvas.coordinates,
Area::Interior => parent_canvas.body_coordinates()
};
let dimensions = dimensions (&parent_coordinates.dimensions(), size,
coord_kind_override);
let position = position (&parent_coordinates, &dimensions, *anchor, offset,
coord_kind_override);
let out = (position, dimensions).try_into().unwrap();
log::trace!("...coordinates");
out
}
#[inline]
fn dimensions (
parent_dimensions : &Dimensions,
size : &Size,
coord_kind_override : Option <coordinates::Kind>
) -> Dimensions {
use controller::size;
use view::{coordinates, dimensions, Dimensions};
log::trace!("dimensions...");
let same_as_parent = || match parent_dimensions {
Dimensions::Tile (dimensions) => {
let rows = match size.height {
size::Unsigned::Absolute (height) => height,
size::Unsigned::Relative (height) =>
std::cmp::max (1, (dimensions.rows() as f32 * *height) as u32)
};
let columns = match size.width {
size::Unsigned::Absolute (width) => width,
size::Unsigned::Relative (width) =>
std::cmp::max (1, (dimensions.columns() as f32 * *width) as u32)
};
Dimensions::from (dimensions::Tile::from (Vector2::new (rows, columns)))
}
Dimensions::Pixel (dimensions) => {
let width = match size.width {
size::Unsigned::Absolute (width) => width,
size::Unsigned::Relative (width) =>
std::cmp::max (1, (dimensions.width() as f32 * *width) as u32)
};
let height = match size.height {
size::Unsigned::Absolute (height) => height,
size::Unsigned::Relative (height) =>
std::cmp::max (1, (dimensions.height() as f32 * *height) as u32)
};
Dimensions::from (dimensions::Pixel::from (Vector2::new (width, height)))
}
};
let out = if let Some (kind) = coord_kind_override {
match parent_dimensions {
Dimensions::Tile (_dimensions) =>
if kind == coordinates::Kind::Tile {
same_as_parent()
} else {
unimplemented!("TODO: pixel dimensions with tile dimension parent")
}
Dimensions::Pixel (dimensions) =>
if kind == coordinates::Kind::Pixel {
same_as_parent()
} else {
let [tile_width, tile_height] = *coordinates::TILE_WH;
let rows = match size.height {
size::Unsigned::Absolute (height) => height,
size::Unsigned::Relative (height) => {
let parent_rows = dimensions.height() / tile_height;
std::cmp::max (1, (parent_rows as f32 * *height) as u32)
}
};
let columns = match size.width {
size::Unsigned::Absolute (width) => width,
size::Unsigned::Relative (width) => {
let parent_columns = dimensions.width() / tile_width;
std::cmp::max (1, (parent_columns as f32 * *width) as u32)
}
};
Dimensions::from (dimensions::Tile::from (Vector2::new (rows, columns)))
}
}
} else {
same_as_parent()
};
log::trace!("...dimensions");
out
}
fn position (
parent_coordinates : &Coordinates,
dimensions : &Dimensions,
anchor : Alignment,
offset : &Offset,
coord_kind_override : Option <coordinates::Kind>
) -> Position {
use controller::{alignment, offset};
use view::{coordinates, dimensions, position, Coordinates};
log::trace!("position...");
let same_as_parent = || match parent_coordinates {
Coordinates::Tile (parent_position, parent_dimensions) => {
let dimensions = dimensions::Tile::try_from (*dimensions).unwrap();
let row = match offset.vertical {
offset::Signed::Absolute (vertical) => match anchor.vertical {
alignment::Vertical::Top => vertical,
alignment::Vertical::Middle => parent_dimensions.rows() as i32/2
- dimensions.rows() as i32/2 - vertical,
alignment::Vertical::Bottom => parent_dimensions.rows() as i32
- dimensions.rows() as i32 - vertical
}
offset::Signed::Relative (vertical) => {
let absolute_offset = (*vertical * parent_dimensions.rows() as f32) as i32;
match anchor.vertical {
alignment::Vertical::Top => absolute_offset,
alignment::Vertical::Middle => parent_dimensions.rows() as i32/2
- dimensions.rows() as i32/2 - absolute_offset,
alignment::Vertical::Bottom => parent_dimensions.rows() as i32
- dimensions.rows() as i32 - absolute_offset
}
}
};
let column = match offset.horizontal {
offset::Signed::Absolute (horizontal) => match anchor.horizontal {
alignment::Horizontal::Left => horizontal,
alignment::Horizontal::Center => parent_dimensions.columns() as i32/2
- dimensions.columns() as i32/2 + horizontal,
alignment::Horizontal::Right => parent_dimensions.columns() as i32
- dimensions.columns() as i32 - horizontal
}
offset::Signed::Relative (horizontal) => {
let absolute_offset = (*horizontal * parent_dimensions.columns() as f32)
as i32;
match anchor.horizontal {
alignment::Horizontal::Left => absolute_offset,
alignment::Horizontal::Center => parent_dimensions.columns() as i32/2
- dimensions.columns() as i32/2 + absolute_offset,
alignment::Horizontal::Right => parent_dimensions.columns() as i32
- dimensions.columns() as i32 - absolute_offset
}
}
};
position::Tile::from (parent_position.position_rc + Vector2::new (row, column))
.into()
}
Coordinates::Pixel (parent_position, parent_dimensions) => {
let dimensions = dimensions::Pixel::try_from (*dimensions).unwrap();
let y = match offset.vertical {
offset::Signed::Absolute (vertical) => match anchor.vertical {
alignment::Vertical::Bottom => vertical,
alignment::Vertical::Middle => parent_dimensions.height() as i32/2
- dimensions.height() as i32/2 + vertical,
alignment::Vertical::Top => parent_dimensions.height() as i32
- dimensions.height() as i32 - vertical
}
offset::Signed::Relative (vertical) => {
let absolute_offset = (*vertical * parent_dimensions.height() as f32) as i32;
match anchor.vertical {
alignment::Vertical::Bottom => absolute_offset,
alignment::Vertical::Middle => parent_dimensions.height() as i32/2
- dimensions.height() as i32/2 + absolute_offset,
alignment::Vertical::Top => parent_dimensions.height() as i32
- dimensions.height() as i32 - absolute_offset
}
}
};
let x = match offset.horizontal {
offset::Signed::Absolute (horizontal) => match anchor.horizontal {
alignment::Horizontal::Left => horizontal,
alignment::Horizontal::Center => parent_dimensions.width() as i32/2
- dimensions.width() as i32/2 + horizontal,
alignment::Horizontal::Right => parent_dimensions.width() as i32
- dimensions.width() as i32 - horizontal
}
offset::Signed::Relative (horizontal) => {
let absolute_offset = (*horizontal * parent_dimensions.width() as f32) as i32;
match anchor.horizontal {
alignment::Horizontal::Left => absolute_offset,
alignment::Horizontal::Center => parent_dimensions.width() as i32/2
- dimensions.width() as i32/2 + absolute_offset,
alignment::Horizontal::Right => parent_dimensions.width() as i32
- dimensions.width() as i32 - absolute_offset
}
}
};
position::Pixel::from (parent_position.position_xy + Vector2::new (x, y)).into()
}
};
let out = if let Some (kind) = coord_kind_override {
match parent_coordinates.kind() {
coordinates::Kind::Tile => if kind == coordinates::Kind::Tile {
same_as_parent()
} else {
unimplemented!("TODO: pixel position with tile position parent")
}
coordinates::Kind::Pixel => if kind == coordinates::Kind::Pixel {
same_as_parent()
} else {
let parent_aabb = geometry::integer::Aabb2::from (*parent_coordinates);
let parent_aabb_tiles = coordinates::pixel_to_tile_aabb (parent_aabb);
let [parent_row, parent_column] = parent_aabb_tiles.min().0.into_array();
let parent_columns = *parent_aabb_tiles.width();
let parent_rows = *parent_aabb_tiles.height();
let dimensions = dimensions::Tile::try_from (*dimensions).unwrap();
let row = match offset.vertical {
offset::Signed::Absolute (vertical) => match anchor.vertical {
alignment::Vertical::Top => vertical,
alignment::Vertical::Middle => parent_rows/2 - dimensions.rows() as i32/2
- vertical,
alignment::Vertical::Bottom => parent_rows - dimensions.rows() as i32
- vertical
}
offset::Signed::Relative (vertical) => {
let absolute_offset = (*vertical * parent_rows as f32) as i32;
match anchor.vertical {
alignment::Vertical::Top => absolute_offset,
alignment::Vertical::Middle => parent_rows/2 - dimensions.rows() as i32/2
- absolute_offset,
alignment::Vertical::Bottom => parent_rows - dimensions.rows() as i32
- absolute_offset
}
}
};
let column = match offset.horizontal {
offset::Signed::Absolute (horizontal) => match anchor.horizontal {
alignment::Horizontal::Left => horizontal,
alignment::Horizontal::Center => parent_columns/2
- dimensions.columns() as i32/2 + horizontal,
alignment::Horizontal::Right => parent_columns
- dimensions.columns() as i32 - horizontal
}
offset::Signed::Relative (horizontal) => {
let absolute_offset = (*horizontal * parent_columns as f32) as i32;
match anchor.horizontal {
alignment::Horizontal::Left => absolute_offset,
alignment::Horizontal::Center => parent_columns/2
- dimensions.columns() as i32/2 + absolute_offset,
alignment::Horizontal::Right => parent_columns
- dimensions.columns() as i32 - absolute_offset
}
}
};
position::Tile::new_rc (parent_row + row, parent_column + column).into()
}
}
} else {
same_as_parent()
};
log::trace!("...position");
out
}
mod builder {
use derive_builder::Builder;
use crate::prelude::*;
#[derive(Builder)]
#[builder(public, pattern="owned", build_fn(private), setter(strip_option))]
struct Free <'a, A : Application> {
elements : &'a Tree <Element>,
parent_id : &'a NodeId,
#[builder(default)]
appearances : Appearances,
#[builder(default)]
area : Area,
#[builder(default)]
bindings : Option <&'a Bindings <A>>,
#[builder(default)]
border : Option <Border>,
#[builder(default)]
clear_color : canvas::ClearColor,
#[builder(default)]
coord_kind_override : Option <coordinates::Kind>,
#[builder(default)]
disabled : bool,
#[builder(default)]
layout : layout::Free,
#[builder(default)]
name : Option <String>,
#[builder(default)]
orientation : (Orientation, Area)
}
impl <'a, A : Application> FreeBuilder <'a, A> {
pub const fn new (elements : &'a Tree <Element>, parent_id : &'a NodeId) -> Self {
FreeBuilder {
elements: Some (elements),
parent_id: Some (parent_id),
appearances: None,
area: None,
bindings: None,
border: None,
clear_color: None,
coord_kind_override: None,
disabled: None,
layout: None,
name: None,
orientation: None
}
}
}
impl <A : Application> BuildElement for FreeBuilder <'_, A> {
fn build_element (self) -> Element {
log::trace!("build element...");
let Free {
elements, parent_id, appearances, area, bindings, border, clear_color,
coord_kind_override, 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 Widget (_, _, parent_canvas) = Frame::try_get (elements, parent_id).unwrap();
let parent_node = elements.get (parent_id).unwrap();
let focus_top = if let Some (first_child_id) = parent_node.children().first()
&& Menu::try_get (elements, first_child_id).is_ok()
{
false
} else {
true
};
let controller = {
let mut controller = Controller::with_bindings (&bindings);
controller.component = Layout {
orientation, variant: (layout, area.clone()).into(),
}.into();
controller.focus_top = focus_top;
controller.appearances = appearances;
controller
};
let view = {
let coordinates =
super::coordinates (parent_canvas, &layout, &area, coord_kind_override);
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);
if parent_node.data().controller.state == State::Disabled || disabled {
frame.disable()
}
log::trace!("...build element");
frame
}
}
}