use super::*;
fn renderer_line_height(
ascent: f32,
descent: f32,
leading: f32,
line_height_mod: f32,
scale: f32,
) -> f32 {
let cell_height = ((ascent + descent + leading) * scale).ceil();
cell_height * line_height_mod
}
fn sugar_height(
ascent: f32,
descent: f32,
leading: f32,
line_height_mod: f32,
scale: f32,
) -> f32 {
((ascent + descent + leading) * line_height_mod * scale).ceil()
}
#[allow(clippy::too_many_arguments)]
fn assert_rows_fit(
panel_width: f32,
panel_height: f32,
sugar_width: f32,
scale: f32,
line_height_mod: f32,
ascent: f32,
descent: f32,
leading: f32,
) {
let sh = sugar_height(ascent, descent, leading, line_height_mod, scale);
let dimensions = TextDimensions {
width: sugar_width,
height: sh,
scale,
};
let (cols, rows) = compute(
panel_width,
panel_height,
dimensions,
line_height_mod,
Margin::all(0.0),
);
let actual_line_height =
renderer_line_height(ascent, descent, leading, line_height_mod, scale);
let rendered_height = rows as f32 * actual_line_height;
assert!(
rendered_height <= panel_height,
"Rows overflow! {} rows * {:.2}px = {:.2}px rendered, but panel is only {:.2}px tall \
(cols={}, sugar={:.2}x{:.2}, scale={:.1}, lh_mod={:.1})",
rows,
actual_line_height,
rendered_height,
panel_height,
cols,
sugar_width,
sh,
scale,
line_height_mod,
);
}
#[test]
fn test_user_case_1834x1436() {
assert_rows_fit(1834.0, 1436.0, 16.41, 2.0, 1.0, 13.0, 3.5, 0.0);
}
#[test]
fn test_user_case_3766x1996() {
assert_rows_fit(3766.0, 1996.0, 16.41, 2.0, 1.0, 13.0, 3.5, 0.0);
}
#[test]
fn test_user_case_5104x2736() {
assert_rows_fit(5104.0, 2736.0, 16.41, 2.0, 1.0, 13.0, 3.5, 0.0);
}
#[test]
fn test_rows_fit_various_sizes() {
for height in (500..=3000).step_by(50) {
for width in [800.0, 1600.0, 2400.0, 3200.0] {
assert_rows_fit(width, height as f32, 16.41, 2.0, 1.0, 13.0, 3.5, 0.0);
}
}
}
#[test]
fn test_rows_fit_with_nonzero_leading() {
let test_cases: Vec<(f32, f32, f32)> = vec![
(12.0, 3.0, 0.5),
(12.0, 3.0, 1.0),
(14.0, 4.0, 0.25),
(10.0, 3.0, 2.0),
];
for (ascent, descent, leading) in test_cases {
for height in (500..=2000).step_by(100) {
assert_rows_fit(
1600.0,
height as f32,
16.0,
2.0,
1.0,
ascent,
descent,
leading,
);
}
}
}
#[test]
fn test_rows_fit_with_line_height_modifier() {
for lh_mod in [1.1, 1.2, 1.5, 2.0] {
for height in (500..=2000).step_by(100) {
assert_rows_fit(1600.0, height as f32, 16.0, 2.0, lh_mod, 12.0, 3.0, 0.5);
}
}
}
#[test]
fn test_rows_fit_scale_1() {
for height in (300..=1200).step_by(50) {
assert_rows_fit(800.0, height as f32, 8.0, 1.0, 1.0, 13.0, 3.5, 0.0);
}
}
#[test]
fn test_rows_fit_zero_leading() {
for height in (500..=2000).step_by(100) {
assert_rows_fit(1600.0, height as f32, 16.0, 2.0, 1.0, 12.77, 3.50, 0.0);
}
}
#[test]
fn test_rows_fit_fractional_metrics() {
assert_rows_fit(1600.0, 1000.0, 16.0, 2.0, 1.0, 12.3, 3.4, 0.1);
assert_rows_fit(1600.0, 1000.0, 16.0, 2.0, 1.0, 11.9, 4.6, 0.3);
assert_rows_fit(1600.0, 1000.0, 16.0, 1.5, 1.0, 12.0, 3.0, 0.5);
}
#[test]
fn test_compute_returns_min_for_zero_dimensions() {
let dims = TextDimensions {
width: 16.0,
height: 32.0,
scale: 2.0,
};
let (cols, rows) = compute(0.0, 0.0, dims, 1.0, Margin::all(0.0));
assert_eq!(cols, MIN_COLS);
assert_eq!(rows, MIN_LINES);
}
#[test]
fn test_compute_returns_min_for_negative_dimensions() {
let dims = TextDimensions {
width: 16.0,
height: 32.0,
scale: 2.0,
};
let (cols, rows) = compute(-100.0, -100.0, dims, 1.0, Margin::all(0.0));
assert_eq!(cols, MIN_COLS);
assert_eq!(rows, MIN_LINES);
}
#[test]
fn test_compute_returns_min_for_zero_scale() {
let dims = TextDimensions {
width: 16.0,
height: 32.0,
scale: 0.0,
};
let (cols, rows) = compute(1600.0, 900.0, dims, 1.0, Margin::all(0.0));
assert_eq!(cols, MIN_COLS);
assert_eq!(rows, MIN_LINES);
}
#[test]
fn test_compute_basic_grid() {
let dims = TextDimensions {
width: 16.0,
height: 33.0,
scale: 2.0,
};
let (cols, rows) = compute(1600.0, 825.0, dims, 1.0, Margin::all(0.0));
assert_eq!(cols, 100);
assert_eq!(rows, 25);
}
#[test]
fn test_compute_floors_fractional_rows() {
let dims = TextDimensions {
width: 16.0,
height: 33.0,
scale: 1.0,
};
let (_, rows) = compute(1600.0, 840.0, dims, 1.0, Margin::all(0.0));
assert_eq!(rows, 25);
}
#[test]
fn test_compute_respects_margins() {
let dims = TextDimensions {
width: 16.0,
height: 32.0,
scale: 2.0,
};
let margin = Margin::new(0.0, 10.0, 0.0, 10.0);
let (cols, _) = compute(1600.0, 800.0, dims, 1.0, margin);
assert_eq!(cols, 97);
}
#[test]
fn test_compute_margin_exceeds_size() {
let dims = TextDimensions {
width: 16.0,
height: 32.0,
scale: 2.0,
};
let margin = Margin::new(0.0, 0.0, 0.0, 1000.0);
let (cols, rows) = compute(100.0, 800.0, dims, 1.0, margin);
assert_eq!(cols, MIN_COLS);
assert_eq!(rows, MIN_LINES);
}
#[test]
fn test_context_dimension_build() {
let dims = TextDimensions {
width: 16.0,
height: 33.0,
scale: 2.0,
};
let cd = ContextDimension::build(1650.0, 825.0, dims, 1.0, Margin::all(0.0));
assert_eq!(cd.columns, 103);
assert_eq!(cd.lines, 25);
}
#[test]
fn test_context_dimension_update_width() {
let dims = TextDimensions {
width: 16.0,
height: 33.0,
scale: 2.0,
};
let mut cd = ContextDimension::build(1600.0, 825.0, dims, 1.0, Margin::all(0.0));
assert_eq!(cd.columns, 100);
cd.update_width(800.0);
assert_eq!(cd.columns, 50);
assert_eq!(cd.lines, 25);
}
#[test]
fn test_context_dimension_update_height() {
let dims = TextDimensions {
width: 16.0,
height: 33.0,
scale: 2.0,
};
let mut cd = ContextDimension::build(1600.0, 825.0, dims, 1.0, Margin::all(0.0));
assert_eq!(cd.lines, 25);
cd.update_height(660.0);
assert_eq!(cd.lines, 20);
assert_eq!(cd.columns, 100);
}
#[test]
fn test_context_dimension_update_dimensions() {
let dims = TextDimensions {
width: 16.0,
height: 33.0,
scale: 1.0,
};
let mut cd = ContextDimension::build(1600.0, 825.0, dims, 1.0, Margin::all(0.0));
assert_eq!(cd.lines, 25);
let new_dims = TextDimensions {
width: 16.0,
height: 66.0,
scale: 1.0,
};
cd.update_dimensions(new_dims);
assert_eq!(cd.lines, 12); }
#[test]
fn test_panel_resize_preserves_proportions_on_window_resize() {
use taffy::{FlexDirection, TaffyTree};
let mut tree: TaffyTree<()> = TaffyTree::new();
let initial_width = 1000.0;
let root = tree
.new_leaf(Style {
display: Display::Flex,
flex_direction: FlexDirection::Row,
size: geometry::Size {
width: length(initial_width),
height: length(800.0),
},
..Default::default()
})
.unwrap();
let left = tree
.new_leaf(Style {
display: Display::Flex,
flex_grow: 1.0,
flex_shrink: 1.0,
..Default::default()
})
.unwrap();
let right = tree
.new_leaf(Style {
display: Display::Flex,
flex_grow: 1.0,
flex_shrink: 1.0,
..Default::default()
})
.unwrap();
tree.add_child(root, left).unwrap();
tree.add_child(root, right).unwrap();
tree.compute_layout(
root,
geometry::Size {
width: AvailableSpace::MaxContent,
height: AvailableSpace::MaxContent,
},
)
.unwrap();
let left_w = tree.layout(left).unwrap().size.width;
let right_w = tree.layout(right).unwrap().size.width;
assert!(
(left_w - 500.0).abs() < 1.0,
"left should be ~500, got {left_w}"
);
assert!(
(right_w - 500.0).abs() < 1.0,
"right should be ~500, got {right_w}"
);
let mut left_style = tree.style(left).unwrap().clone();
left_style.flex_basis = length(0.0);
left_style.flex_grow = 800.0;
left_style.flex_shrink = 1.0;
tree.set_style(left, left_style).unwrap();
let mut right_style = tree.style(right).unwrap().clone();
right_style.flex_basis = length(0.0);
right_style.flex_grow = 200.0;
right_style.flex_shrink = 1.0;
tree.set_style(right, right_style).unwrap();
tree.compute_layout(
root,
geometry::Size {
width: AvailableSpace::MaxContent,
height: AvailableSpace::MaxContent,
},
)
.unwrap();
let left_w = tree.layout(left).unwrap().size.width;
let right_w = tree.layout(right).unwrap().size.width;
assert!(
(left_w - 800.0).abs() < 1.0,
"left should be 800, got {left_w}"
);
assert!(
(right_w - 200.0).abs() < 1.0,
"right should be 200, got {right_w}"
);
let new_width = 1200.0;
let mut root_style = tree.style(root).unwrap().clone();
root_style.size.width = length(new_width);
tree.set_style(root, root_style).unwrap();
tree.compute_layout(
root,
geometry::Size {
width: AvailableSpace::MaxContent,
height: AvailableSpace::MaxContent,
},
)
.unwrap();
let left_w = tree.layout(left).unwrap().size.width;
let right_w = tree.layout(right).unwrap().size.width;
let expected_left = new_width * 0.8;
let expected_right = new_width * 0.2;
assert!(
(left_w - expected_left).abs() < 1.0,
"After resize, left should be ~{expected_left} (80%), got {left_w}"
);
assert!(
(right_w - expected_right).abs() < 1.0,
"After resize, right should be ~{expected_right} (20%), got {right_w}"
);
}
#[test]
fn test_split_inside_resized_panel_preserves_proportions() {
use taffy::{FlexDirection, TaffyTree};
let mut tree: TaffyTree<()> = TaffyTree::new();
let root = tree
.new_leaf(Style {
display: Display::Flex,
flex_direction: FlexDirection::Row,
size: geometry::Size {
width: length(1000.0),
height: length(800.0),
},
..Default::default()
})
.unwrap();
let left = tree
.new_leaf(Style {
display: Display::Flex,
flex_grow: 1.0,
flex_shrink: 1.0,
..Default::default()
})
.unwrap();
let right = tree
.new_leaf(Style {
display: Display::Flex,
flex_grow: 1.0,
flex_shrink: 1.0,
..Default::default()
})
.unwrap();
tree.add_child(root, left).unwrap();
tree.add_child(root, right).unwrap();
let mut left_style = tree.style(left).unwrap().clone();
left_style.flex_basis = length(0.0);
left_style.flex_grow = 200.0;
left_style.flex_shrink = 1.0;
tree.set_style(left, left_style).unwrap();
let mut right_style = tree.style(right).unwrap().clone();
right_style.flex_basis = length(0.0);
right_style.flex_grow = 800.0;
right_style.flex_shrink = 1.0;
tree.set_style(right, right_style).unwrap();
let available = geometry::Size {
width: AvailableSpace::MaxContent,
height: AvailableSpace::MaxContent,
};
tree.compute_layout(root, available).unwrap();
let left_w = tree.layout(left).unwrap().size.width;
let right_w = tree.layout(right).unwrap().size.width;
assert!(
(left_w - 200.0).abs() < 1.0,
"left should be 200, got {left_w}"
);
assert!(
(right_w - 800.0).abs() < 1.0,
"right should be 800, got {right_w}"
);
let right_inherited = tree.style(right).unwrap().clone();
let container = tree
.new_leaf(Style {
display: Display::Flex,
flex_direction: FlexDirection::Column,
flex_basis: right_inherited.flex_basis,
flex_grow: right_inherited.flex_grow,
flex_shrink: right_inherited.flex_shrink,
..Default::default()
})
.unwrap();
let mut reset_right = right_inherited;
reset_right.flex_basis = taffy::Dimension::auto();
reset_right.flex_grow = 1.0;
reset_right.flex_shrink = 1.0;
tree.set_style(right, reset_right).unwrap();
let bottom = tree
.new_leaf(Style {
display: Display::Flex,
flex_grow: 1.0,
flex_shrink: 1.0,
..Default::default()
})
.unwrap();
tree.remove_child(root, right).unwrap();
tree.add_child(container, right).unwrap();
tree.add_child(container, bottom).unwrap();
tree.add_child(root, container).unwrap();
tree.compute_layout(root, available).unwrap();
let container_w = tree.layout(container).unwrap().size.width;
assert!(
(container_w - 800.0).abs() < 1.0,
"Container should keep 80% (800px), got {container_w}"
);
let left_w = tree.layout(left).unwrap().size.width;
assert!(
(left_w - 200.0).abs() < 1.0,
"Left should keep 20% (200px), got {left_w}"
);
let right_h = tree.layout(right).unwrap().size.height;
let bottom_h = tree.layout(bottom).unwrap().size.height;
assert!(
(right_h - 400.0).abs() < 1.0,
"Right (top half) should be ~400px tall, got {right_h}"
);
assert!(
(bottom_h - 400.0).abs() < 1.0,
"Bottom (bottom half) should be ~400px tall, got {bottom_h}"
);
}