From 9707996fb26fd7a6878a03e72f16e18c737c657f Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Mon, 14 Aug 2023 15:22:11 +0100 Subject: [PATCH] Fix: Make CSS Grid algorithm correctly apply max width/height and available space when it is the root node (#491) * Add viewport control to gentest setup * Add tests for max-width and available space on a CSS Grid root node * Only treat definite available grid space as indefinite when container size is indefinite, and only do so for the final two track sizing steps. (cherry picked from commit 9958624168dfe671c82cbe3afb8ba6a7d35f7454) --- scripts/gentest/src/main.rs | 116 +++++++++++------- scripts/gentest/test_base_style.css | 9 ++ scripts/gentest/test_helper.js | 16 +++ src/compute/grid/mod.rs | 17 +-- src/compute/grid/track_sizing.rs | 28 +++-- ...ilable_space_greater_than_max_content.html | 20 +++ ...ilable_space_smaller_than_max_content.html | 20 +++ ...ilable_space_smaller_than_min_content.html | 20 +++ ...id_max_width_greater_than_max_content.html | 20 +++ ...ess_than_max_content_with_min_content.html | 20 +++ ...id_max_width_smaller_than_max_content.html | 20 +++ ...id_max_width_smaller_than_min_content.html | 20 +++ ...vailable_space_greater_than_max_content.rs | 74 +++++++++++ ...vailable_space_smaller_than_max_content.rs | 74 +++++++++++ ...vailable_space_smaller_than_min_content.rs | 74 +++++++++++ ...grid_max_width_greater_than_max_content.rs | 82 +++++++++++++ ..._less_than_max_content_with_min_content.rs | 82 +++++++++++++ ...grid_max_width_smaller_than_max_content.rs | 82 +++++++++++++ ...grid_max_width_smaller_than_min_content.rs | 82 +++++++++++++ tests/generated/mod.rs | 14 +++ 20 files changed, 832 insertions(+), 58 deletions(-) create mode 100644 test_fixtures/grid_available_space_greater_than_max_content.html create mode 100644 test_fixtures/grid_available_space_smaller_than_max_content.html create mode 100644 test_fixtures/grid_available_space_smaller_than_min_content.html create mode 100644 test_fixtures/grid_max_width_greater_than_max_content.html create mode 100644 test_fixtures/grid_max_width_less_than_max_content_with_min_content.html create mode 100644 test_fixtures/grid_max_width_smaller_than_max_content.html create mode 100644 test_fixtures/grid_max_width_smaller_than_min_content.html create mode 100644 tests/generated/grid_available_space_greater_than_max_content.rs create mode 100644 tests/generated/grid_available_space_smaller_than_max_content.rs create mode 100644 tests/generated/grid_available_space_smaller_than_min_content.rs create mode 100644 tests/generated/grid_max_width_greater_than_max_content.rs create mode 100644 tests/generated/grid_max_width_less_than_max_content_with_min_content.rs create mode 100644 tests/generated/grid_max_width_smaller_than_max_content.rs create mode 100644 tests/generated/grid_max_width_smaller_than_min_content.rs diff --git a/scripts/gentest/src/main.rs b/scripts/gentest/src/main.rs index fee14f8ae..615a710ae 100644 --- a/scripts/gentest/src/main.rs +++ b/scripts/gentest/src/main.rs @@ -10,6 +10,47 @@ use quote::{format_ident, quote}; use serde_json::Value; use syn::Ident; +macro_rules! dim_quoted_renamed { + ($obj:ident, $in_name:ident, $out_name:ident, $value_mapper:ident, $default:expr) => { + let $out_name = match $obj.get(stringify!($in_name)) { + Some(Value::Object(ref value)) => { + let dim = $value_mapper(value); + quote!($out_name: #dim,) + } + _ => { + let dim = $default; + quote!($out_name: #dim,) + } + }; + }; +} + +macro_rules! dim_quoted { + ($obj:ident, $dim_name:ident, $value_mapper: ident, $default:expr) => { + dim_quoted_renamed!($obj, $dim_name, $dim_name, $value_mapper, $default) + }; +} + +macro_rules! edges_quoted { + ($style:ident, $val:ident, $value_mapper:ident, $default_value: expr) => { + let $val = match $style[stringify!($val)] { + Value::Object(ref value) => { + dim_quoted!(value, left, $value_mapper, $default_value); + dim_quoted!(value, right, $value_mapper, $default_value); + dim_quoted!(value, top, $value_mapper, $default_value); + dim_quoted!(value, bottom, $value_mapper, $default_value); + + let edges = quote!(taffy::geometry::Rect { + #left #right #top #bottom + }); + + quote!($val: #edges,) + }, + _ => quote!(), + }; + }; +} + #[tokio::main] async fn main() { env_logger::init(); @@ -127,6 +168,20 @@ fn generate_test(name: impl AsRef, description: &Value) -> TokenStream { let set_rounding_mode = if use_rounding { quote!() } else { quote!(taffy.disable_rounding();) }; + // Compute available space + let viewport = &description["viewport"]; + let available_space = if viewport["width"]["unit"] == "max-content" && viewport["height"]["unit"] == "max-content" { + quote!(taffy::geometry::Size::MAX_CONTENT) + } else { + dim_quoted!(viewport, width, generate_available_space, quote!(taffy::style::AvailableSpace::MAX_CONTENT)); + dim_quoted!(viewport, height, generate_available_space, quote!(taffy::style::AvailableSpace::MAX_CONTENT)); + quote!( + taffy::geometry::Size { + #width #height + } + ) + }; + quote!( #[test] fn #name() { @@ -136,7 +191,7 @@ fn generate_test(name: impl AsRef, description: &Value) -> TokenStream { let mut taffy = taffy::Taffy::new(); #set_rounding_mode #node_description - taffy.compute_layout(node, taffy::geometry::Size::MAX_CONTENT).unwrap(); + taffy.compute_layout(node, #available_space).unwrap(); println!("\nComputed tree:"); taffy::debug::print_tree(&taffy, node); @@ -191,47 +246,6 @@ fn generate_assertions(ident: &str, node: &Value, use_rounding: bool) -> TokenSt } } -macro_rules! dim_quoted_renamed { - ($obj:ident, $in_name:ident, $out_name:ident, $value_mapper:ident, $default:expr) => { - let $out_name = match $obj.get(stringify!($in_name)) { - Some(Value::Object(ref value)) => { - let dim = $value_mapper(value); - quote!($out_name: #dim,) - } - _ => { - let dim = $default; - quote!($out_name: #dim,) - } - }; - }; -} - -macro_rules! dim_quoted { - ($obj:ident, $dim_name:ident, $value_mapper: ident, $default:expr) => { - dim_quoted_renamed!($obj, $dim_name, $dim_name, $value_mapper, $default) - }; -} - -macro_rules! edges_quoted { - ($style:ident, $val:ident, $value_mapper:ident, $default_value: expr) => { - let $val = match $style[stringify!($val)] { - Value::Object(ref value) => { - dim_quoted!(value, left, $value_mapper, $default_value); - dim_quoted!(value, right, $value_mapper, $default_value); - dim_quoted!(value, top, $value_mapper, $default_value); - dim_quoted!(value, bottom, $value_mapper, $default_value); - - let edges = quote!(taffy::geometry::Rect { - #left #right #top #bottom - }); - - quote!($val: #edges,) - }, - _ => quote!(), - }; - }; -} - fn generate_node(ident: &str, node: &Value) -> TokenStream { let style = &node["style"]; @@ -660,6 +674,24 @@ fn generate_dimension(dimen: &serde_json::Map) -> TokenStream { } } +fn generate_available_space(dimen: &serde_json::Map) -> TokenStream { + let unit = dimen.get("unit").unwrap(); + let value = || dimen.get("value").unwrap().as_f64().unwrap() as f32; + + match unit { + Value::String(ref unit) => match unit.as_ref() { + "max-content" => quote!(taffy::style::AvailableSpace::MaxContent), + "min-content" => quote!(taffy::style::AvailableSpace::MaxContent), + "points" => { + let value = value(); + quote!(taffy::style::AvailableSpace::Definite(#value)) + } + _ => unreachable!(), + }, + _ => unreachable!(), + } +} + fn generate_grid_auto_flow(auto_flow: &serde_json::Map) -> TokenStream { let direction = auto_flow.get("direction").unwrap().as_str().unwrap(); let algorithm = auto_flow.get("algorithm").unwrap().as_str().unwrap(); diff --git a/scripts/gentest/test_base_style.css b/scripts/gentest/test_base_style.css index 452a2d703..749dbe6ba 100644 --- a/scripts/gentest/test_base_style.css +++ b/scripts/gentest/test_base_style.css @@ -31,6 +31,15 @@ body > * { border-color: red; } +.viewport { + display: flex; + align-items: start; + justify-content: start; + /* Defaults: can be overriden in individual tests */ + width: max-content; + height: max-content; +} + #test-root { font-family: ahem; line-height: 1; diff --git a/scripts/gentest/test_helper.js b/scripts/gentest/test_helper.js index b05eb7e7d..27f66a816 100644 --- a/scripts/gentest/test_helper.js +++ b/scripts/gentest/test_helper.js @@ -81,6 +81,20 @@ class TrackSizingParser { } +function parseViewportConstraint(e) { + if (e.parentNode.classList.contains('viewport')) { + return { + width: parseDimension(e.parentNode.style.width || 'max-content'), + height: parseDimension(e.parentNode.style.height || 'max-content'), + } + } else { + return { + width: { unit: 'max-content' }, + height: { unit: 'max-content' }, + } + } +} + function parseRepetition(input) { if (input === "auto-fill") return { unit: 'auto-fill' }; if (input === "auto-fit") return { unit: 'auto-fit' }; @@ -283,6 +297,8 @@ function describeElement(e) { // Whether the test should enable rounding useRounding: e.getAttribute("data-test-rounding") !== "false", + viewport: parseViewportConstraint(e), + children: Array.from(e.children).map(c => describeElement(c)), } } diff --git a/src/compute/grid/mod.rs b/src/compute/grid/mod.rs index 09a91eb09..25288fa64 100644 --- a/src/compute/grid/mod.rs +++ b/src/compute/grid/mod.rs @@ -149,14 +149,10 @@ pub fn compute( let constrained_available_space = known_dimensions .or(size) - .maybe_clamp(min_size, max_size) .map(|size| size.map(AvailableSpace::Definite)) - .unwrap_or(available_space.map(|space| match space { - // Available grid space should not depend on Definite available space as a grid is allowed - // to expand beyond it's available space - AvailableSpace::Definite(_) => AvailableSpace::MaxContent, - _ => space, - })); + .unwrap_or(available_space) + .maybe_clamp(min_size, max_size) + .maybe_max(padding_border_size); let available_grid_space = Size { width: constrained_available_space @@ -167,7 +163,7 @@ pub fn compute( .map_definite_value(|space| space - padding.vertical_axis_sum() - border.vertical_axis_sum()), }; - let outer_node_size = known_dimensions.or(size.maybe_clamp(min_size, max_size).or(min_size)); + let outer_node_size = known_dimensions.or(size).maybe_clamp(min_size, max_size).maybe_max(padding_border_size); let mut inner_node_size = Size { width: outer_node_size.width.map(|space| space - padding.horizontal_axis_sum() - border.horizontal_axis_sum()), height: outer_node_size.height.map(|space| space - padding.vertical_axis_sum() - border.vertical_axis_sum()), @@ -232,6 +228,11 @@ pub fn compute( let initial_row_sum = rows.iter().map(|track| track.base_size).sum::(); inner_node_size.height = inner_node_size.height.or_else(|| initial_row_sum.into()); + #[cfg(feature = "debug")] + NODE_LOGGER.labelled_debug_log("initial_column_sum", initial_column_sum); + #[cfg(feature = "debug")] + NODE_LOGGER.labelled_debug_log("initial_row_sum", initial_row_sum); + // 6. Compute container size let resolved_style_size = known_dimensions.or(style.size.maybe_resolve(parent_size)); let container_border_box = Size { diff --git a/src/compute/grid/track_sizing.rs b/src/compute/grid/track_sizing.rs index 704402518..3f5a671c4 100644 --- a/src/compute/grid/track_sizing.rs +++ b/src/compute/grid/track_sizing.rs @@ -327,6 +327,19 @@ pub(super) fn track_sizing_algorithm( // Distributes free space (if any) to tracks with FINITE growth limits, up to their limits. maximise_tracks(axis_tracks, inner_node_size.get(axis), available_grid_space.get(axis)); + // For the purpose of the final two expansion steps ("Expand Flexible Tracks" and "Stretch auto Tracks"), we only want to expand + // into space generated by the grid container's size (as defined by either it's preferred size style or by it's parent node through + // something like stretch alignment), not just any available space. To do this we map definite available space to AvailableSpace::MaxContent + // in the case that inner_node_size is None + let axis_available_space_for_expansion = if let Some(available_space) = inner_node_size.get(axis) { + AvailableSpace::Definite(available_space) + } else { + match available_grid_space.get(axis) { + AvailableSpace::MinContent => AvailableSpace::MinContent, + AvailableSpace::MaxContent | AvailableSpace::Definite(_) => AvailableSpace::MaxContent, + } + }; + // 11.7. Expand Flexible Tracks // This step sizes flexible tracks using the largest value it can assign to an fr without exceeding the available space. expand_flexible_tracks( @@ -336,13 +349,13 @@ pub(super) fn track_sizing_algorithm( items, axis_min_size, axis_max_size, - available_grid_space, + axis_available_space_for_expansion, inner_node_size, ); // 11.8. Stretch auto Tracks // This step expands tracks that have an auto max track sizing function by dividing any remaining positive, definite free space equally amongst them. - stretch_auto_tracks(axis, axis_tracks, axis_min_size, available_grid_space); + stretch_auto_tracks(axis_tracks, axis_min_size, axis_available_space_for_expansion); } /// Whether it is a minimum or maximum size's space being distributed @@ -1083,11 +1096,11 @@ fn expand_flexible_tracks( items: &mut [GridItem], axis_min_size: Option, axis_max_size: Option, - available_grid_space: Size, + axis_available_space_for_expansion: AvailableSpace, inner_node_size: Size>, ) { // First, find the grid’s used flex fraction: - let flex_fraction = match available_grid_space.get(axis) { + let flex_fraction = match axis_available_space_for_expansion { // If the free space is zero: // The used flex fraction is zero. // Otherwise, if the free space is a definite length: @@ -1237,10 +1250,9 @@ fn find_size_of_fr(tracks: &[GridTrack], space_to_fill: f32) -> f32 { /// This step expands tracks that have an auto max track sizing function by dividing any remaining positive, definite free space equally amongst them. #[inline(always)] fn stretch_auto_tracks( - axis: AbstractAxis, axis_tracks: &mut [GridTrack], axis_min_size: Option, - available_grid_space: Size, + axis_available_space_for_expansion: AvailableSpace, ) { let num_auto_tracks = axis_tracks.iter().filter(|track| track.max_track_sizing_function == MaxTrackSizingFunction::Auto).count(); @@ -1249,8 +1261,8 @@ fn stretch_auto_tracks( // If the free space is indefinite, but the grid container has a definite min-width/height // use that size to calculate the free space for this step instead. - let free_space = if available_grid_space.get(axis).is_definite() { - available_grid_space.get(axis).compute_free_space(used_space) + let free_space = if axis_available_space_for_expansion.is_definite() { + axis_available_space_for_expansion.compute_free_space(used_space) } else { match axis_min_size { Some(size) => size - used_space, diff --git a/test_fixtures/grid_available_space_greater_than_max_content.html b/test_fixtures/grid_available_space_greater_than_max_content.html new file mode 100644 index 000000000..4674700d8 --- /dev/null +++ b/test_fixtures/grid_available_space_greater_than_max_content.html @@ -0,0 +1,20 @@ + + + + + + + Test description + + + + +
+
+
HH​HH​HH​HH
+
HH​HH​HH​HH
+
+
+ + + \ No newline at end of file diff --git a/test_fixtures/grid_available_space_smaller_than_max_content.html b/test_fixtures/grid_available_space_smaller_than_max_content.html new file mode 100644 index 000000000..f952ac4a2 --- /dev/null +++ b/test_fixtures/grid_available_space_smaller_than_max_content.html @@ -0,0 +1,20 @@ + + + + + + + Test description + + + + +
+
+
HH​HH​HH​HH
+
HH​HH​HH​HH
+
+
+ + + \ No newline at end of file diff --git a/test_fixtures/grid_available_space_smaller_than_min_content.html b/test_fixtures/grid_available_space_smaller_than_min_content.html new file mode 100644 index 000000000..a848831d7 --- /dev/null +++ b/test_fixtures/grid_available_space_smaller_than_min_content.html @@ -0,0 +1,20 @@ + + + + + + + Test description + + + + +
+
+
HHHH​HHHH
+
HHHH​HHHH
+
+
+ + + \ No newline at end of file diff --git a/test_fixtures/grid_max_width_greater_than_max_content.html b/test_fixtures/grid_max_width_greater_than_max_content.html new file mode 100644 index 000000000..88918ce10 --- /dev/null +++ b/test_fixtures/grid_max_width_greater_than_max_content.html @@ -0,0 +1,20 @@ + + + + + + + Test description + + + + +
+
+
HH​HH​HH​HH
+
HH​HH​HH​HH
+
+
+ + + \ No newline at end of file diff --git a/test_fixtures/grid_max_width_less_than_max_content_with_min_content.html b/test_fixtures/grid_max_width_less_than_max_content_with_min_content.html new file mode 100644 index 000000000..5fe02bf63 --- /dev/null +++ b/test_fixtures/grid_max_width_less_than_max_content_with_min_content.html @@ -0,0 +1,20 @@ + + + + + + + Test description + + + + +
+
+
HH​HH​HH​HH
+
HH​HH​HH​HH
+
+
+ + + \ No newline at end of file diff --git a/test_fixtures/grid_max_width_smaller_than_max_content.html b/test_fixtures/grid_max_width_smaller_than_max_content.html new file mode 100644 index 000000000..6ca0be76a --- /dev/null +++ b/test_fixtures/grid_max_width_smaller_than_max_content.html @@ -0,0 +1,20 @@ + + + + + + + Test description + + + + +
+
+
HH​HH​HH​HH
+
HH​HH​HH​HH
+
+
+ + + \ No newline at end of file diff --git a/test_fixtures/grid_max_width_smaller_than_min_content.html b/test_fixtures/grid_max_width_smaller_than_min_content.html new file mode 100644 index 000000000..26d21dbce --- /dev/null +++ b/test_fixtures/grid_max_width_smaller_than_min_content.html @@ -0,0 +1,20 @@ + + + + + + + Test description + + + + +
+
+
HHHH​HHHH
+
HHHH​HHHH
+
+
+ + + \ No newline at end of file diff --git a/tests/generated/grid_available_space_greater_than_max_content.rs b/tests/generated/grid_available_space_greater_than_max_content.rs new file mode 100644 index 000000000..2e5085a57 --- /dev/null +++ b/tests/generated/grid_available_space_greater_than_max_content.rs @@ -0,0 +1,74 @@ +#[test] +fn grid_available_space_greater_than_max_content() { + use slotmap::Key; + #[allow(unused_imports)] + use taffy::{layout::Layout, prelude::*}; + let mut taffy = taffy::Taffy::new(); + let node0 = taffy + .new_leaf_with_measure( + taffy::style::Style { ..Default::default() }, + taffy::node::MeasureFunc::Raw(|known_dimensions, available_space| { + const TEXT: &str = "HH\u{200b}HH\u{200b}HH\u{200b}HH"; + super::measure_standard_text( + known_dimensions, + available_space, + TEXT, + super::WritingMode::Horizontal, + None, + ) + }), + ) + .unwrap(); + let node1 = taffy + .new_leaf_with_measure( + taffy::style::Style { ..Default::default() }, + taffy::node::MeasureFunc::Raw(|known_dimensions, available_space| { + const TEXT: &str = "HH\u{200b}HH\u{200b}HH\u{200b}HH"; + super::measure_standard_text( + known_dimensions, + available_space, + TEXT, + super::WritingMode::Horizontal, + None, + ) + }), + ) + .unwrap(); + let node = taffy + .new_with_children( + taffy::style::Style { + display: taffy::style::Display::Grid, + grid_template_columns: vec![auto(), auto()], + ..Default::default() + }, + &[node0, node1], + ) + .unwrap(); + taffy + .compute_layout( + node, + taffy::geometry::Size { + width: taffy::style::AvailableSpace::Definite(400f32), + height: taffy::style::AvailableSpace::MaxContent, + }, + ) + .unwrap(); + println!("\nComputed tree:"); + taffy::debug::print_tree(&taffy, node); + println!(); + let Layout { size, location, .. } = taffy.layout(node).unwrap(); + assert_eq!(size.width, 160f32, "width of node {:?}. Expected {}. Actual {}", node.data(), 160f32, size.width); + assert_eq!(size.height, 10f32, "height of node {:?}. Expected {}. Actual {}", node.data(), 10f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node.data(), 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node0).unwrap(); + assert_eq!(size.width, 80f32, "width of node {:?}. Expected {}. Actual {}", node0.data(), 80f32, size.width); + assert_eq!(size.height, 10f32, "height of node {:?}. Expected {}. Actual {}", node0.data(), 10f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node1).unwrap(); + assert_eq!(size.width, 80f32, "width of node {:?}. Expected {}. Actual {}", node1.data(), 80f32, size.width); + assert_eq!(size.height, 10f32, "height of node {:?}. Expected {}. Actual {}", node1.data(), 10f32, size.height); + assert_eq!(location.x, 80f32, "x of node {:?}. Expected {}. Actual {}", node1.data(), 80f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node1.data(), 0f32, location.y); +} diff --git a/tests/generated/grid_available_space_smaller_than_max_content.rs b/tests/generated/grid_available_space_smaller_than_max_content.rs new file mode 100644 index 000000000..10c3b0979 --- /dev/null +++ b/tests/generated/grid_available_space_smaller_than_max_content.rs @@ -0,0 +1,74 @@ +#[test] +fn grid_available_space_smaller_than_max_content() { + use slotmap::Key; + #[allow(unused_imports)] + use taffy::{layout::Layout, prelude::*}; + let mut taffy = taffy::Taffy::new(); + let node0 = taffy + .new_leaf_with_measure( + taffy::style::Style { ..Default::default() }, + taffy::node::MeasureFunc::Raw(|known_dimensions, available_space| { + const TEXT: &str = "HH\u{200b}HH\u{200b}HH\u{200b}HH"; + super::measure_standard_text( + known_dimensions, + available_space, + TEXT, + super::WritingMode::Horizontal, + None, + ) + }), + ) + .unwrap(); + let node1 = taffy + .new_leaf_with_measure( + taffy::style::Style { ..Default::default() }, + taffy::node::MeasureFunc::Raw(|known_dimensions, available_space| { + const TEXT: &str = "HH\u{200b}HH\u{200b}HH\u{200b}HH"; + super::measure_standard_text( + known_dimensions, + available_space, + TEXT, + super::WritingMode::Horizontal, + None, + ) + }), + ) + .unwrap(); + let node = taffy + .new_with_children( + taffy::style::Style { + display: taffy::style::Display::Grid, + grid_template_columns: vec![auto(), auto()], + ..Default::default() + }, + &[node0, node1], + ) + .unwrap(); + taffy + .compute_layout( + node, + taffy::geometry::Size { + width: taffy::style::AvailableSpace::Definite(80f32), + height: taffy::style::AvailableSpace::MaxContent, + }, + ) + .unwrap(); + println!("\nComputed tree:"); + taffy::debug::print_tree(&taffy, node); + println!(); + let Layout { size, location, .. } = taffy.layout(node).unwrap(); + assert_eq!(size.width, 80f32, "width of node {:?}. Expected {}. Actual {}", node.data(), 80f32, size.width); + assert_eq!(size.height, 20f32, "height of node {:?}. Expected {}. Actual {}", node.data(), 20f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node.data(), 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node0).unwrap(); + assert_eq!(size.width, 40f32, "width of node {:?}. Expected {}. Actual {}", node0.data(), 40f32, size.width); + assert_eq!(size.height, 20f32, "height of node {:?}. Expected {}. Actual {}", node0.data(), 20f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node1).unwrap(); + assert_eq!(size.width, 40f32, "width of node {:?}. Expected {}. Actual {}", node1.data(), 40f32, size.width); + assert_eq!(size.height, 20f32, "height of node {:?}. Expected {}. Actual {}", node1.data(), 20f32, size.height); + assert_eq!(location.x, 40f32, "x of node {:?}. Expected {}. Actual {}", node1.data(), 40f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node1.data(), 0f32, location.y); +} diff --git a/tests/generated/grid_available_space_smaller_than_min_content.rs b/tests/generated/grid_available_space_smaller_than_min_content.rs new file mode 100644 index 000000000..528149a81 --- /dev/null +++ b/tests/generated/grid_available_space_smaller_than_min_content.rs @@ -0,0 +1,74 @@ +#[test] +fn grid_available_space_smaller_than_min_content() { + use slotmap::Key; + #[allow(unused_imports)] + use taffy::{layout::Layout, prelude::*}; + let mut taffy = taffy::Taffy::new(); + let node0 = taffy + .new_leaf_with_measure( + taffy::style::Style { ..Default::default() }, + taffy::node::MeasureFunc::Raw(|known_dimensions, available_space| { + const TEXT: &str = "HHHH\u{200b}HHHH"; + super::measure_standard_text( + known_dimensions, + available_space, + TEXT, + super::WritingMode::Horizontal, + None, + ) + }), + ) + .unwrap(); + let node1 = taffy + .new_leaf_with_measure( + taffy::style::Style { ..Default::default() }, + taffy::node::MeasureFunc::Raw(|known_dimensions, available_space| { + const TEXT: &str = "HHHH\u{200b}HHHH"; + super::measure_standard_text( + known_dimensions, + available_space, + TEXT, + super::WritingMode::Horizontal, + None, + ) + }), + ) + .unwrap(); + let node = taffy + .new_with_children( + taffy::style::Style { + display: taffy::style::Display::Grid, + grid_template_columns: vec![auto(), auto()], + ..Default::default() + }, + &[node0, node1], + ) + .unwrap(); + taffy + .compute_layout( + node, + taffy::geometry::Size { + width: taffy::style::AvailableSpace::Definite(60f32), + height: taffy::style::AvailableSpace::MaxContent, + }, + ) + .unwrap(); + println!("\nComputed tree:"); + taffy::debug::print_tree(&taffy, node); + println!(); + let Layout { size, location, .. } = taffy.layout(node).unwrap(); + assert_eq!(size.width, 80f32, "width of node {:?}. Expected {}. Actual {}", node.data(), 80f32, size.width); + assert_eq!(size.height, 20f32, "height of node {:?}. Expected {}. Actual {}", node.data(), 20f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node.data(), 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node0).unwrap(); + assert_eq!(size.width, 40f32, "width of node {:?}. Expected {}. Actual {}", node0.data(), 40f32, size.width); + assert_eq!(size.height, 20f32, "height of node {:?}. Expected {}. Actual {}", node0.data(), 20f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node1).unwrap(); + assert_eq!(size.width, 40f32, "width of node {:?}. Expected {}. Actual {}", node1.data(), 40f32, size.width); + assert_eq!(size.height, 20f32, "height of node {:?}. Expected {}. Actual {}", node1.data(), 20f32, size.height); + assert_eq!(location.x, 40f32, "x of node {:?}. Expected {}. Actual {}", node1.data(), 40f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node1.data(), 0f32, location.y); +} diff --git a/tests/generated/grid_max_width_greater_than_max_content.rs b/tests/generated/grid_max_width_greater_than_max_content.rs new file mode 100644 index 000000000..66009978e --- /dev/null +++ b/tests/generated/grid_max_width_greater_than_max_content.rs @@ -0,0 +1,82 @@ +#[test] +fn grid_max_width_greater_than_max_content() { + use slotmap::Key; + #[allow(unused_imports)] + use taffy::{layout::Layout, prelude::*}; + let mut taffy = taffy::Taffy::new(); + let node00 = taffy + .new_leaf_with_measure( + taffy::style::Style { ..Default::default() }, + taffy::node::MeasureFunc::Raw(|known_dimensions, available_space| { + const TEXT: &str = "HH\u{200b}HH\u{200b}HH\u{200b}HH"; + super::measure_standard_text( + known_dimensions, + available_space, + TEXT, + super::WritingMode::Horizontal, + None, + ) + }), + ) + .unwrap(); + let node01 = taffy + .new_leaf_with_measure( + taffy::style::Style { ..Default::default() }, + taffy::node::MeasureFunc::Raw(|known_dimensions, available_space| { + const TEXT: &str = "HH\u{200b}HH\u{200b}HH\u{200b}HH"; + super::measure_standard_text( + known_dimensions, + available_space, + TEXT, + super::WritingMode::Horizontal, + None, + ) + }), + ) + .unwrap(); + let node0 = taffy + .new_with_children( + taffy::style::Style { + display: taffy::style::Display::Grid, + grid_template_columns: vec![auto(), auto()], + max_size: taffy::geometry::Size { width: taffy::style::Dimension::Points(400f32), height: auto() }, + ..Default::default() + }, + &[node00, node01], + ) + .unwrap(); + let node = taffy + .new_with_children( + taffy::style::Style { + display: taffy::style::Display::Grid, + grid_template_columns: vec![max_content()], + ..Default::default() + }, + &[node0], + ) + .unwrap(); + taffy.compute_layout(node, taffy::geometry::Size::MAX_CONTENT).unwrap(); + println!("\nComputed tree:"); + taffy::debug::print_tree(&taffy, node); + println!(); + let Layout { size, location, .. } = taffy.layout(node).unwrap(); + assert_eq!(size.width, 160f32, "width of node {:?}. Expected {}. Actual {}", node.data(), 160f32, size.width); + assert_eq!(size.height, 10f32, "height of node {:?}. Expected {}. Actual {}", node.data(), 10f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node.data(), 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node0).unwrap(); + assert_eq!(size.width, 160f32, "width of node {:?}. Expected {}. Actual {}", node0.data(), 160f32, size.width); + assert_eq!(size.height, 10f32, "height of node {:?}. Expected {}. Actual {}", node0.data(), 10f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node00).unwrap(); + assert_eq!(size.width, 80f32, "width of node {:?}. Expected {}. Actual {}", node00.data(), 80f32, size.width); + assert_eq!(size.height, 10f32, "height of node {:?}. Expected {}. Actual {}", node00.data(), 10f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node00.data(), 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node00.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node01).unwrap(); + assert_eq!(size.width, 80f32, "width of node {:?}. Expected {}. Actual {}", node01.data(), 80f32, size.width); + assert_eq!(size.height, 10f32, "height of node {:?}. Expected {}. Actual {}", node01.data(), 10f32, size.height); + assert_eq!(location.x, 80f32, "x of node {:?}. Expected {}. Actual {}", node01.data(), 80f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node01.data(), 0f32, location.y); +} diff --git a/tests/generated/grid_max_width_less_than_max_content_with_min_content.rs b/tests/generated/grid_max_width_less_than_max_content_with_min_content.rs new file mode 100644 index 000000000..8a5b810f8 --- /dev/null +++ b/tests/generated/grid_max_width_less_than_max_content_with_min_content.rs @@ -0,0 +1,82 @@ +#[test] +fn grid_max_width_less_than_max_content_with_min_content() { + use slotmap::Key; + #[allow(unused_imports)] + use taffy::{layout::Layout, prelude::*}; + let mut taffy = taffy::Taffy::new(); + let node00 = taffy + .new_leaf_with_measure( + taffy::style::Style { ..Default::default() }, + taffy::node::MeasureFunc::Raw(|known_dimensions, available_space| { + const TEXT: &str = "HH\u{200b}HH\u{200b}HH\u{200b}HH"; + super::measure_standard_text( + known_dimensions, + available_space, + TEXT, + super::WritingMode::Horizontal, + None, + ) + }), + ) + .unwrap(); + let node01 = taffy + .new_leaf_with_measure( + taffy::style::Style { ..Default::default() }, + taffy::node::MeasureFunc::Raw(|known_dimensions, available_space| { + const TEXT: &str = "HH\u{200b}HH\u{200b}HH\u{200b}HH"; + super::measure_standard_text( + known_dimensions, + available_space, + TEXT, + super::WritingMode::Horizontal, + None, + ) + }), + ) + .unwrap(); + let node0 = taffy + .new_with_children( + taffy::style::Style { + display: taffy::style::Display::Grid, + grid_template_columns: vec![auto(), auto()], + max_size: taffy::geometry::Size { width: taffy::style::Dimension::Points(80f32), height: auto() }, + ..Default::default() + }, + &[node00, node01], + ) + .unwrap(); + let node = taffy + .new_with_children( + taffy::style::Style { + display: taffy::style::Display::Grid, + grid_template_columns: vec![min_content()], + ..Default::default() + }, + &[node0], + ) + .unwrap(); + taffy.compute_layout(node, taffy::geometry::Size::MAX_CONTENT).unwrap(); + println!("\nComputed tree:"); + taffy::debug::print_tree(&taffy, node); + println!(); + let Layout { size, location, .. } = taffy.layout(node).unwrap(); + assert_eq!(size.width, 40f32, "width of node {:?}. Expected {}. Actual {}", node.data(), 40f32, size.width); + assert_eq!(size.height, 40f32, "height of node {:?}. Expected {}. Actual {}", node.data(), 40f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node.data(), 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node0).unwrap(); + assert_eq!(size.width, 40f32, "width of node {:?}. Expected {}. Actual {}", node0.data(), 40f32, size.width); + assert_eq!(size.height, 40f32, "height of node {:?}. Expected {}. Actual {}", node0.data(), 40f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node00).unwrap(); + assert_eq!(size.width, 20f32, "width of node {:?}. Expected {}. Actual {}", node00.data(), 20f32, size.width); + assert_eq!(size.height, 40f32, "height of node {:?}. Expected {}. Actual {}", node00.data(), 40f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node00.data(), 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node00.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node01).unwrap(); + assert_eq!(size.width, 20f32, "width of node {:?}. Expected {}. Actual {}", node01.data(), 20f32, size.width); + assert_eq!(size.height, 40f32, "height of node {:?}. Expected {}. Actual {}", node01.data(), 40f32, size.height); + assert_eq!(location.x, 20f32, "x of node {:?}. Expected {}. Actual {}", node01.data(), 20f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node01.data(), 0f32, location.y); +} diff --git a/tests/generated/grid_max_width_smaller_than_max_content.rs b/tests/generated/grid_max_width_smaller_than_max_content.rs new file mode 100644 index 000000000..5857f2658 --- /dev/null +++ b/tests/generated/grid_max_width_smaller_than_max_content.rs @@ -0,0 +1,82 @@ +#[test] +fn grid_max_width_smaller_than_max_content() { + use slotmap::Key; + #[allow(unused_imports)] + use taffy::{layout::Layout, prelude::*}; + let mut taffy = taffy::Taffy::new(); + let node00 = taffy + .new_leaf_with_measure( + taffy::style::Style { ..Default::default() }, + taffy::node::MeasureFunc::Raw(|known_dimensions, available_space| { + const TEXT: &str = "HH\u{200b}HH\u{200b}HH\u{200b}HH"; + super::measure_standard_text( + known_dimensions, + available_space, + TEXT, + super::WritingMode::Horizontal, + None, + ) + }), + ) + .unwrap(); + let node01 = taffy + .new_leaf_with_measure( + taffy::style::Style { ..Default::default() }, + taffy::node::MeasureFunc::Raw(|known_dimensions, available_space| { + const TEXT: &str = "HH\u{200b}HH\u{200b}HH\u{200b}HH"; + super::measure_standard_text( + known_dimensions, + available_space, + TEXT, + super::WritingMode::Horizontal, + None, + ) + }), + ) + .unwrap(); + let node0 = taffy + .new_with_children( + taffy::style::Style { + display: taffy::style::Display::Grid, + grid_template_columns: vec![auto(), auto()], + max_size: taffy::geometry::Size { width: taffy::style::Dimension::Points(80f32), height: auto() }, + ..Default::default() + }, + &[node00, node01], + ) + .unwrap(); + let node = taffy + .new_with_children( + taffy::style::Style { + display: taffy::style::Display::Grid, + grid_template_columns: vec![max_content()], + ..Default::default() + }, + &[node0], + ) + .unwrap(); + taffy.compute_layout(node, taffy::geometry::Size::MAX_CONTENT).unwrap(); + println!("\nComputed tree:"); + taffy::debug::print_tree(&taffy, node); + println!(); + let Layout { size, location, .. } = taffy.layout(node).unwrap(); + assert_eq!(size.width, 80f32, "width of node {:?}. Expected {}. Actual {}", node.data(), 80f32, size.width); + assert_eq!(size.height, 20f32, "height of node {:?}. Expected {}. Actual {}", node.data(), 20f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node.data(), 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node0).unwrap(); + assert_eq!(size.width, 80f32, "width of node {:?}. Expected {}. Actual {}", node0.data(), 80f32, size.width); + assert_eq!(size.height, 20f32, "height of node {:?}. Expected {}. Actual {}", node0.data(), 20f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node00).unwrap(); + assert_eq!(size.width, 40f32, "width of node {:?}. Expected {}. Actual {}", node00.data(), 40f32, size.width); + assert_eq!(size.height, 20f32, "height of node {:?}. Expected {}. Actual {}", node00.data(), 20f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node00.data(), 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node00.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node01).unwrap(); + assert_eq!(size.width, 40f32, "width of node {:?}. Expected {}. Actual {}", node01.data(), 40f32, size.width); + assert_eq!(size.height, 20f32, "height of node {:?}. Expected {}. Actual {}", node01.data(), 20f32, size.height); + assert_eq!(location.x, 40f32, "x of node {:?}. Expected {}. Actual {}", node01.data(), 40f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node01.data(), 0f32, location.y); +} diff --git a/tests/generated/grid_max_width_smaller_than_min_content.rs b/tests/generated/grid_max_width_smaller_than_min_content.rs new file mode 100644 index 000000000..3563034a1 --- /dev/null +++ b/tests/generated/grid_max_width_smaller_than_min_content.rs @@ -0,0 +1,82 @@ +#[test] +fn grid_max_width_smaller_than_min_content() { + use slotmap::Key; + #[allow(unused_imports)] + use taffy::{layout::Layout, prelude::*}; + let mut taffy = taffy::Taffy::new(); + let node00 = taffy + .new_leaf_with_measure( + taffy::style::Style { ..Default::default() }, + taffy::node::MeasureFunc::Raw(|known_dimensions, available_space| { + const TEXT: &str = "HHHH\u{200b}HHHH"; + super::measure_standard_text( + known_dimensions, + available_space, + TEXT, + super::WritingMode::Horizontal, + None, + ) + }), + ) + .unwrap(); + let node01 = taffy + .new_leaf_with_measure( + taffy::style::Style { ..Default::default() }, + taffy::node::MeasureFunc::Raw(|known_dimensions, available_space| { + const TEXT: &str = "HHHH\u{200b}HHHH"; + super::measure_standard_text( + known_dimensions, + available_space, + TEXT, + super::WritingMode::Horizontal, + None, + ) + }), + ) + .unwrap(); + let node0 = taffy + .new_with_children( + taffy::style::Style { + display: taffy::style::Display::Grid, + grid_template_columns: vec![auto(), auto()], + max_size: taffy::geometry::Size { width: taffy::style::Dimension::Points(60f32), height: auto() }, + ..Default::default() + }, + &[node00, node01], + ) + .unwrap(); + let node = taffy + .new_with_children( + taffy::style::Style { + display: taffy::style::Display::Grid, + grid_template_columns: vec![max_content()], + ..Default::default() + }, + &[node0], + ) + .unwrap(); + taffy.compute_layout(node, taffy::geometry::Size::MAX_CONTENT).unwrap(); + println!("\nComputed tree:"); + taffy::debug::print_tree(&taffy, node); + println!(); + let Layout { size, location, .. } = taffy.layout(node).unwrap(); + assert_eq!(size.width, 60f32, "width of node {:?}. Expected {}. Actual {}", node.data(), 60f32, size.width); + assert_eq!(size.height, 20f32, "height of node {:?}. Expected {}. Actual {}", node.data(), 20f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node.data(), 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node0).unwrap(); + assert_eq!(size.width, 60f32, "width of node {:?}. Expected {}. Actual {}", node0.data(), 60f32, size.width); + assert_eq!(size.height, 20f32, "height of node {:?}. Expected {}. Actual {}", node0.data(), 20f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node00).unwrap(); + assert_eq!(size.width, 40f32, "width of node {:?}. Expected {}. Actual {}", node00.data(), 40f32, size.width); + assert_eq!(size.height, 20f32, "height of node {:?}. Expected {}. Actual {}", node00.data(), 20f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node00.data(), 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node00.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node01).unwrap(); + assert_eq!(size.width, 40f32, "width of node {:?}. Expected {}. Actual {}", node01.data(), 40f32, size.width); + assert_eq!(size.height, 20f32, "height of node {:?}. Expected {}. Actual {}", node01.data(), 20f32, size.height); + assert_eq!(location.x, 40f32, "x of node {:?}. Expected {}. Actual {}", node01.data(), 40f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node01.data(), 0f32, location.y); +} diff --git a/tests/generated/mod.rs b/tests/generated/mod.rs index a9806b4df..7117f4f70 100644 --- a/tests/generated/mod.rs +++ b/tests/generated/mod.rs @@ -471,6 +471,12 @@ mod grid_auto_single_item_fixed_width_with_definite_width; #[cfg(feature = "grid")] mod grid_auto_takes_precedence_over_fr; #[cfg(feature = "grid")] +mod grid_available_space_greater_than_max_content; +#[cfg(feature = "grid")] +mod grid_available_space_smaller_than_max_content; +#[cfg(feature = "grid")] +mod grid_available_space_smaller_than_min_content; +#[cfg(feature = "grid")] mod grid_basic; #[cfg(feature = "grid")] mod grid_basic_implicit_tracks; @@ -595,6 +601,14 @@ mod grid_max_content_single_item_span_2_gap_percent_definite; #[cfg(feature = "grid")] mod grid_max_content_single_item_span_2_gap_percent_indefinite; #[cfg(feature = "grid")] +mod grid_max_width_greater_than_max_content; +#[cfg(feature = "grid")] +mod grid_max_width_less_than_max_content_with_min_content; +#[cfg(feature = "grid")] +mod grid_max_width_smaller_than_max_content; +#[cfg(feature = "grid")] +mod grid_max_width_smaller_than_min_content; +#[cfg(feature = "grid")] mod grid_min_content_flex_column; #[cfg(feature = "grid")] mod grid_min_content_flex_row;