diff --git a/Cargo.toml b/Cargo.toml index 0dd5e477a..0e1ba6478 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "taffy" -version = "0.3.12" +version = "0.3.13" authors = [ "Alice Cecile ", "Johnathan Kelley ", @@ -19,13 +19,13 @@ num-traits = { version = "0.2", default-features = false } rand = { version = "0.8.5", optional = true } serde = { version = "1.0", optional = true, features = ["serde_derive"] } slotmap = "1.0.6" -grid = { version = "0.9.0", optional = true } +grid = { version = "0.10.0", default-features = false, optional = true } [features] default = ["std", "grid"] grid = ["alloc", "dep:grid"] alloc = [] -std = ["num-traits/std"] +std = ["num-traits/std", "grid?/std"] serde = ["dep:serde"] random = ["dep:rand"] yoga_benchmark = [] diff --git a/RELEASES.md b/RELEASES.md index bcb02787d..77c4b193b 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,5 +1,27 @@ # Release Notes +## 0.3.13 + +### Fixes + +- Fix rounding accumulation bug (#521) (Fixes #501 and bevyengine/bevy#8911) +- Flexbox: pass correct cross-axis available space when computing an item's intrinsic main size (#522)(Fixes bevyengine/bevy#9350) +- Flexbox: Subtract child margin not parent margin when computing stretch-alignment known size +- Grid: Make CSS Grid algorithm correctly apply max width/height and available space when it is the root node (#491) +- Grid: Fix CSS Grid "auto track" / placement bugs #481 + - Fix divide by zero when using grid_auto_rows/grid_auto_columns with zero negative implicit tracks + - Fix over counting of tracks (leading to incorrect container heights) when auto-placing in grids that contain negative implicit tracks. + - Fix axis conflation in auto-placement code when grid_auto_flow is column + - Fix assignment of auto track sizes when initializing negative implicit tracks +- Leaf: Apply margins to leaf nodes when computing available space for measure functions +- Leaf: Reserve space for padding/borders in nodes with measure functions (#497) + + **NOTE: This has the potential to break layouts relying on the old behaviour.** However, such layouts would be relying on a style having no effect, so it is judged that such layouts are unlikely to exist in the wild. If this turns out not to be true then this fix will be reverted on the 0.3.x branch. + +### Dependencies + +- Upgrade `grid` to `0.10`. This eliminates the transitive dependency on `no-std-compat`. + ## 0.3.12 ### Fixes 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 e06d7c6e9..749dbe6ba 100644 --- a/scripts/gentest/test_base_style.css +++ b/scripts/gentest/test_base_style.css @@ -1,14 +1,13 @@ -/* The font "silkscreen" subsetted to only contain the"H" (capital h) glyph - The "H" glyph in this font happens to be exactly 10 pixels wide by 14 high - Furthermore, we can use a line-height of 0.71428571428 to scale the height to 10 pixels +/* The Ahem font. See: https://github.com/Kozea/Ahem + The "X" glyph in this font happens to be exactly 10 pixels wide by 10 high - By combining H's with zero-width space characters (​) we can reliably set an elements + By combining X's with zero-width space characters (​) we can reliably set an elements min-content and max-content sizes. */ @font-face { - font-family: 'silkscreen-h'; - src: url(data:application/font-woff2;charset=utf-8;base64,d09GMgABAAAAAAOAABAAAAAAB7wAAAMkAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP0ZGVE0cGh4GVgCCWggoCWQRCAqBZIFjCw4AATYCJAMYBCAFhAgHRQxTG5IGAC4GnDLLNaI4Ih4+4mqyRg9HHA8fX6MqHp72+9+vvWfOXEdEkkaIJglvYpJJhEbI4o0QLSQ8tJ/ez/fn9/eeN4h3Sl/w0L7uB5CWVVeaGMPcBJ633fsTGVBiIcZj6bwqRjr/xwv9r0t5/9KdBfjyxtcttVqp2BpQ5igTsIwHKEClWemU7qWRJxTUvMlUEGFYTu6MiB4KAT+fp/dg+5v9Fv69218CBMZYBKQQOqEiEXk09nVq/s6XeKGtjZ/YGaTvuOsU8iDXv/GnFcEbKGugCQSgoGAFApzQKrBCmx3AUF9KE2vo188/xL88/d//tz8HAgCiEYBIJJqaQGH+cAUj836+xIwmiKdguu3lmr40xRRttVIHO9vba0mbR4MOy0bNii8ytRLxqinknInaInQ5/fyRo50El0++w51kOyRDQbdLiMP9JaQ7ndb6ImiT0iv8w9zYdBmYv0UBQkyZDRoYo2IJsAKAiqdaACFqHddRN6mnHNuQ1xoZxagOY6ibXWetv11yO7O8lNrMNr/YtP2GLBIM+xS27eNenffMPGrd71AqqStRoU7XEGjzf4SiWL7W1joeDmyy35U6kk1Hw1JyFFqXJYz6vaKF/UPjTYGsxZIgaOLfZSQfyPCjxejHY90mIBDM3vrQeXa2+LVrsW3mZzN9onlv0ZcgKGv+G/lfRwME063FM+sBMbP03UqzskBpxvIcqW+nHpxmAGDshSLqAEdAjLCAHWnsEopVMqjW2UVnh0M0C9zhiwk/+IqeEXzTFbu13w3E4Yl+FMuyKQ9LCUL8csB9bl9eLPMpwHKJgjRmMTEvLUUufpyCgjiMKi0nzCUqIS4vIAfDUqz1cJKPTefqCrvV87VoxnEZqOgQF58CRKEoDGFiQBg4hNjYhJj4l0Ng1adXbsD8Qm8ULUC3oeIOPrmyBGGh1yt5hDi40xlH52nomGIchmVkdMjyhaI87eSvdMkqisrJwRJagGndGDN+IzKKYlPa2tVb1UyE0Vm8E6WDQ+0fisnRquQDfePRqLVMer19ufM5pj3RRNRTJ2E/0i2NeZW75H150GioP1GlAAA=) format('woff2'); + font-family: 'ahem'; + src: url(data:application/font-woff2;charset=utf-8;base64,d09GMgABAAAAAAXYAA0AAAAAKjgAAAWAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP0ZGVE0cGh4GYACEUhEQCrUAlzALg3AAATYCJAOHVgQgBYZQB4RFG5AYFeOYFbBxgM3u/5P9f53AyfVjdayDrHio6Mo1sl6KByXlCXnIVzSV5fT5TQ6ZXYT2USiITV79J77MWg5EBsol7qDhyo33x8P3c+Gf+142my1jCuTYlRUqFhJYETii8Z2qKkDFIPmzBIAMunuOxJGJPs+89lYS9Ec0RfnM0Mq1cEQnuxeTQGhm4JhuxBMZ4EBHEV1vjc4Fu7x5fMIF6JSO/8U5lc0B0A3VCXQhbYfk///VdNbjMwXYa1vASnF+7bd0/afj0z/RstqGAQUSRwFlCwcaZ1maalGAqYWaZhAIp0HbGFGhQp8R+fesEPDRAacNrm2GQQH4Yj1KJ9ANA5CE8LtQICHlL+OkliLdSodxIjppPDY1MknRAfZzqZd6AcCqaTlzdf8x5brZybVnteg9V2ZJ49QC2eMMgyGWIKTLKGIyFpirpdLdeHud1wBDLIvtcTo9TZ/me3U/MBAgQ0eIFMUoWdJlcnQ5uufoPjIjC7IjN6Mfo7/jfuP+/TE16K420VkXY0osJTGepPeBfUGBAxKT+N7qR/I7o/n99ma5sMfNpdn8778f/+vxxa27wV3/rnfXvivdpe6Ct5PbIVzANRzDEWzDBsxAAoyguwJ99HOvD8EOipKwViRk7A2KVtmuOl107da9R8/bQOaB8A+ceQBEmFDGhVTaWOdDnOZl3fbjvO7n/X4ty4uyqpu264dxmhfL1Xqz3e0Px9P5cr3dH09CoxWTnllYVtPY0NTS3Nre2dHV3dvT1z84PDQyNjo1OT1zxkBjCEe8OM274BCfdYFX5surkXt1+7j7ZhMBcoN4bXi/9v5gFx8jFerSxZW9/eOTg8Pxt/kyIJ/cIA5DO/tXSabE7IzcvPyc4hLEFaquXFjfdpW8vrEZ3tlaQ8gqSS/RyOAPiO/xC/kVxV1ADRIIhPayEkwZPFmF/VHetd2z3NS91a3du+zl1aO36LEDX4L21P96y+5f+qv+8ss34XX05bPtoNb3U1j23Ox8QHjXnz+Jjeh7z5m/VV3zl6+r2zXg/GWuUUk90q+Y/EuCia9U+V/Hi3vKH2k91NA5kbZXA39cAiZjp55uyKLoEGfIKFRxhpIecY6yOq5TYVC8ppYB8S2VqpTkXnoX6q1H6pPvU6bX9MyANJGem5xm0wtV2uR6qUxbj/iQDUm7DXkVyeQAoxApohs55BkYMxBARTYKQGkjUUiYlrTXGBSTjPdZNEwhsv5GiIu2WLutqCjOzBiNUCJVG2QEcri6wGFxMxBSkWAxQFvbyOdIvmgOfP2RUPAtqClwBVkbyNVkJIhXG06ZEQlFppEZcsCo8Jgh54qFAIdogE8JK7bbCCWduZxmZpdZQNvw9obxRlBsXOEYyGiQrvayDC6imNOoxRAvSK5Oi0j9dkxmgftX5lagZSZaGPpMFCeJmRL5zynRq5RLsUtvhRAgXnS+tgl8l86WDo2N/RgrP+RBKOZDEZsI70NJdph0Qz+iBDQi/djb/kBEiiz/cnqnqnR01U13PfTUS299vCeRyuQKpUqt0er0BiMAQjCCYjhBUjTDcrwgmswWq83ucLrcHq/PDw0DCwePgIiEjIKKho6BiYWNg4uHT0BIRExCSkZOQUlFrVuKmsaE1yMtK5OIgjTrjY4lcfn2w8JHbwX4F1X059e/pIota3ohaeVD26K3btOebTt2vTM4su9AH5w/DvuOnTD59C0DkZmVnY1DPRo3Fw8vP5+AoA8hEWFRYkQblyATK57Cqx+TDvRrGMk6dO/KtRt3Ltyq0tTS1bNqWMeaREtpStbNmhkzi/HLCwr40MIpLMfGzWOQwrZ5C2lFGe2oolOu9ndsWTO1zQnXTp7cFzWnznij+LuUYlVenjzayBbkk89aAgA=) format('woff2'); font-weight: normal; font-style: normal; } @@ -32,11 +31,20 @@ 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: silkscreen-h; - line-height: 0.71428571428; - font-size: 14px; - color: white; + font-family: ahem; + line-height: 1; + font-size: 10px; + color: green; } div { 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/flexbox.rs b/src/compute/flexbox.rs index c72416232..55eb0b927 100644 --- a/src/compute/flexbox.rs +++ b/src/compute/flexbox.rs @@ -265,6 +265,8 @@ fn compute_preliminary( // If container size is undefined, determine the container's main size // and then re-resolve gaps based on newly determined size + #[cfg(feature = "debug")] + NODE_LOGGER.log("determine_container_main_size"); let original_gap = constants.gap; if let Some(inner_main_size) = constants.node_inner_size.main(constants.dir) { let outer_main_size = inner_main_size + constants.padding_border.main_axis_sum(constants.dir); @@ -272,10 +274,15 @@ fn compute_preliminary( constants.container_size.set_main(constants.dir, outer_main_size); } else { // Sets constants.container_size and constants.outer_container_size - determine_container_main_size(tree, available_space.main(constants.dir), &mut flex_lines, &mut constants); + determine_container_main_size(tree, available_space, &mut flex_lines, &mut constants); constants.node_inner_size.set_main(constants.dir, Some(constants.inner_container_size.main(constants.dir))); constants.node_outer_size.set_main(constants.dir, Some(constants.container_size.main(constants.dir))); + #[cfg(feature = "debug")] + NODE_LOGGER.labelled_debug_log("constants.node_outer_size", constants.node_outer_size); + #[cfg(feature = "debug")] + NODE_LOGGER.labelled_debug_log("constants.node_inner_size", constants.node_inner_size); + // Re-resolve percentage gaps let style = tree.style(node); let inner_container_size = constants.inner_container_size.main(constants.dir); @@ -352,7 +359,7 @@ fn compute_preliminary( // 15. Determine the flex container’s used cross size. #[cfg(feature = "debug")] NODE_LOGGER.log("determine_container_cross_size"); - let total_line_cross_size = determine_container_cross_size(&mut flex_lines, known_dimensions, &mut constants); + let total_line_cross_size = determine_container_cross_size(&flex_lines, known_dimensions, &mut constants); // We have the container size. // If our caller does not care about performing layout we are done now. @@ -655,7 +662,7 @@ fn determine_flex_base_size( .cross(dir) .into_option() .maybe_clamp(child_min_cross, child_max_cross) - .maybe_sub(constants.margin.cross_axis_sum(dir)), + .maybe_sub(child.margin.cross_axis_sum(dir)), ); } ckd @@ -809,14 +816,15 @@ fn collect_flex_lines<'a>( /// Determine the container's main size (if not already known) fn determine_container_main_size( tree: &mut impl LayoutTree, - main_axis_available_space: AvailableSpace, + available_space: Size, lines: &mut Vec>, constants: &mut AlgoConstants, ) { + let dir = constants.dir; let main_padding_border = constants.padding_border.main_axis_sum(constants.dir); let outer_main_size: f32 = constants.node_outer_size.main(constants.dir).unwrap_or_else(|| { - match main_axis_available_space { + match available_space.main(dir) { AvailableSpace::Definite(main_axis_available_space) => { let longest_line_length: f32 = lines .iter() @@ -902,6 +910,20 @@ fn determine_container_main_size( // Else compute the min- or -max content size and apply the full formula for computing the // min- or max- content contributuon _ => { + // Parent size for child sizing + let cross_axis_parent_size = constants.node_inner_size.cross(dir); + + // Available space for child sizing + let cross_axis_margin_sum = constants.margin.cross_axis_sum(dir); + let child_min_cross = item.min_size.cross(dir).maybe_add(cross_axis_margin_sum); + let child_max_cross = item.max_size.cross(dir).maybe_add(cross_axis_margin_sum); + let cross_axis_available_space: AvailableSpace = available_space + .cross(dir) + .map_definite_value(|val| cross_axis_parent_size.unwrap_or(val)) + .maybe_clamp(child_min_cross, child_max_cross); + + let child_available_space = available_space.with_cross(dir, cross_axis_available_space); + // Either the min- or max- content size depending on which constraint we are sizing under. // TODO: Optimise by using already computed values where available let content_main_size = GenericAlgorithm::measure_size( @@ -909,7 +931,7 @@ fn determine_container_main_size( item.node, Size::NONE, constants.node_inner_size, - Size { width: main_axis_available_space, height: main_axis_available_space }, + child_available_space, SizingMode::InherentSize, ) .main(constants.dir) @@ -1436,7 +1458,7 @@ fn handle_align_content_stretch(flex_lines: &mut [FlexLine], node_size: Size>, constants: &mut AlgoConstants, ) -> f32 { diff --git a/src/compute/grid/explicit_grid.rs b/src/compute/grid/explicit_grid.rs index ef04a61fb..2b3d773a8 100644 --- a/src/compute/grid/explicit_grid.rs +++ b/src/compute/grid/explicit_grid.rs @@ -7,7 +7,9 @@ use crate::resolve::ResolveOrZero; use crate::style::{GridTrackRepetition, LengthPercentage, NonRepeatedTrackSizingFunction, Style, TrackSizingFunction}; use crate::style_helpers::TaffyAuto; use crate::sys::{GridTrackVec, Vec}; -use core::cmp::{max, min}; + +#[cfg(not(feature = "std"))] +use num_traits::float::FloatCore; /// Compute the number of rows and columns in the explicit grid pub(crate) fn compute_explicit_grid_size_in_axis(style: &Style, axis: AbsoluteAxis) -> u16 { @@ -191,15 +193,15 @@ pub(super) fn initialize_grid_tracks( tracks.push(GridTrack::gutter(gap)); // Create negative implicit tracks - if auto_tracks.is_empty() { - let iter = core::iter::repeat(NonRepeatedTrackSizingFunction::AUTO); - create_implicit_tracks(tracks, counts.negative_implicit, iter, gap) - } else { - let max_count = max(auto_tracks.len(), counts.negative_implicit as usize); - let min_count = min(auto_tracks.len(), counts.negative_implicit as usize); - let offset = max_count % min_count; - let iter = auto_tracks.iter().copied().cycle().skip(offset); - create_implicit_tracks(tracks, counts.negative_implicit, iter, gap) + if counts.negative_implicit > 0 { + if auto_tracks.is_empty() { + let iter = core::iter::repeat(NonRepeatedTrackSizingFunction::AUTO); + create_implicit_tracks(tracks, counts.negative_implicit, iter, gap) + } else { + let offset = auto_tracks.len() - (counts.negative_implicit as usize % auto_tracks.len()); + let iter = auto_tracks.iter().copied().cycle().skip(offset); + create_implicit_tracks(tracks, counts.negative_implicit, iter, gap) + } } let mut current_track_index = (counts.negative_implicit) as usize; diff --git a/src/compute/grid/mod.rs b/src/compute/grid/mod.rs index 09a91eb09..a1393e26a 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,16 +228,23 @@ 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 { width: resolved_style_size .get(AbstractAxis::Inline) .unwrap_or_else(|| initial_column_sum + padding.horizontal_axis_sum() + border.horizontal_axis_sum()) + .maybe_clamp(min_size.width, max_size.width) .max(padding_border_size.width), height: resolved_style_size .get(AbstractAxis::Block) .unwrap_or_else(|| initial_row_sum + padding.vertical_axis_sum() + border.vertical_axis_sum()) + .maybe_clamp(min_size.height, max_size.height) .max(padding_border_size.height), }; let container_content_box = Size { diff --git a/src/compute/grid/placement.rs b/src/compute/grid/placement.rs index d02e7af8e..33350dab0 100644 --- a/src/compute/grid/placement.rs +++ b/src/compute/grid/placement.rs @@ -113,9 +113,11 @@ pub(super) fn place_grid_items<'a, ChildIter>( // 4. Position the remaining grid items // (which either have definite position only in the secondary axis or indefinite positions in both axis) - let x_neg_tracks = cell_occupancy_matrix.track_counts(AbsoluteAxis::Horizontal).negative_implicit as i16; - let y_neg_tracks = cell_occupancy_matrix.track_counts(AbsoluteAxis::Vertical).negative_implicit as i16; - let grid_start_position = (OriginZeroLine(-x_neg_tracks), OriginZeroLine(-y_neg_tracks)); + let primary_axis = grid_auto_flow.primary_axis(); + let secondary_axis = primary_axis.other_axis(); + let primary_neg_tracks = cell_occupancy_matrix.track_counts(primary_axis).negative_implicit as i16; + let secondary_neg_tracks = cell_occupancy_matrix.track_counts(secondary_axis).negative_implicit as i16; + let grid_start_position = (OriginZeroLine(-primary_neg_tracks), OriginZeroLine(-secondary_neg_tracks)); let mut grid_position = grid_start_position; let mut idx = 0; children_iter() diff --git a/src/compute/grid/track_sizing.rs b/src/compute/grid/track_sizing.rs index a1fa6db78..3f5a671c4 100644 --- a/src/compute/grid/track_sizing.rs +++ b/src/compute/grid/track_sizing.rs @@ -108,7 +108,7 @@ where /// Compute the item's resolved margins for size contributions. Horizontal percentage margins always resolve /// to zero if the container size is indefinite as otherwise this would introduce a cyclic dependency. #[inline(always)] - fn margins_axis_sums_with_baseline_shims(&self, item: &mut GridItem) -> Size { + fn margins_axis_sums_with_baseline_shims(&self, item: &GridItem) -> Size { item.margins_axis_sums_with_baseline_shims(self.inner_node_size.width) } @@ -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 @@ -502,7 +515,7 @@ fn resolve_intrinsic_track_sizes( tree: &mut impl LayoutTree, axis: AbstractAxis, axis_tracks: &mut [GridTrack], - other_axis_tracks: &mut [GridTrack], + other_axis_tracks: &[GridTrack], items: &mut [GridItem], axis_available_grid_space: AvailableSpace, inner_node_size: Size>, @@ -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/src/compute/grid/types/cell_occupancy.rs b/src/compute/grid/types/cell_occupancy.rs index 35b3d5cef..cf006e75c 100644 --- a/src/compute/grid/types/cell_occupancy.rs +++ b/src/compute/grid/types/cell_occupancy.rs @@ -91,10 +91,9 @@ impl CellOccupancyMatrix { fn expand_to_fit_range(&mut self, row_range: Range, col_range: Range) { // Calculate number of rows and columns missing to accomodate ranges (if any) let req_negative_rows = min(row_range.start, 0); - let req_positive_rows = max(row_range.end - self.rows.explicit as i16 - self.rows.positive_implicit as i16, 0); + let req_positive_rows = max(row_range.end - self.rows.len() as i16, 0); let req_negative_cols = min(col_range.start, 0); - let req_positive_cols = - max(col_range.end - self.columns.explicit as i16 - self.columns.positive_implicit as i16, 0); + let req_positive_cols = max(col_range.end - self.columns.len() as i16, 0); let old_row_count = self.rows.len(); let old_col_count = self.columns.len(); diff --git a/src/compute/grid/types/grid_item.rs b/src/compute/grid/types/grid_item.rs index 513038d2f..510a420a3 100644 --- a/src/compute/grid/types/grid_item.rs +++ b/src/compute/grid/types/grid_item.rs @@ -207,7 +207,7 @@ impl GridItem { /// allow percentage sizes further down the tree to resolve properly in some cases fn known_dimensions( &self, - tree: &mut impl LayoutTree, + tree: &impl LayoutTree, inner_node_size: Size>, grid_area_size: Size>, ) -> Size> { diff --git a/src/compute/leaf.rs b/src/compute/leaf.rs index 6f95145db..cd913efcd 100644 --- a/src/compute/leaf.rs +++ b/src/compute/leaf.rs @@ -43,7 +43,7 @@ impl LayoutAlgorithm for LeafAlgorithm { /// Compute the size of a leaf node (node with no children) pub(crate) fn compute( - tree: &mut impl LayoutTree, + tree: &impl LayoutTree, node: Node, known_dimensions: Size>, parent_size: Size>, @@ -74,6 +74,7 @@ pub(crate) fn compute( // Note: both horizontal and vertical percentage padding/borders are resolved against the container's inline size (i.e. width). // This is not a bug, but is how CSS is specified (see: https://developer.mozilla.org/en-US/docs/Web/CSS/padding#values) + let margin = style.margin.resolve_or_zero(parent_size.width); let padding = style.padding.resolve_or_zero(parent_size.width); let border = style.border.resolve_or_zero(parent_size.width); let padding_border = padding + border; @@ -100,11 +101,13 @@ pub(crate) fn compute( let available_space = Size { width: available_space .width + .maybe_sub(margin.horizontal_axis_sum()) .maybe_set(node_size.width) .maybe_set(node_max_size.width) .map_definite_value(|size| size.maybe_clamp(node_min_size.width, node_max_size.width)), height: available_space .height + .maybe_sub(margin.vertical_axis_sum()) .maybe_set(node_size.height) .maybe_set(node_max_size.height) .map_definite_value(|size| size.maybe_clamp(node_min_size.height, node_max_size.height)), diff --git a/src/compute/mod.rs b/src/compute/mod.rs index 6fd974350..48ad57ea1 100644 --- a/src/compute/mod.rs +++ b/src/compute/mod.rs @@ -26,6 +26,8 @@ use crate::debug::NODE_LOGGER; /// Updates the stored layout of the provided `node` and its children pub fn compute_layout(taffy: &mut Taffy, root: Node, available_space: Size) -> Result<(), TaffyError> { + taffy.is_layouting = true; + // Recursively compute node layout let size_and_baselines = GenericAlgorithm::perform_layout( taffy, @@ -44,6 +46,8 @@ pub fn compute_layout(taffy: &mut Taffy, root: Node, available_space: Size for more context -fn round_layout(tree: &mut impl LayoutTree, node: Node, abs_x: f32, abs_y: f32) { - let layout = tree.layout_mut(node); - let abs_x = abs_x + layout.location.x; - let abs_y = abs_y + layout.location.y; - - layout.location.x = round(layout.location.x); - layout.location.y = round(layout.location.y); - layout.size.width = round(abs_x + layout.size.width) - round(abs_x); - layout.size.height = round(abs_y + layout.size.height) - round(abs_y); - - let child_count = tree.child_count(node); +/// +/// In order to prevent innacuracies caused by rounding already-rounded values, we read from `unrounded_layout` +/// and write to `final_layout`. +fn round_layout(tree: &mut Taffy, node_id: Node, cumulative_x: f32, cumulative_y: f32) { + let node = &mut tree.nodes[node_id]; + let unrounded_layout = node.unrounded_layout; + let layout = &mut node.final_layout; + + let cumulative_x = cumulative_x + unrounded_layout.location.x; + let cumulative_y = cumulative_y + unrounded_layout.location.y; + + layout.location.x = round(unrounded_layout.location.x); + layout.location.y = round(unrounded_layout.location.y); + layout.size.width = round(cumulative_x + unrounded_layout.size.width) - round(cumulative_x); + layout.size.height = round(cumulative_y + unrounded_layout.size.height) - round(cumulative_y); + + let child_count = tree.child_count(node_id).unwrap(); for index in 0..child_count { - let child = tree.child(node, index); - round_layout(tree, child, abs_x, abs_y); + let child = tree.child(node_id, index); + round_layout(tree, child, cumulative_x, cumulative_y); } } diff --git a/src/data.rs b/src/data.rs index 03a06c673..729a9da9c 100644 --- a/src/data.rs +++ b/src/data.rs @@ -14,8 +14,14 @@ pub(crate) const CACHE_SIZE: usize = 7; pub(crate) struct NodeData { /// The layout strategy used by this node pub(crate) style: Style, - /// The results of the layout computation - pub(crate) layout: Layout, + + /// The always unrounded results of the layout computation. We must store this separately from the rounded + /// layout to avoid errors from rounding already-rounded values. See . + pub(crate) unrounded_layout: Layout, + + /// The final results of the layout computation. + /// These may be rounded or unrounded depending on what the `use_rounding` config setting is set to. + pub(crate) final_layout: Layout, /// Should we try and measure this node? pub(crate) needs_measure: bool, @@ -28,7 +34,13 @@ impl NodeData { /// Create the data for a new node #[must_use] pub const fn new(style: Style) -> Self { - Self { style, size_cache: [None; CACHE_SIZE], layout: Layout::new(), needs_measure: false } + Self { + style, + size_cache: [None; CACHE_SIZE], + unrounded_layout: Layout::new(), + final_layout: Layout::new(), + needs_measure: false, + } } /// Marks a node and all of its parents (recursively) as dirty diff --git a/src/node.rs b/src/node.rs index 500f9d9eb..d4789c09a 100644 --- a/src/node.rs +++ b/src/node.rs @@ -64,6 +64,11 @@ pub struct Taffy { /// Layout mode configuration pub(crate) config: TaffyConfig, + + /// Hack to allow the `LayoutTree::layout_mut` function to expose the `NodeData.unrounded_layout` of a node to + /// the layout algorithms during layout, while exposing the `NodeData.final_layout` when called by external users. + /// This allows us to fix without breaking backwards compatibility + pub(crate) is_layouting: bool, } impl Default for Taffy { @@ -95,12 +100,22 @@ impl LayoutTree for Taffy { &self.nodes[node].style } + #[inline(always)] fn layout(&self, node: Node) -> &Layout { - &self.nodes[node].layout + if self.is_layouting && self.config.use_rounding { + &self.nodes[node].unrounded_layout + } else { + &self.nodes[node].final_layout + } } + #[inline(always)] fn layout_mut(&mut self, node: Node) -> &mut Layout { - &mut self.nodes[node].layout + if self.is_layouting && self.config.use_rounding { + &mut self.nodes[node].unrounded_layout + } else { + &mut self.nodes[node].final_layout + } } #[inline(always)] @@ -156,6 +171,7 @@ impl Taffy { parents: SlotMap::with_capacity(capacity), measure_funcs: SparseSecondaryMap::with_capacity(capacity), config: TaffyConfig::default(), + is_layouting: false, } } @@ -225,6 +241,12 @@ impl Taffy { } } + if let Some(children) = self.children.get(node) { + for child in children.iter().copied() { + self.parents[child] = None; + } + } + let _ = self.children.remove(node); let _ = self.parents.remove(node); let _ = self.nodes.remove(node); @@ -352,7 +374,7 @@ impl Taffy { /// Return this node layout relative to its parent pub fn layout(&self, node: Node) -> TaffyResult<&Layout> { - Ok(&self.nodes[node].layout) + Ok(&self.nodes[node].final_layout) } /// Marks the layout computation of this node and its children as outdated diff --git a/test_fixtures/bevy_issue_9530.html b/test_fixtures/bevy_issue_9530.html new file mode 100644 index 000000000..4b346eb59 --- /dev/null +++ b/test_fixtures/bevy_issue_9530.html @@ -0,0 +1,23 @@ + + + + + + + Test description + + + + +
+
+
+
+
HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH​HHHH
+
+ +
+
+ + + \ No newline at end of file diff --git a/test_fixtures/bevy_issue_9530_reduced.html b/test_fixtures/bevy_issue_9530_reduced.html new file mode 100644 index 000000000..4d6f223f8 --- /dev/null +++ b/test_fixtures/bevy_issue_9530_reduced.html @@ -0,0 +1,19 @@ + + + + + + + Test description + + + + +
+
+
HH​HH​HH​HH
+
+
+ + + \ No newline at end of file diff --git a/test_fixtures/bevy_issue_9530_reduced2.html b/test_fixtures/bevy_issue_9530_reduced2.html new file mode 100644 index 000000000..f488740ae --- /dev/null +++ b/test_fixtures/bevy_issue_9530_reduced2.html @@ -0,0 +1,19 @@ + + + + + + + Test description + + + + +
+
+
HH​HH​HH​HH​HH​HH​HH​HH
+
+
+ + + \ No newline at end of file diff --git a/test_fixtures/bevy_issue_9530_reduced3.html b/test_fixtures/bevy_issue_9530_reduced3.html new file mode 100644 index 000000000..44488c9c0 --- /dev/null +++ b/test_fixtures/bevy_issue_9530_reduced3.html @@ -0,0 +1,17 @@ + + + + + + + Test description + + + + +
+
HH​HH​HH​HH​HH​HH​HH​HH
+
+ + + \ No newline at end of file diff --git a/test_fixtures/bevy_issue_9530_reduced4.html b/test_fixtures/bevy_issue_9530_reduced4.html new file mode 100644 index 000000000..42f84c156 --- /dev/null +++ b/test_fixtures/bevy_issue_9530_reduced4.html @@ -0,0 +1,17 @@ + + + + + + + Test description + + + + +
+
HH​HH​HH​HH​HH​HH​HH​HH
+
+ + + \ No newline at end of file diff --git a/test_fixtures/grid_auto_columns.html b/test_fixtures/grid_auto_columns.html new file mode 100644 index 000000000..7e717bef0 --- /dev/null +++ b/test_fixtures/grid_auto_columns.html @@ -0,0 +1,24 @@ + + + + + + + Test description + + + + +
+
+
+
+
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/test_fixtures/grid_auto_rows.html b/test_fixtures/grid_auto_rows.html new file mode 100644 index 000000000..19f55d5bc --- /dev/null +++ b/test_fixtures/grid_auto_rows.html @@ -0,0 +1,24 @@ + + + + + + + Test description + + + + +
+
+
+
+
+
+
+
+
+
+ + + \ No newline at end of file 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/bevy_issue_9530.rs b/tests/generated/bevy_issue_9530.rs new file mode 100644 index 000000000..4641f9fa5 --- /dev/null +++ b/tests/generated/bevy_issue_9530.rs @@ -0,0 +1,114 @@ +#[test] +fn bevy_issue_9530() { + use slotmap::Key; + #[allow(unused_imports)] + use taffy::{layout::Layout, prelude::*}; + let mut taffy = taffy::Taffy::new(); + let node0 = taffy + .new_leaf(taffy::style::Style { + flex_direction: taffy::style::FlexDirection::Column, + size: taffy::geometry::Size { + width: taffy::style::Dimension::Percent(1f32), + height: taffy::style::Dimension::Points(20f32), + }, + ..Default::default() + }) + .unwrap(); + let node10 = taffy + .new_leaf(taffy::style::Style { + size: taffy::geometry::Size { + width: taffy::style::Dimension::Percent(1f32), + height: taffy::style::Dimension::Points(50f32), + }, + ..Default::default() + }) + .unwrap(); + let node11 = taffy . new_leaf_with_measure (taffy :: style :: Style { align_items : Some (taffy :: style :: AlignItems :: Center) , align_content : Some (taffy :: style :: AlignContent :: Center) , justify_content : Some (taffy :: style :: JustifyContent :: Center) , flex_grow : 1f32 , margin : taffy :: geometry :: Rect { left : taffy :: style :: LengthPercentageAuto :: Points (20f32) , right : taffy :: style :: LengthPercentageAuto :: Points (20f32) , top : taffy :: style :: LengthPercentageAuto :: Points (20f32) , bottom : taffy :: style :: LengthPercentageAuto :: Points (20f32) , } , .. Default :: default () } , taffy :: node :: MeasureFunc :: Raw (| known_dimensions , available_space | { const TEXT : & str = "HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH\u{200b}HHHH" ; super :: measure_standard_text (known_dimensions , available_space , TEXT , super :: WritingMode :: Horizontal , None) }) ,) . unwrap () ; + let node12 = taffy + .new_leaf(taffy::style::Style { + size: taffy::geometry::Size { + width: taffy::style::Dimension::Percent(1f32), + height: taffy::style::Dimension::Points(50f32), + }, + ..Default::default() + }) + .unwrap(); + let node1 = taffy + .new_with_children( + taffy::style::Style { + flex_direction: taffy::style::FlexDirection::Column, + flex_grow: 1f32, + size: taffy::geometry::Size { width: taffy::style::Dimension::Percent(1f32), height: auto() }, + margin: taffy::geometry::Rect { + left: taffy::style::LengthPercentageAuto::Points(20f32), + right: taffy::style::LengthPercentageAuto::Points(20f32), + top: taffy::style::LengthPercentageAuto::Points(20f32), + bottom: taffy::style::LengthPercentageAuto::Points(20f32), + }, + padding: taffy::geometry::Rect { + left: taffy::style::LengthPercentage::Points(20f32), + right: taffy::style::LengthPercentage::Points(20f32), + top: taffy::style::LengthPercentage::Points(20f32), + bottom: taffy::style::LengthPercentage::Points(20f32), + }, + ..Default::default() + }, + &[node10, node11, node12], + ) + .unwrap(); + let node = taffy + .new_with_children( + taffy::style::Style { + flex_direction: taffy::style::FlexDirection::Column, + align_items: Some(taffy::style::AlignItems::Center), + align_content: Some(taffy::style::AlignContent::Center), + size: taffy::geometry::Size { + width: taffy::style::Dimension::Points(300f32), + height: taffy::style::Dimension::Points(300f32), + }, + margin: taffy::geometry::Rect { + left: taffy::style::LengthPercentageAuto::Auto, + right: taffy::style::LengthPercentageAuto::Auto, + top: taffy::style::LengthPercentageAuto::Auto, + bottom: taffy::style::LengthPercentageAuto::Auto, + }, + ..Default::default() + }, + &[node0, node1], + ) + .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, 300f32, "width of node {:?}. Expected {}. Actual {}", node.data(), 300f32, size.width); + assert_eq!(size.height, 300f32, "height of node {:?}. Expected {}. Actual {}", node.data(), 300f32, 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, 300f32, "width of node {:?}. Expected {}. Actual {}", node0.data(), 300f32, size.width); + assert_eq!(size.height, 0f32, "height of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, 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, 300f32, "width of node {:?}. Expected {}. Actual {}", node1.data(), 300f32, size.width); + assert_eq!(size.height, 420f32, "height of node {:?}. Expected {}. Actual {}", node1.data(), 420f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node1.data(), 0f32, location.x); + assert_eq!(location.y, 20f32, "y of node {:?}. Expected {}. Actual {}", node1.data(), 20f32, location.y); + let Layout { size, location, .. } = taffy.layout(node10).unwrap(); + assert_eq!(size.width, 260f32, "width of node {:?}. Expected {}. Actual {}", node10.data(), 260f32, size.width); + assert_eq!(size.height, 50f32, "height of node {:?}. Expected {}. Actual {}", node10.data(), 50f32, size.height); + assert_eq!(location.x, 20f32, "x of node {:?}. Expected {}. Actual {}", node10.data(), 20f32, location.x); + assert_eq!(location.y, 20f32, "y of node {:?}. Expected {}. Actual {}", node10.data(), 20f32, location.y); + let Layout { size, location, .. } = taffy.layout(node11).unwrap(); + assert_eq!(size.width, 220f32, "width of node {:?}. Expected {}. Actual {}", node11.data(), 220f32, size.width); + assert_eq!(size.height, 240f32, "height of node {:?}. Expected {}. Actual {}", node11.data(), 240f32, size.height); + assert_eq!(location.x, 40f32, "x of node {:?}. Expected {}. Actual {}", node11.data(), 40f32, location.x); + assert_eq!(location.y, 90f32, "y of node {:?}. Expected {}. Actual {}", node11.data(), 90f32, location.y); + let Layout { size, location, .. } = taffy.layout(node12).unwrap(); + assert_eq!(size.width, 260f32, "width of node {:?}. Expected {}. Actual {}", node12.data(), 260f32, size.width); + assert_eq!(size.height, 50f32, "height of node {:?}. Expected {}. Actual {}", node12.data(), 50f32, size.height); + assert_eq!(location.x, 20f32, "x of node {:?}. Expected {}. Actual {}", node12.data(), 20f32, location.x); + assert_eq!(location.y, 350f32, "y of node {:?}. Expected {}. Actual {}", node12.data(), 350f32, location.y); +} diff --git a/tests/generated/bevy_issue_9530_reduced.rs b/tests/generated/bevy_issue_9530_reduced.rs new file mode 100644 index 000000000..449546295 --- /dev/null +++ b/tests/generated/bevy_issue_9530_reduced.rs @@ -0,0 +1,61 @@ +#[test] +fn bevy_issue_9530_reduced() { + 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 { flex_grow: 1f32, ..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 { + flex_direction: taffy::style::FlexDirection::Column, + flex_grow: 1f32, + ..Default::default() + }, + &[node00], + ) + .unwrap(); + let node = taffy + .new_with_children( + taffy::style::Style { + flex_direction: taffy::style::FlexDirection::Column, + size: taffy::geometry::Size { width: taffy::style::Dimension::Points(40f32), height: auto() }, + ..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, 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(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); +} diff --git a/tests/generated/bevy_issue_9530_reduced2.rs b/tests/generated/bevy_issue_9530_reduced2.rs new file mode 100644 index 000000000..41eca5abc --- /dev/null +++ b/tests/generated/bevy_issue_9530_reduced2.rs @@ -0,0 +1,64 @@ +#[test] +fn bevy_issue_9530_reduced2() { + 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 { flex_grow: 1f32, ..Default::default() }, + taffy::node::MeasureFunc::Raw(|known_dimensions, available_space| { + const TEXT: &str = "HH\u{200b}HH\u{200b}HH\u{200b}HH\u{200b}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 { + flex_direction: taffy::style::FlexDirection::Column, + flex_grow: 1f32, + size: taffy::geometry::Size { width: taffy::style::Dimension::Points(80f32), height: auto() }, + margin: taffy::geometry::Rect { + left: taffy::style::LengthPercentageAuto::Points(20f32), + right: taffy::style::LengthPercentageAuto::Points(20f32), + top: taffy::style::LengthPercentageAuto::Points(0f32), + bottom: taffy::style::LengthPercentageAuto::Points(0f32), + }, + ..Default::default() + }, + &[node00], + ) + .unwrap(); + let node = taffy + .new_with_children( + taffy::style::Style { flex_direction: taffy::style::FlexDirection::Column, ..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, 120f32, "width of node {:?}. Expected {}. Actual {}", node.data(), 120f32, 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, 20f32, "x of node {:?}. Expected {}. Actual {}", node0.data(), 20f32, 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, 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); +} diff --git a/tests/generated/bevy_issue_9530_reduced3.rs b/tests/generated/bevy_issue_9530_reduced3.rs new file mode 100644 index 000000000..f29fb6d40 --- /dev/null +++ b/tests/generated/bevy_issue_9530_reduced3.rs @@ -0,0 +1,55 @@ +#[test] +fn bevy_issue_9530_reduced3() { + 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 { + flex_grow: 1f32, + margin: taffy::geometry::Rect { + left: taffy::style::LengthPercentageAuto::Points(20f32), + right: taffy::style::LengthPercentageAuto::Points(20f32), + top: taffy::style::LengthPercentageAuto::Points(0f32), + bottom: taffy::style::LengthPercentageAuto::Points(0f32), + }, + ..Default::default() + }, + taffy::node::MeasureFunc::Raw(|known_dimensions, available_space| { + const TEXT: &str = "HH\u{200b}HH\u{200b}HH\u{200b}HH\u{200b}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 { + flex_direction: taffy::style::FlexDirection::Column, + size: taffy::geometry::Size { width: taffy::style::Dimension::Points(80f32), height: auto() }, + ..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, 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, 20f32, "x of node {:?}. Expected {}. Actual {}", node0.data(), 20f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, location.y); +} diff --git a/tests/generated/bevy_issue_9530_reduced4.rs b/tests/generated/bevy_issue_9530_reduced4.rs new file mode 100644 index 000000000..fac6668f2 --- /dev/null +++ b/tests/generated/bevy_issue_9530_reduced4.rs @@ -0,0 +1,54 @@ +#[test] +fn bevy_issue_9530_reduced4() { + 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 { + margin: taffy::geometry::Rect { + left: taffy::style::LengthPercentageAuto::Points(20f32), + right: taffy::style::LengthPercentageAuto::Points(20f32), + top: taffy::style::LengthPercentageAuto::Points(20f32), + bottom: taffy::style::LengthPercentageAuto::Points(20f32), + }, + ..Default::default() + }, + taffy::node::MeasureFunc::Raw(|known_dimensions, available_space| { + const TEXT: &str = "HH\u{200b}HH\u{200b}HH\u{200b}HH\u{200b}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 { + flex_direction: taffy::style::FlexDirection::Column, + size: taffy::geometry::Size { width: taffy::style::Dimension::Points(80f32), height: auto() }, + ..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, 80f32, "height of node {:?}. Expected {}. Actual {}", node.data(), 80f32, 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, 20f32, "x of node {:?}. Expected {}. Actual {}", node0.data(), 20f32, location.x); + assert_eq!(location.y, 20f32, "y of node {:?}. Expected {}. Actual {}", node0.data(), 20f32, location.y); +} diff --git a/tests/generated/grid_auto_columns.rs b/tests/generated/grid_auto_columns.rs new file mode 100644 index 000000000..40e498581 --- /dev/null +++ b/tests/generated/grid_auto_columns.rs @@ -0,0 +1,82 @@ +#[test] +fn grid_auto_columns() { + use slotmap::Key; + #[allow(unused_imports)] + use taffy::{layout::Layout, prelude::*}; + let mut taffy = taffy::Taffy::new(); + let node0 = taffy + .new_leaf(taffy::style::Style { + grid_column: taffy::geometry::Line { start: line(-3i16), end: taffy::style::GridPlacement::Auto }, + ..Default::default() + }) + .unwrap(); + let node1 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap(); + let node2 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap(); + let node3 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap(); + let node4 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap(); + let node5 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap(); + let node6 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap(); + let node7 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap(); + let node = taffy + .new_with_children( + taffy::style::Style { + display: taffy::style::Display::Grid, + grid_template_rows: vec![points(100f32)], + grid_template_columns: vec![points(40f32)], + grid_auto_columns: vec![points(10f32), points(20f32), points(30f32)], + grid_auto_flow: taffy::style::GridAutoFlow::Column, + ..Default::default() + }, + &[node0, node1, node2, node3, node4, node5, node6, node7], + ) + .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, 190f32, "width of node {:?}. Expected {}. Actual {}", node.data(), 190f32, size.width); + assert_eq!(size.height, 100f32, "height of node {:?}. Expected {}. Actual {}", node.data(), 100f32, 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, 30f32, "width of node {:?}. Expected {}. Actual {}", node0.data(), 30f32, size.width); + assert_eq!(size.height, 100f32, "height of node {:?}. Expected {}. Actual {}", node0.data(), 100f32, 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, 100f32, "height of node {:?}. Expected {}. Actual {}", node1.data(), 100f32, size.height); + assert_eq!(location.x, 30f32, "x of node {:?}. Expected {}. Actual {}", node1.data(), 30f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node1.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node2).unwrap(); + assert_eq!(size.width, 10f32, "width of node {:?}. Expected {}. Actual {}", node2.data(), 10f32, size.width); + assert_eq!(size.height, 100f32, "height of node {:?}. Expected {}. Actual {}", node2.data(), 100f32, size.height); + assert_eq!(location.x, 70f32, "x of node {:?}. Expected {}. Actual {}", node2.data(), 70f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node2.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node3).unwrap(); + assert_eq!(size.width, 20f32, "width of node {:?}. Expected {}. Actual {}", node3.data(), 20f32, size.width); + assert_eq!(size.height, 100f32, "height of node {:?}. Expected {}. Actual {}", node3.data(), 100f32, size.height); + assert_eq!(location.x, 80f32, "x of node {:?}. Expected {}. Actual {}", node3.data(), 80f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node3.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node4).unwrap(); + assert_eq!(size.width, 30f32, "width of node {:?}. Expected {}. Actual {}", node4.data(), 30f32, size.width); + assert_eq!(size.height, 100f32, "height of node {:?}. Expected {}. Actual {}", node4.data(), 100f32, size.height); + assert_eq!(location.x, 100f32, "x of node {:?}. Expected {}. Actual {}", node4.data(), 100f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node4.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node5).unwrap(); + assert_eq!(size.width, 10f32, "width of node {:?}. Expected {}. Actual {}", node5.data(), 10f32, size.width); + assert_eq!(size.height, 100f32, "height of node {:?}. Expected {}. Actual {}", node5.data(), 100f32, size.height); + assert_eq!(location.x, 130f32, "x of node {:?}. Expected {}. Actual {}", node5.data(), 130f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node5.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node6).unwrap(); + assert_eq!(size.width, 20f32, "width of node {:?}. Expected {}. Actual {}", node6.data(), 20f32, size.width); + assert_eq!(size.height, 100f32, "height of node {:?}. Expected {}. Actual {}", node6.data(), 100f32, size.height); + assert_eq!(location.x, 140f32, "x of node {:?}. Expected {}. Actual {}", node6.data(), 140f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node6.data(), 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node7).unwrap(); + assert_eq!(size.width, 30f32, "width of node {:?}. Expected {}. Actual {}", node7.data(), 30f32, size.width); + assert_eq!(size.height, 100f32, "height of node {:?}. Expected {}. Actual {}", node7.data(), 100f32, size.height); + assert_eq!(location.x, 160f32, "x of node {:?}. Expected {}. Actual {}", node7.data(), 160f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node7.data(), 0f32, location.y); +} diff --git a/tests/generated/grid_auto_rows.rs b/tests/generated/grid_auto_rows.rs new file mode 100644 index 000000000..5019a9878 --- /dev/null +++ b/tests/generated/grid_auto_rows.rs @@ -0,0 +1,81 @@ +#[test] +fn grid_auto_rows() { + use slotmap::Key; + #[allow(unused_imports)] + use taffy::{layout::Layout, prelude::*}; + let mut taffy = taffy::Taffy::new(); + let node0 = taffy + .new_leaf(taffy::style::Style { + grid_row: taffy::geometry::Line { start: line(-4i16), end: taffy::style::GridPlacement::Auto }, + ..Default::default() + }) + .unwrap(); + let node1 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap(); + let node2 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap(); + let node3 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap(); + let node4 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap(); + let node5 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap(); + let node6 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap(); + let node7 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap(); + let node = taffy + .new_with_children( + taffy::style::Style { + display: taffy::style::Display::Grid, + grid_template_rows: vec![points(40f32)], + grid_template_columns: vec![points(100f32)], + grid_auto_rows: vec![points(10f32), points(20f32), points(30f32)], + ..Default::default() + }, + &[node0, node1, node2, node3, node4, node5, node6, node7], + ) + .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, 100f32, "width of node {:?}. Expected {}. Actual {}", node.data(), 100f32, size.width); + assert_eq!(size.height, 180f32, "height of node {:?}. Expected {}. Actual {}", node.data(), 180f32, 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, 100f32, "width of node {:?}. Expected {}. Actual {}", node0.data(), 100f32, 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, 100f32, "width of node {:?}. Expected {}. Actual {}", node1.data(), 100f32, size.width); + assert_eq!(size.height, 30f32, "height of node {:?}. Expected {}. Actual {}", node1.data(), 30f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node1.data(), 0f32, location.x); + assert_eq!(location.y, 20f32, "y of node {:?}. Expected {}. Actual {}", node1.data(), 20f32, location.y); + let Layout { size, location, .. } = taffy.layout(node2).unwrap(); + assert_eq!(size.width, 100f32, "width of node {:?}. Expected {}. Actual {}", node2.data(), 100f32, size.width); + assert_eq!(size.height, 40f32, "height of node {:?}. Expected {}. Actual {}", node2.data(), 40f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node2.data(), 0f32, location.x); + assert_eq!(location.y, 50f32, "y of node {:?}. Expected {}. Actual {}", node2.data(), 50f32, location.y); + let Layout { size, location, .. } = taffy.layout(node3).unwrap(); + assert_eq!(size.width, 100f32, "width of node {:?}. Expected {}. Actual {}", node3.data(), 100f32, size.width); + assert_eq!(size.height, 10f32, "height of node {:?}. Expected {}. Actual {}", node3.data(), 10f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node3.data(), 0f32, location.x); + assert_eq!(location.y, 90f32, "y of node {:?}. Expected {}. Actual {}", node3.data(), 90f32, location.y); + let Layout { size, location, .. } = taffy.layout(node4).unwrap(); + assert_eq!(size.width, 100f32, "width of node {:?}. Expected {}. Actual {}", node4.data(), 100f32, size.width); + assert_eq!(size.height, 20f32, "height of node {:?}. Expected {}. Actual {}", node4.data(), 20f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node4.data(), 0f32, location.x); + assert_eq!(location.y, 100f32, "y of node {:?}. Expected {}. Actual {}", node4.data(), 100f32, location.y); + let Layout { size, location, .. } = taffy.layout(node5).unwrap(); + assert_eq!(size.width, 100f32, "width of node {:?}. Expected {}. Actual {}", node5.data(), 100f32, size.width); + assert_eq!(size.height, 30f32, "height of node {:?}. Expected {}. Actual {}", node5.data(), 30f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node5.data(), 0f32, location.x); + assert_eq!(location.y, 120f32, "y of node {:?}. Expected {}. Actual {}", node5.data(), 120f32, location.y); + let Layout { size, location, .. } = taffy.layout(node6).unwrap(); + assert_eq!(size.width, 100f32, "width of node {:?}. Expected {}. Actual {}", node6.data(), 100f32, size.width); + assert_eq!(size.height, 10f32, "height of node {:?}. Expected {}. Actual {}", node6.data(), 10f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node6.data(), 0f32, location.x); + assert_eq!(location.y, 150f32, "y of node {:?}. Expected {}. Actual {}", node6.data(), 150f32, location.y); + let Layout { size, location, .. } = taffy.layout(node7).unwrap(); + assert_eq!(size.width, 100f32, "width of node {:?}. Expected {}. Actual {}", node7.data(), 100f32, size.width); + assert_eq!(size.height, 20f32, "height of node {:?}. Expected {}. Actual {}", node7.data(), 20f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node7.data(), 0f32, location.x); + assert_eq!(location.y, 160f32, "y of node {:?}. Expected {}. Actual {}", node7.data(), 160f32, location.y); +} 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 4be363806..7117f4f70 100644 --- a/tests/generated/mod.rs +++ b/tests/generated/mod.rs @@ -205,6 +205,11 @@ mod bevy_issue_8017; mod bevy_issue_8017_reduced; mod bevy_issue_8082; mod bevy_issue_8082_percent; +mod bevy_issue_9530; +mod bevy_issue_9530_reduced; +mod bevy_issue_9530_reduced2; +mod bevy_issue_9530_reduced3; +mod bevy_issue_9530_reduced4; mod border_center_child; mod border_container_match_child; mod border_flex_child; @@ -446,6 +451,8 @@ mod grid_aspect_ratio_overriden_by_explicit_sizes; #[cfg(feature = "grid")] mod grid_aspect_ratio_overriden_by_explicit_sizes_flex; #[cfg(feature = "grid")] +mod grid_auto_columns; +#[cfg(feature = "grid")] mod grid_auto_columns_fixed_width; #[cfg(feature = "grid")] mod grid_auto_fill_fixed_size; @@ -454,6 +461,8 @@ mod grid_auto_fill_with_empty_auto_track; #[cfg(feature = "grid")] mod grid_auto_fit_with_empty_auto_track; #[cfg(feature = "grid")] +mod grid_auto_rows; +#[cfg(feature = "grid")] mod grid_auto_single_item; #[cfg(feature = "grid")] mod grid_auto_single_item_fixed_width; @@ -462,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; @@ -586,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; diff --git a/tests/relayout.rs b/tests/relayout.rs index 2575141ec..6a771602c 100644 --- a/tests/relayout.rs +++ b/tests/relayout.rs @@ -319,3 +319,75 @@ fn toggle_grid_container_display_none() { assert_eq!(layout.size.width, 0.0); assert_eq!(layout.size.height, 0.0); } + +#[test] +fn relayout_is_stable_with_rounding() { + let mut taffy = Taffy::new(); + taffy.enable_rounding(); + + //
+ //
+ //
+ //
+ //
+ //
+ //
+ + let inner = + taffy.new_leaf(Style { min_size: Size { width: points(300.), height: auto() }, ..Default::default() }).unwrap(); + let wrapper = taffy + .new_with_children( + Style { + size: Size { width: points(150.), height: auto() }, + justify_content: Some(JustifyContent::End), + ..Default::default() + }, + &[inner], + ) + .unwrap(); + let outer = taffy + .new_with_children( + Style { + size: Size { width: percent(1.), height: auto() }, + inset: Rect { left: points(1.5), right: auto(), top: auto(), bottom: auto() }, + ..Default::default() + }, + &[wrapper], + ) + .unwrap(); + let root = taffy + .new_with_children( + Style { size: Size { width: points(1920.), height: points(1080.) }, ..Default::default() }, + &[outer], + ) + .unwrap(); + for _ in 0..5 { + taffy.mark_dirty(root).ok(); + taffy.compute_layout(root, Size::MAX_CONTENT).ok(); + taffy::debug::print_tree(&taffy, root); + + let root_layout = taffy.layout(root).unwrap(); + assert_eq!(root_layout.location.x, 0.0); + assert_eq!(root_layout.location.y, 0.0); + assert_eq!(root_layout.size.width, 1920.0); + assert_eq!(root_layout.size.height, 1080.0); + + let outer_layout = taffy.layout(outer).unwrap(); + assert_eq!(outer_layout.location.x, 2.0); + assert_eq!(outer_layout.location.y, 0.0); + assert_eq!(outer_layout.size.width, 1920.0); + assert_eq!(outer_layout.size.height, 1080.0); + + let wrapper_layout = taffy.layout(wrapper).unwrap(); + assert_eq!(wrapper_layout.location.x, 0.0); + assert_eq!(wrapper_layout.location.y, 0.0); + assert_eq!(wrapper_layout.size.width, 150.0); + assert_eq!(wrapper_layout.size.height, 1080.0); + + let inner_layout = taffy.layout(inner).unwrap(); + assert_eq!(inner_layout.location.x, -150.0); + assert_eq!(inner_layout.location.y, 0.0); + assert_eq!(inner_layout.size.width, 301.0); + assert_eq!(inner_layout.size.height, 1080.0); + } +}