#[cfg(test)]
mod tests_module {
use crate::nodes::*;
use crate::*;
macro_rules! assert_area {
($expected:expr) => {
draw(move |a, _| {
assert_eq!(a, $expected);
vec![()]
})
};
}
mod container_tests {
use super::*;
#[test]
fn test_containers_hug_children_column() {
stack(vec![
assert_area!(Area::new(45., 40., 10., 20.)).inert(),
column(vec![
assert_area!(Area::new(45., 40., 10., 10.))
.width(10.)
.height(10.),
assert_area!(Area::new(45., 50., 10., 10.))
.width(10.)
.height(10.),
]),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_containers_hug_children_row() {
stack(vec![
assert_area!(Area::new(40., 45., 20., 10.)).inert(),
row(vec![
assert_area!(Area::new(40., 45., 10., 10.))
.width(10.)
.height(10.),
assert_area!(Area::new(50., 45., 10., 10.))
.width(10.)
.height(10.),
]),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_containers_hug_children_stack() {
stack(vec![
assert_area!(Area::new(45., 45., 10., 10.)).inert(),
stack(vec![
assert_area!(Area::new(49., 49., 2., 2.))
.width(2.)
.height(2.),
assert_area!(Area::new(45., 45., 10., 10.))
.width(10.)
.height(10.),
]),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_containers_hug_children_column_nested() {
stack(vec![
assert_area!(Area::new(45., 40., 10., 20.)).inert(),
column(vec![
stack(vec![
assert_area!(Area::new(45., 40., 10., 10.)).inert(),
column(vec![
assert_area!(Area::new(45., 40., 10., 10.))
.width(10.)
.height(10.),
]),
]),
stack(vec![
assert_area!(Area::new(45., 50., 10., 10.)).inert(),
row(vec![
assert_area!(Area::new(45., 50., 10., 10.))
.width(10.)
.height(10.),
]),
]),
]),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
}
mod layer_tests {
use super::*;
#[test]
fn test_layer_sorts_by_value() {
let values = stack(vec![
draw(|_, _| vec![1]).layer(5),
draw(|_, _| vec![2]).layer(-5),
draw(|_, _| vec![3]).layer(0),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
assert_eq!(
values,
vec![2, 3, 1],
"Draws sorted by layer value ascending"
);
}
#[test]
fn test_layer_same_value_preserves_order() {
let values = stack(vec![
draw(|_, _| vec![1]).layer(5),
draw(|_, _| vec![2]).layer(5),
draw(|_, _| vec![3]).layer(5),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
assert_eq!(
values,
vec![1, 2, 3],
"Same layer values preserve insertion order"
);
}
#[test]
fn test_layer_child_overrides_parent() {
let values = stack(vec![
stack(vec![draw(|_, _| vec![1]).layer(10)]).layer(1),
draw(|_, _| vec![2]).layer(5),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
assert_eq!(
values,
vec![2, 1],
"Child layer values override parent context"
);
}
#[test]
fn test_layer_nested_inherits_context() {
let values = stack(vec![
stack(vec![draw(|_, _| vec![1]), draw(|_, _| vec![2])]).layer(1),
draw(|_, _| vec![3]).layer(2),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
assert_eq!(
values,
vec![1, 2, 3],
"Nested draws inherit parent layer when not explicitly set"
);
}
}
impl<D> Layout<'_, D> {
fn debug_visualize(&mut self, available_area: Area) {
fn visualize_areas(areas: &[Area], bounds: Area) {
if areas.is_empty() {
return;
}
let scale_x = 0.5;
let scale_y = 0.18;
let grid_width = (bounds.width * scale_x).ceil() as usize;
let grid_height = (bounds.height * scale_y).ceil() as usize;
let mut grid = vec![vec![' '; grid_width]; grid_height];
draw_border(&mut grid);
for (i, area) in areas.iter().enumerate() {
let char_to_use = char::from_digit((i % 10) as u32, 10).unwrap_or('*');
draw_box(&mut grid, *area, bounds, scale_x, scale_y, char_to_use);
}
println!("{}", grid_to_ascii(&grid));
}
fn draw_border(grid: &mut [Vec<char>]) {
if grid.is_empty() || grid[0].is_empty() {
return;
}
let height = grid.len();
let width = grid[0].len();
grid[0].iter_mut().for_each(|c| *c = '─');
if height > 1 {
grid[height - 1].iter_mut().for_each(|c| *c = '─');
}
for row in grid.iter_mut() {
row[0] = '│';
if width > 1 {
row[width - 1] = '│';
}
}
if width > 0 && height > 0 {
grid[0][0] = '┌';
if width > 1 {
grid[0][width - 1] = '┐';
}
if height > 1 {
grid[height - 1][0] = '└';
if width > 1 {
grid[height - 1][width - 1] = '┘';
}
}
}
}
fn draw_box(
grid: &mut [Vec<char>],
area: Area,
bounds: Area,
scale_x: f32,
scale_y: f32,
ch: char,
) {
let start_x = ((area.x - bounds.x) * scale_x).max(0.0) as usize;
let start_y = ((area.y - bounds.y) * scale_y).max(0.0) as usize;
let end_x =
((area.x + area.width - bounds.x) * scale_x).min(grid[0].len() as f32) as usize;
let end_y =
((area.y + area.height - bounds.y) * scale_y).min(grid.len() as f32) as usize;
if start_x >= end_x || start_y >= end_y {
return;
}
for y in (start_y + 1)..end_y.saturating_sub(1) {
for x in (start_x + 1)..end_x.saturating_sub(1) {
if y < grid.len() && x < grid[0].len() {
grid[y][x] = ch;
}
}
}
for x in start_x..end_x {
if start_y < grid.len() && x < grid[0].len() {
grid[start_y][x] = '─';
}
if end_y > 0 && end_y - 1 < grid.len() && x < grid[0].len() {
grid[end_y - 1][x] = '─';
}
}
for y in start_y..end_y {
if y < grid.len() && start_x < grid[0].len() {
grid[y][start_x] = '│';
}
if y < grid.len() && end_x > 0 && end_x - 1 < grid[0].len() {
grid[y][end_x - 1] = '│';
}
}
if start_y < grid.len() && start_x < grid[0].len() {
grid[start_y][start_x] = '┌';
}
if start_y < grid.len() && end_x > 0 && end_x - 1 < grid[0].len() {
grid[start_y][end_x - 1] = '┐';
}
if end_y > 0 && end_y - 1 < grid.len() && start_x < grid[0].len() {
grid[end_y - 1][start_x] = '└';
}
if end_y > 0 && end_y - 1 < grid.len() && end_x > 0 && end_x - 1 < grid[0].len() {
grid[end_y - 1][end_x - 1] = '┘';
}
}
fn grid_to_ascii(grid: &[Vec<char>]) -> String {
let mut result = String::new();
for row in grid {
result.extend(row.iter());
result.push('\n');
}
result
}
let mut area_layout = self.to_area_layout();
visualize_areas(
&area_layout
.draw(available_area, &mut ())
.into_iter()
.rev()
.collect::<Vec<_>>(),
Area::new(0., 0., 100., 100.),
);
}
fn to_area_layout(&self) -> Layout<'static, Area> {
use crate::types::LayoutType;
fn transform_node<D>(node: &Layout<'_, D>) -> Layout<'static, Area> {
let new_layout = match &node.layout {
LayoutType::Draw(_) => LayoutType::Draw(Some(Box::new(|area, _| vec![area]))),
LayoutType::Column {
spacing,
x_align,
y_align,
} => LayoutType::Column {
spacing: *spacing,
x_align: *x_align,
y_align: *y_align,
},
LayoutType::Row {
spacing,
x_align,
y_align,
} => LayoutType::Row {
spacing: *spacing,
x_align: *x_align,
y_align: *y_align,
},
LayoutType::Stack { x_align, y_align } => LayoutType::Stack {
x_align: *x_align,
y_align: *y_align,
},
LayoutType::Padding {
leading,
trailing,
top,
bottom,
} => LayoutType::Padding {
leading: *leading,
trailing: *trailing,
top: *top,
bottom: *bottom,
},
LayoutType::Offset { x, y } => LayoutType::Offset { x: *x, y: *y },
LayoutType::Space => LayoutType::Space,
LayoutType::Empty => LayoutType::Empty,
};
Layout {
layout: new_layout,
constraints: node.constraints,
layer: None,
dynamic_constraints: Default::default(),
resolved: node.resolved,
allocated: node.allocated,
children: node.children.iter().map(transform_node).collect(),
}
}
transform_node(self)
}
}
mod dynamic_tests {
use super::*;
#[test]
fn test_expands_nested_nodes() {
let values: Vec<usize> = draw(|area, s: &mut ()| {
draw(|area, s: &mut ()| {
draw(|_area, _: &mut ()| {
vec![1]
})
.draw(area, s)
})
.draw(area, s)
})
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
assert_eq!(values.len(), 1);
assert_eq!(values[0], 1);
}
#[test]
fn test_draws_all_expanded_nodes() {
let values = draw(|area, s: &mut ()| {
stack(vec![
stack(vec![
draw(|area, s: &mut ()| draw(|_area, _: &mut ()| vec![1]).draw(area, s)),
draw(|_, _| vec![1]),
]),
draw(|_, _| vec![1]),
])
.draw(area, s)
})
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
assert_eq!(values.len(), 3);
}
#[test]
fn test_simple_column_layout() {
column(vec![
draw(|a, _| {
assert_eq!(a.width, 100.0);
assert_eq!(a.height, 50.0);
vec![()]
})
.height(50.0),
draw(|a, _| {
assert_eq!(a.width, 100.0);
assert_eq!(a.height, 50.0);
assert_eq!(a.y, 50.0);
vec![()]
})
.height(50.0),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_simple_row_layout() {
row(vec![
draw(|a, _| {
assert_eq!(a.width, 50.0);
assert_eq!(a.height, 100.0);
vec![()]
})
.width(50.0),
draw(|a, _| {
assert_eq!(a.width, 50.0);
assert_eq!(a.height, 100.0);
assert_eq!(a.x, 50.0);
vec![()]
})
.width(50.0),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_nested_layout() {
column(vec![
row(vec![
draw(|a, _| {
assert_eq!(a.width, 50.0);
assert_eq!(a.height, 25.0);
vec![()]
})
.width(50.0),
draw(|a, _| {
assert_eq!(a.width, 50.0);
assert_eq!(a.height, 25.0);
assert_eq!(a.x, 50.0);
vec![()]
})
.width(50.0),
])
.height(25.0),
draw(|a, _| {
assert_eq!(a.width, 100.0);
assert_eq!(a.height, 75.0);
assert_eq!(a.y, 25.0);
vec![()]
})
.height(75.0),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_padding() {
draw(|a, _| {
assert_eq!(a.x, 10.0);
assert_eq!(a.y, 10.0);
assert_eq!(a.width, 80.0);
assert_eq!(a.height, 80.0);
vec![()]
})
.pad(10.0)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_stack_layout() {
let values = stack(vec![
draw(|a, _| {
assert_eq!(a.width, 100.0);
assert_eq!(a.height, 100.0);
vec![1]
}),
draw(|a, _| {
assert_eq!(a.width, 100.0);
assert_eq!(a.height, 100.0);
vec![1]
}),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
assert_eq!(values.len(), 2);
}
#[test]
fn test_dynamic_node() {
draw(|a, _| {
assert_eq!(a.width, 100.0);
vec![()]
})
.height(50.0)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_dynamic_node_drawing_issue() {
let values = column(vec![
draw(|_, _| vec!["dynamic_child_1".to_string()]).height(20.0),
draw(|_, _| vec!["static_draw".to_string()]).height(30.0),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
println!("Draw calls: {:?}", values);
assert!(
!values.is_empty(),
"Dynamic node children should have been drawn"
);
assert!(
values.contains(&"static_draw".to_string()),
"Static draw should be called"
);
}
#[test]
fn test_nested_dynamic_nodes() {
let values = column(vec![
draw(|_, _| vec!["outer_before".to_string()]).height(10.0),
draw(|_, _| vec!["inner_1".to_string()]).height(20.0),
draw(|_, _| vec!["outer_after".to_string()]).height(15.0),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
println!("Nested draw calls: {:?}", values);
assert!(
values.contains(&"outer_before".to_string()),
"Outer before should be drawn"
);
assert!(
values.contains(&"outer_after".to_string()),
"Outer after should be drawn"
);
assert!(
values.iter().any(|call| call.starts_with("inner_")),
"Inner dynamic should be drawn"
);
}
#[test]
fn test_row_dynamic() {
column(vec![
row(vec![
space().height(0.),
assert_area!(Area::new(50., 0., 50., 20.)).height(20.),
]),
assert_area!(Area::new(0., 20., 100., 80.)),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_static_vs_dynamic_height() {
column(vec![
row(vec![
space().height(0.),
assert_area!(Area::new(50., 0., 50., 30.)).height(30.),
]),
assert_area!(Area::new(0., 30., 100., 70.)),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
column(vec![
row(vec![
space().height(0.),
assert_area!(Area::new(50., 0., 50., 30.)).height(30.),
]),
assert_area!(Area::new(0., 30., 100., 70.)),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
}
#[cfg(test)]
mod layout_tests {
use super::*;
#[test]
fn test_seq_align_on_axis() {
row_aligned(
Align::Leading,
vec![
assert_area!(Area::new(0., 0., 10., 100.)).width(10.),
assert_area!(Area::new(10., 0., 30., 100.)).width(30.),
],
)
.expand()
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
row(vec![
assert_area!(Area::new(30., 0., 10., 100.)).width(10.),
assert_area!(Area::new(40., 0., 30., 100.)).width(30.),
])
.align(Align::CenterX)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
row_aligned(
Align::Trailing,
vec![
assert_area!(Area::new(60., 0., 10., 100.)).width(10.),
assert_area!(Area::new(70., 0., 30., 100.)).width(30.),
],
)
.expand()
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
column_aligned(
Align::Top,
vec![
assert_area!(Area::new(0., 0., 100., 10.)).height(10.),
assert_area!(Area::new(0., 10., 100., 30.)).height(30.),
],
)
.expand()
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
column(vec![
assert_area!(Area::new(0., 30., 100., 10.)).height(10.),
assert_area!(Area::new(0., 40., 100., 30.)).height(30.),
])
.align(Align::CenterY)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
column_aligned(
Align::Bottom,
vec![
assert_area!(Area::new(0., 60., 100., 10.)).height(10.),
assert_area!(Area::new(0., 70., 100., 30.)).height(30.),
],
)
.expand()
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_seq_align_off_axis() {
column_aligned(
Align::Leading,
vec![
assert_area!(Area::new(0., 0., 10., 50.)).width(10.),
assert_area!(Area::new(0., 50., 30., 50.)).width(30.),
],
)
.expand()
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
column(vec![
assert_area!(Area::new(45., 0., 10., 50.)).width(10.),
assert_area!(Area::new(35., 50., 30., 50.)).width(30.),
])
.align(Align::CenterX)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
column_aligned(
Align::Trailing,
vec![
assert_area!(Area::new(90., 0., 10., 50.)).width(10.),
assert_area!(Area::new(70., 50., 30., 50.)).width(30.),
],
)
.expand()
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
row_aligned(
Align::Top,
vec![
assert_area!(Area::new(0., 0., 50., 10.)).height(10.),
assert_area!(Area::new(50., 0., 50., 30.)).height(30.),
],
)
.expand()
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
row(vec![
assert_area!(Area::new(0., 45., 50., 10.)).height(10.),
assert_area!(Area::new(50., 35., 50., 30.)).height(30.),
])
.align(Align::CenterY)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
row_aligned(
Align::Bottom,
vec![
assert_area!(Area::new(0., 90., 50., 10.)).height(10.),
assert_area!(Area::new(50., 70., 50., 30.)).height(30.),
],
)
.expand()
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_seq_align_on_axis_nested_seq() {
row_aligned(
Align::Leading,
vec![
row(vec![assert_area!(Area::new(0., 0., 10., 100.)).width(10.)]),
assert_area!(Area::new(10., 0., 30., 100.)).width(30.),
],
)
.expand()
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
row(vec![
row(vec![assert_area!(Area::new(30., 0., 10., 100.)).width(10.)]),
assert_area!(Area::new(40., 0., 30., 100.)).width(30.),
])
.align(Align::CenterX)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
row_aligned(
Align::Trailing,
vec![
row(vec![assert_area!(Area::new(60., 0., 10., 100.)).width(10.)]),
assert_area!(Area::new(70., 0., 30., 100.)).width(30.),
],
)
.expand()
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
column_aligned(
Align::Top,
vec![
row(vec![assert_area!(Area::new(0., 0., 100., 10.)).height(10.)]),
assert_area!(Area::new(0., 10., 100., 30.)).height(30.),
],
)
.expand()
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
column(vec![
row(vec![
assert_area!(Area::new(0., 30., 100., 10.)).height(10.),
]),
assert_area!(Area::new(0., 40., 100., 30.)).height(30.),
])
.align(Align::CenterY)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
column_aligned(
Align::Bottom,
vec![
row(vec![
assert_area!(Area::new(0., 60., 100., 10.)).height(10.),
]),
assert_area!(Area::new(0., 70., 100., 30.)).height(30.),
],
)
.expand()
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_seq_align_off_axis_nested_seq() {
column_aligned(
Align::Leading,
vec![
row(vec![assert_area!(Area::new(0., 0., 10., 50.)).width(10.)]),
assert_area!(Area::new(0., 50., 30., 50.)).width(30.),
],
)
.expand()
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
column(vec![
row(vec![assert_area!(Area::new(45., 0., 10., 50.)).width(10.)]),
assert_area!(Area::new(35., 50., 30., 50.)).width(30.),
])
.align(Align::CenterX)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
column_aligned(
Align::Trailing,
vec![
row(vec![assert_area!(Area::new(90., 0., 10., 50.)).width(10.)]),
assert_area!(Area::new(70., 50., 30., 50.)).width(30.),
],
)
.expand()
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
row_aligned(
Align::Top,
vec![
row(vec![assert_area!(Area::new(0., 0., 50., 10.)).height(10.)]),
assert_area!(Area::new(50., 0., 50., 30.)).height(30.),
],
)
.expand()
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
row(vec![
row(vec![assert_area!(Area::new(0., 45., 50., 10.)).height(10.)]),
assert_area!(Area::new(50., 35., 50., 30.)).height(30.),
])
.align(Align::CenterY)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
row_aligned(
Align::Bottom,
vec![
row(vec![assert_area!(Area::new(0., 90., 50., 10.)).height(10.)]),
assert_area!(Area::new(50., 70., 50., 30.)).height(30.),
],
)
.expand()
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_aspect_ratio() {
assert_area!(Area::new(0., 0., 100., 100.))
.aspect_width(1.)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
assert_area!(Area::new(25., 0., 50., 100.))
.aspect_width(0.5)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
assert_area!(Area::new(0., 0., 50., 100.))
.aspect_width(0.5)
.align(Align::Leading)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
assert_area!(Area::new(50., 0., 50., 100.))
.aspect_width(0.5)
.align(Align::Trailing)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
assert_area!(Area::new(0., 25., 100., 50.))
.aspect_height(2.)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
assert_area!(Area::new(0., 0., 100., 50.))
.aspect_height(2.)
.align(Align::Top)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
assert_area!(Area::new(0., 50., 100., 50.))
.aspect_height(2.)
.align(Align::Bottom)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_aspect_ratio_in_seq() {
row(vec![
assert_area!(Area::new(0., 0., 100., 100.)).aspect_width(1.),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
stack(vec![
assert_area!(Area::new(25., 0., 50., 100.)).aspect_width(0.5),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
column(vec![
assert_area!(Area::new(0., 0., 50., 100.))
.aspect_width(0.5)
.align(Align::Leading),
])
.expand()
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
stack(vec![
assert_area!(Area::new(50., 0., 50., 100.))
.aspect_width(0.5)
.align(Align::Trailing),
])
.expand()
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_aspect_ratio_nested() {
column(vec![
assert_area!(Area::new(0., 0., 200., 50.)),
row(vec![
assert_area!(Area::new(0., 50., 150., 50.)),
assert_area!(Area::new(150., 50., 50., 50.)).aspect_width(1.),
]),
])
.draw(Area::new(0.0, 0.0, 200.0, 100.0), &mut ());
}
#[test]
fn test_pad() {
assert_area!(Area::new(10., 10., 80., 80.))
.pad(10.)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
assert_area!(Area::new(10., 0., 80., 100.))
.pad_x(10.)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
assert_area!(Area::new(0., 10., 100., 80.))
.pad_y(10.)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
assert_area!(Area::new(10., 0., 90., 100.))
.pad_leading(10.)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
assert_area!(Area::new(0., 0., 90., 100.))
.pad_trailing(10.)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
assert_area!(Area::new(0., 10., 100., 90.))
.pad_top(10.)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
assert_area!(Area::new(0., 0., 100., 90.))
.pad_bottom(10.)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_aspect_ratio_in_pad() {
assert_area!(Area::new(25., 0., 50., 100.))
.aspect_width(0.5)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
stack(vec![
assert_area!(Area::new(30., 10., 40., 80.))
.aspect_width(0.5)
.pad(10.),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
stack(vec![
assert_area!(Area::new(35., 10., 30., 80.))
.pad(10.)
.aspect_width(0.5),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_aspect_ratio_fit() {
column(vec![
assert_area!(Area::new(0., 0., 100., 50.)),
assert_area!(Area::new(25., 50., 50., 50.)).aspect_width(1.),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
column(vec![
assert_area!(Area::new(25., 0., 50., 50.)).aspect_width(1.),
assert_area!(Area::new(25., 50., 50., 50.)).aspect_width(1.),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
row(vec![
assert_area!(Area::new(0., 0., 50., 100.)),
assert_area!(Area::new(50., 25., 50., 50.)).aspect_height(1.),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
row(vec![
assert_area!(Area::new(0., 25., 50., 50.)).aspect_height(1.),
assert_area!(Area::new(50., 25., 50., 50.)).aspect_height(1.),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_space_expansion() {
row(vec![
assert_area!(Area::new(0., 0., 1., 100.)).width(1.),
space(),
assert_area!(Area::new(998., 0., 1., 100.)).width(1.),
assert_area!(Area::new(999., 0., 1., 100.)).width(1.),
])
.draw(Area::new(0., 0., 1000., 100.), &mut ());
}
#[test]
fn test_explicit_with_padding() {
column(vec![
assert_area!(Area::new(10., 10., 80., 20.))
.height(20.)
.pad(10.),
assert_area!(Area::new(0., 40., 100., 60.)),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_compressed_expanded_respects_lower_bound() {
stack(vec![
assert_area!(Area::new(0., -50., 100., 200.)).height(200.),
assert_area!(Area::new(0., -50., 100., 200.)),
])
.expand()
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
column(vec![
stack(vec![
assert_area!(Area::new(0., -50., 100., 200.)).height(200.),
assert_area!(Area::new(0., -50., 100., 200.)),
])
.expand(),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_compressed_aspect_ratio() {
stack(vec![
assert_area!(Area::new(0., 0., 100., 100.)).inert(),
row(vec![
assert_area!(Area::new(0., 25., 50., 50.)).aspect_width(1.),
assert_area!(Area::new(50., 0., 50., 100.)).width(50.),
]),
])
.debug_visualize(Area::new(0., 0., 100., 100.));
}
#[test]
fn test_dynamic_attached() {
row(vec![
space(),
stack(vec![
assert_area!(Area::new(25., 25., 25., 50.)).inert(),
assert_area!(Area::new(25., 25., 25., 50.)).dynamic_height(|h, _| h * 2.),
]),
space(),
space(),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
}
#[cfg(test)]
mod sequence_tests {
use super::*;
#[test]
fn test_column_basic() {
column(vec![
assert_area!(Area::new(0., 0., 100., 50.)),
assert_area!(Area::new(0., 50., 100., 50.)),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_column_constrained_1() {
column(vec![
assert_area!(Area::new(0., 0., 100., 10.)).height(10.),
assert_area!(Area::new(0., 10., 100., 90.)),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
column(vec![
assert_area!(Area::new(0., 0., 100., 10.)).height(10.),
assert_area!(Area::new(0., 10., 100., 90.)),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_column_constrained_2() {
column(vec![
assert_area!(Area::new(0., 0., 100., 90.)),
assert_area!(Area::new(0., 90., 100., 10.)).height(10.),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
column(vec![
assert_area!(Area::new(0., 0., 100., 90.)),
assert_area!(Area::new(0., 90., 100., 10.)).height(10.),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_row_basic() {
row(vec![
assert_area!(Area::new(0., 0., 50., 100.)),
assert_area!(Area::new(50., 0., 50., 100.)),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_row_constrained_1() {
row(vec![
assert_area!(Area::new(0., 25., 10., 50.))
.width(10.)
.height(50.),
assert_area!(Area::new(10., 0., 90., 100.)),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
row(vec![
assert_area!(Area::new(0., 0., 10., 20.))
.width(10.)
.height(20.)
.align(Align::Top),
assert_area!(Area::new(10., 40., 10., 20.))
.width(10.)
.height(20.),
assert_area!(Area::new(20., 80., 10., 20.))
.width(10.)
.height(20.)
.align(Align::Bottom),
assert_area!(Area::new(30., 0., 70., 100.)),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_row_constrained_2() {
row(vec![
assert_area!(Area::new(0., 0., 70., 100.)),
assert_area!(Area::new(70., 0., 10., 20.))
.width(10.)
.height(20.)
.align(Align::Top),
assert_area!(Area::new(80., 40., 10., 20.))
.width(10.)
.height(20.),
assert_area!(Area::new(90., 80., 10., 20.))
.width(10.)
.height(20.)
.align(Align::Bottom),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
row(vec![
assert_area!(Area::new(0., 0., 70., 100.)),
assert_area!(Area::new(70., 0., 10., 20.))
.width(10.)
.height(20.)
.align(Align::Top),
assert_area!(Area::new(80., 40., 10., 20.))
.width(10.)
.height(20.),
assert_area!(Area::new(90., 80., 10., 20.))
.width(10.)
.height(20.)
.align(Align::Bottom),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_stack_basic() {
stack(vec![
assert_area!(Area::new(0., 0., 100., 100.)),
assert_area!(Area::new(0., 0., 100., 100.)),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_stack_alignment() {
stack(vec![
assert_area!(Area::new(0., 0., 10., 20.))
.width(10.)
.height(20.)
.align(Align::TopLeading),
assert_area!(Area::new(45., 0., 10., 20.))
.width(10.)
.height(20.)
.align(Align::TopCenter),
assert_area!(Area::new(90., 0., 10., 20.))
.width(10.)
.height(20.)
.align(Align::TopTrailing),
assert_area!(Area::new(90., 40., 10., 20.))
.width(10.)
.height(20.)
.align(Align::CenterTrailing),
assert_area!(Area::new(90., 80., 10., 20.))
.width(10.)
.height(20.)
.align(Align::BottomTrailing),
assert_area!(Area::new(45., 80., 10., 20.))
.width(10.)
.height(20.)
.align(Align::BottomCenter),
assert_area!(Area::new(0., 80., 10., 20.))
.width(10.)
.height(20.)
.align(Align::BottomLeading),
assert_area!(Area::new(0., 40., 10., 20.))
.width(10.)
.height(20.)
.align(Align::CenterLeading),
assert_area!(Area::new(45., 40., 10., 20.))
.width(10.)
.height(20.)
.align(Align::CenterCenter),
])
.expand()
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_sequence_spacing() {
row_spaced(
10.,
vec![
assert_area!(Area::new(0., 40., 10., 20.))
.width(10.)
.height(20.),
assert_area!(Area::new(20., 0., 25., 100.)),
assert_area!(Area::new(55., 40., 10., 20.))
.width(10.)
.height(20.),
assert_area!(Area::new(75., 0., 25., 100.)),
],
)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
column_spaced(
10.,
vec![
assert_area!(Area::new(0., 0., 100., 15.)),
assert_area!(Area::new(45., 25., 10., 20.))
.width(10.)
.height(20.),
assert_area!(Area::new(0., 55., 100., 15.)),
assert_area!(Area::new(45., 80., 10., 20.))
.width(10.)
.height(20.),
],
)
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_row_with_constrained_item() {
row(vec![
assert_area!(Area::new(0., 0., 30., 100.)).width(30.),
assert_area!(Area::new(30., 0., 70., 100.)),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_nested_row_with_constrained_item() {
row(vec![
row(vec![
assert_area!(Area::new(0., 0., 20., 100.)).width(20.),
assert_area!(Area::new(20., 0., 30., 100.)),
])
.width(50.),
assert_area!(Area::new(50., 0., 50., 100.)),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_stack_with_constrained_item() {
stack(vec![
assert_area!(Area::new(0., 0., 100., 100.)),
assert_area!(Area::new(25., 25., 50., 50.))
.width(50.)
.height(50.),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_row_with_multiple_constrained_items() {
row(vec![
assert_area!(Area::new(0., 0., 20., 100.)).width(20.),
assert_area!(Area::new(20., 0., 30., 100.)).width(30.),
draw(|a, _| {
assert!((a.x - 50.0).abs() < 0.001);
assert!((a.y - 0.0).abs() < 0.001);
assert!((a.width - 50.0).abs() < 0.001);
assert!((a.height - 100.0).abs() < 0.001);
vec![()]
}),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_row_with_constrained_height_in_column() {
column(vec![
draw(|a, _| {
assert_eq!(a, Area::new(0., 0., 100., 40.));
vec![()]
}),
row(vec![
draw(|a, _| {
assert_eq!(a, Area::new(0., 40., 50., 20.));
vec![()]
})
.height(20.),
draw(|a, _| {
assert_eq!(a, Area::new(50., 40., 50., 20.));
vec![()]
})
.height(20.),
]),
draw(|a, _| {
assert_eq!(a, Area::new(0., 60., 100., 40.));
vec![()]
}),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_configurable_depth_tree() {
fn build_tree(depth: usize) -> Layout<'static, Area> {
if depth == 0 {
draw(|area, _| vec![area])
} else {
column(vec![draw(|area, _| vec![area]), build_tree(depth - 1)])
}
}
let mut layout = build_tree(500);
let leaves = layout.draw(Area::new(0., 0., 300., 300.), &mut ());
assert_eq!(leaves.len(), 501);
}
}
mod inert_tests {
use super::*;
#[test]
fn test_dropdown_stack_approach() {
let column_width = 50.;
column(vec![
stack(vec![
draw(move |a, _| {
assert_eq!(a.width, column_width);
vec![()]
})
.inert(),
draw(|_, _| vec![()])
.dynamic_height(|_, _| 30.)
.width(30.)
.pad_x(5.),
])
.expand_x(),
stack(vec![
draw(move |a, _| {
assert_eq!(a.width, column_width);
vec![()]
})
.inert(),
draw(|_, _| vec![()]).dynamic_height(|_, _| 30.).width(50.),
]),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_transparent_in_column() {
column(vec![
assert_area!(Area::new(25., 25., 50., 50.))
.width(50.)
.height(50.),
draw(move |a, _| {
assert_eq!(a.width, 50.);
assert_eq!(a.height, 0.);
assert_eq!(a.y, 75.);
vec![()]
})
.inert(),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_transparent_with_constraints() {
stack(vec![
draw(move |a, _| {
assert_eq!(a.width, 50.);
assert_eq!(a.height, 50.);
vec![()]
})
.width(50.)
.height(50.)
.inert(),
draw(|_, _| vec![()]).width(10.).height(10.),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
column(vec![
draw(|_, _| vec![()]).width(10.).height(10.),
draw(move |a, _| {
assert_eq!(a.width, 50.);
assert_eq!(a.height, 50.);
vec![]
})
.width(50.)
.height(50.)
.inert(),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
row(vec![
draw(|_, _| vec![()]).width(10.).height(10.),
draw(move |a, _| {
assert_eq!(a.width, 50.);
assert_eq!(a.height, 50.);
vec![]
})
.width(50.)
.height(50.)
.inert(),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
}
mod general_tests {
use super::*;
#[test]
fn test_dynamic_height_width_dependent_pad() {
let content_area = Area::new(10., 43., 80., 14.);
let bg_area = Area::new(0., 33., 100., 34.);
stack(vec![
assert_area!(bg_area).inert(),
stack(vec![
draw(|_, _| vec![()]).inert(),
assert_area!(content_area).dynamic_height(|_, _| 14.),
])
.pad(10.),
])
.draw(Area::new(0.0, 0.0, 100.0, 100.0), &mut ());
}
#[test]
fn test_row_spaced_stack_with_unconstrained_child_preserves_spacing() {
row_spaced(
10.,
vec![
assert_area!(Area::new(0., 0., 30., 100.)),
stack(vec![
assert_area!(Area::new(40., 0., 80., 100.)),
draw(|_, _| vec![()]).width(80.).height(14.),
]),
],
)
.draw(Area::new(0., 0., 120., 100.), &mut ());
}
#[test]
fn test_row_inert_y_does_not_expand_past_constrained_height() {
stack(vec![
assert_area!(Area::new(0., 35., 100., 30.)).inert(),
row_spaced(
10.,
vec![
space().inert_y(),
assert_area!(Area::new(70., 35., 30., 30.))
.width(30.)
.height(30.),
],
),
])
.draw(Area::new(0., 0., 100., 100.), &mut ());
}
}
mod map_tests {
use super::*;
enum View {
Button(Area),
Label(Area),
}
#[test]
fn test_map_subtree_into_parent() {
let button_bar: Layout<'_, Area> = row(vec![
draw(|a, _: &mut ()| vec![a]).width(50.).height(30.),
draw(|a, _: &mut ()| vec![a]).width(50.).height(30.),
]);
let mut layout: Layout<'_, View> = column(vec![
button_bar.map(View::Button),
draw(|a, _| vec![View::Label(a)]).height(40.),
]);
let result = layout.draw(Area::new(0., 0., 100., 100.), &mut ());
let mut buttons = Vec::new();
let mut labels = Vec::new();
for v in &result {
match v {
View::Button(a) => buttons.push(a),
View::Label(a) => labels.push(a),
}
}
assert_eq!(buttons.len(), 2);
assert_eq!(labels.len(), 1);
}
}
}