From 9b4d0ccad229dff11f568ff906273d3ef80baba3 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Wed, 25 Sep 2024 14:23:40 +0200 Subject: [PATCH 1/5] feat: flex support --- crates/core/src/render/skia_measurer.rs | 1 + .../attributes/main_align_cross_align.md | 1 + crates/state/src/values/alignment.rs | 1 + crates/state/src/values/size.rs | 10 +++ crates/torin/src/measure.rs | 66 +++++++++++++++++-- crates/torin/src/values/alignment.rs | 6 ++ crates/torin/src/values/size.rs | 22 +++++-- examples/flex.rs | 53 +++++++++++++++ 8 files changed, 149 insertions(+), 11 deletions(-) create mode 100644 examples/flex.rs diff --git a/crates/core/src/render/skia_measurer.rs b/crates/core/src/render/skia_measurer.rs index dd98f41a8..0c7faba8e 100644 --- a/crates/core/src/render/skia_measurer.rs +++ b/crates/core/src/render/skia_measurer.rs @@ -213,6 +213,7 @@ pub fn align_main_align_paragraph(node: &DioxusNode, area: &Area, paragraph: &Pa Alignment::SpaceBetween => 0., Alignment::SpaceEvenly => 0., Alignment::SpaceAround => 0., + Alignment::Flex => 0., } } diff --git a/crates/elements/src/_docs/attributes/main_align_cross_align.md b/crates/elements/src/_docs/attributes/main_align_cross_align.md index d49ef4b75..212a76dfb 100644 --- a/crates/elements/src/_docs/attributes/main_align_cross_align.md +++ b/crates/elements/src/_docs/attributes/main_align_cross_align.md @@ -10,6 +10,7 @@ Accepted values for both attributes are: - `space-between`(only for `main_align`): Distributed among the available space - `space-around` (only for `main_align`): Distributed among the available space with small margins in the sides - `space-evenly` (only for `main_align`): Distributed among the available space with the same size of margins in the sides and in between the elements. +- `flex`: TODO When using the `vertical` direction, `main_align` will be the Y axis and `cross_align` will be the X axis. But when using the `horizontal` direction, the `main_align` will be the X axis and the `cross_align` will be the Y axis. diff --git a/crates/state/src/values/alignment.rs b/crates/state/src/values/alignment.rs index 93e4d5820..c9779b275 100644 --- a/crates/state/src/values/alignment.rs +++ b/crates/state/src/values/alignment.rs @@ -13,6 +13,7 @@ impl Parse for Alignment { "space-between" => Alignment::SpaceBetween, "space-evenly" => Alignment::SpaceEvenly, "space-around" => Alignment::SpaceAround, + "flex" => Alignment::Flex, _ => Alignment::Start, }) } diff --git a/crates/state/src/values/size.rs b/crates/state/src/values/size.rs index c92e7bdcf..d84ec77a6 100644 --- a/crates/state/src/values/size.rs +++ b/crates/state/src/values/size.rs @@ -15,6 +15,16 @@ impl Parse for Size { fn parse(value: &str) -> Result { if value == "auto" { Ok(Size::Inner) + } else if value == "flex" { + Ok(Size::Flex(Length::new(1.0))) + } else if value.contains("flex") { + Ok(Size::Flex(Length::new( + value + .replace("flex(", "") + .replace(')', "") + .parse::() + .map_err(|_| ParseError)?, + ))) } else if value == "fill" { Ok(Size::Fill) } else if value == "fill-min" { diff --git a/crates/torin/src/measure.rs b/crates/torin/src/measure.rs index 88b8771f7..2d4ea8ab4 100644 --- a/crates/torin/src/measure.rs +++ b/crates/torin/src/measure.rs @@ -296,8 +296,9 @@ where ) { let children = self.dom_adapter.children_of(parent_node_id); + let mut initial_phase_flex_factors = FxHashMap::default(); let mut initial_phase_sizes = FxHashMap::default(); - let mut initial_phase_inner_sizes = *inner_sizes; + let mut initial_phase_inner_sizes = Size2D::default(); // Used to calculate the spacing and some alignments let (non_absolute_children_len, first_child, last_child) = if parent_node.spacing.get() > 0. @@ -370,11 +371,13 @@ where Self::stack_child( &mut initial_phase_available_area, parent_node, + &child_data, &mut initial_phase_area, &mut initial_phase_inner_area, &mut initial_phase_inner_sizes, &child_areas.area, is_last_child, + Phase::Initial, ); if parent_node.cross_alignment.is_not_start() @@ -382,9 +385,24 @@ where { initial_phase_sizes.insert(*child_id, child_areas.area.size); } + + if parent_node.main_alignment.is_flex() { + match parent_node.direction { + DirectionMode::Vertical => { + if let Some(ff) = child_data.height.flex_factor() { + initial_phase_flex_factors.insert(*child_id, ff); + } + } + DirectionMode::Horizontal => { + if let Some(ff) = child_data.width.flex_factor() { + initial_phase_flex_factors.insert(*child_id, ff); + } + } + } + } } - if parent_node.main_alignment.is_not_start() { + if parent_node.main_alignment.is_not_start() && !parent_node.main_alignment.is_flex() { // Adjust the available and inner areas of the Main axis Self::shrink_area_to_fit_when_unbounded( available_area, @@ -445,6 +463,36 @@ where ); } + if parent_node.main_alignment.is_flex() { + let flex_factor = initial_phase_flex_factors.get(&child_id); + + if let Some(flex_factor) = flex_factor { + let flex_factors = initial_phase_flex_factors + .values() + .cloned() + .reduce(|acc, v| acc + v) + .unwrap_or_default(); + + let available_size = + initial_available_area.width() - initial_phase_inner_sizes.width; + + let flex_factor_per = flex_factor.get() / flex_factors.get() * 100.; + + let axis = AlignAxis::new(&parent_node.direction, AlignmentDirection::Main); + + let size = available_size / 100. * flex_factor_per; + + match axis { + AlignAxis::Height => { + adapted_available_area.size.height = size; + } + AlignAxis::Width => { + adapted_available_area.size.width = size; + } + } + } + } + if parent_node.cross_alignment.is_not_start() { let initial_phase_size = initial_phase_sizes.get(&child_id); @@ -480,11 +528,13 @@ where Self::stack_child( available_area, parent_node, + &child_data, area, inner_area, inner_sizes, &child_areas.area, is_last_child, + Phase::Final, ); } @@ -607,11 +657,13 @@ where fn stack_child( available_area: &mut Area, parent_node: &Node, + child_node: &Node, parent_area: &mut Area, inner_area: &mut Area, inner_sizes: &mut Size2D, child_area: &Area, is_last_sibiling: bool, + phase: Phase, ) { // Only apply the spacing to elements after `i > 0` and `i < len - 1` let spacing = (!is_last_sibiling) @@ -625,7 +677,10 @@ where available_area.size.width -= child_area.size.width + spacing.get(); inner_sizes.height = child_area.height().max(inner_sizes.height); - inner_sizes.width += child_area.width() + spacing.get(); + inner_sizes.width += spacing.get(); + if !child_node.width.is_flex() || phase == Phase::Final { + inner_sizes.width += child_area.width(); + } // Keep the biggest height if parent_node.height.inner_sized() { @@ -651,7 +706,10 @@ where available_area.size.height -= child_area.size.height + spacing.get(); inner_sizes.width = child_area.width().max(inner_sizes.width); - inner_sizes.height += child_area.height() + spacing.get(); + inner_sizes.height += spacing.get(); + if !child_node.height.is_flex() || phase == Phase::Final { + inner_sizes.height += child_area.height(); + } // Keep the biggest width if parent_node.width.inner_sized() { diff --git a/crates/torin/src/values/alignment.rs b/crates/torin/src/values/alignment.rs index 6ee467e4e..53bb1b151 100644 --- a/crates/torin/src/values/alignment.rs +++ b/crates/torin/src/values/alignment.rs @@ -7,6 +7,7 @@ pub enum Alignment { SpaceBetween, SpaceEvenly, SpaceAround, + Flex, } impl Alignment { @@ -21,6 +22,10 @@ impl Alignment { ) } + pub fn is_flex(&self) -> bool { + matches!(self, Self::Flex) + } + pub fn pretty(&self) -> String { match self { Alignment::Start => "start".to_string(), @@ -29,6 +34,7 @@ impl Alignment { Alignment::SpaceBetween => "space-between".to_string(), Alignment::SpaceEvenly => "space-evenly".to_string(), Alignment::SpaceAround => "space-around".to_string(), + Alignment::Flex => "flex".to_string(), } } } diff --git a/crates/torin/src/values/size.rs b/crates/torin/src/values/size.rs index 3e05b869b..477c39a54 100644 --- a/crates/torin/src/values/size.rs +++ b/crates/torin/src/values/size.rs @@ -21,6 +21,7 @@ pub enum Size { RootPercentage(Length), InnerPercentage(Length), DynamicCalculations(Box>), + Flex(Length), } impl Default for Size { @@ -30,6 +31,17 @@ impl Default for Size { } impl Size { + pub fn flex_factor(&self) -> Option { + match self { + Self::Flex(f) => Some(*f), + _ => None, + } + } + + pub fn is_flex(&self) -> bool { + matches!(self, Self::Flex(_)) + } + pub fn inner_sized(&self) -> bool { matches!( self, @@ -58,6 +70,7 @@ impl Size { Size::FillMinimum => "fill-min".to_string(), Size::RootPercentage(p) => format!("{}% of root", p.get()), Size::InnerPercentage(p) => format!("{}% of auto", p.get()), + Size::Flex(f) => format!("flex({}", f.get()), } } @@ -76,14 +89,9 @@ impl Size { Some(run_calculations(calculations.deref(), parent_value).unwrap_or(0.0)) } Size::Fill => Some(available_parent_value), - Size::FillMinimum => { - if phase == Phase::Initial { - None - } else { - Some(available_parent_value) - } - } + Size::FillMinimum if phase == Phase::Final => Some(available_parent_value), Size::RootPercentage(per) => Some(root_value / 100.0 * per.get()), + Size::Flex(_) if phase == Phase::Final => Some(available_parent_value), _ => None, } } diff --git a/examples/flex.rs b/examples/flex.rs new file mode 100644 index 000000000..1cab2d63b --- /dev/null +++ b/examples/flex.rs @@ -0,0 +1,53 @@ +#![cfg_attr( + all(not(debug_assertions), target_os = "windows"), + windows_subsystem = "windows" +)] + +use freya::prelude::*; + +fn main() { + launch(app); +} + +fn app() -> Element { + rsx!( + rect { + height: "100%", + width: "100%", + direction: "horizontal", + main_align: "flex", + spacing: "4", + padding: "4", + rect { + height: "100%", + width: "10%", + background: "red", + } + rect { + width: "flex", + height: "100%", + background: "orange", + } + rect { + height: "100%", + width: "25", + background: "black", + } + rect { + width: "flex(3)", + height: "100%", + background: "yellow", + } + rect { + width: "flex", + height: "100%", + background: "green", + } + rect { + height: "100%", + width: "30%", + background: "blue", + } + } + ) +} From 6a83d8e8f0170f92ea07a0894001a621872d9571 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Wed, 25 Sep 2024 22:30:44 +0200 Subject: [PATCH 2/5] move flex to content --- crates/core/src/render/skia_measurer.rs | 1 - .../elements/src/_docs/attributes/content.md | 6 +- .../attributes/main_align_cross_align.md | 1 - crates/state/src/values/alignment.rs | 1 - crates/state/src/values/content.rs | 1 + crates/torin/src/measure.rs | 121 +++++++++++------- crates/torin/src/values/alignment.rs | 6 - crates/torin/src/values/content.rs | 6 + crates/torin/src/values/size.rs | 2 +- examples/flex.rs | 2 +- examples/flex_2.rs | 34 +++++ 11 files changed, 122 insertions(+), 59 deletions(-) create mode 100644 examples/flex_2.rs diff --git a/crates/core/src/render/skia_measurer.rs b/crates/core/src/render/skia_measurer.rs index 0c7faba8e..dd98f41a8 100644 --- a/crates/core/src/render/skia_measurer.rs +++ b/crates/core/src/render/skia_measurer.rs @@ -213,7 +213,6 @@ pub fn align_main_align_paragraph(node: &DioxusNode, area: &Area, paragraph: &Pa Alignment::SpaceBetween => 0., Alignment::SpaceEvenly => 0., Alignment::SpaceAround => 0., - Alignment::Flex => 0., } } diff --git a/crates/elements/src/_docs/attributes/content.md b/crates/elements/src/_docs/attributes/content.md index a26b154ed..d1d723c00 100644 --- a/crates/elements/src/_docs/attributes/content.md +++ b/crates/elements/src/_docs/attributes/content.md @@ -4,10 +4,12 @@ Accepted values: - `normal` (default): Uses parent bounds. - `fit`: Uses parent bounds but later shrunks to the size of the biggest element inside. +- `flex`: Marks the container as flex container, children of this element will be able to use `size`/`size(n)` in their `width` and `height` attributes. -The `fit` mode will allow the inner elements using `width: fill-min` to expand to the biggest element inside this element. -### Example +### `fit` + +The `fit` mode will allow the inner elements using `width: fill-min` to expand to the biggest element inside this element. ```rust, no_run # use freya::prelude::*; diff --git a/crates/elements/src/_docs/attributes/main_align_cross_align.md b/crates/elements/src/_docs/attributes/main_align_cross_align.md index 212a76dfb..d49ef4b75 100644 --- a/crates/elements/src/_docs/attributes/main_align_cross_align.md +++ b/crates/elements/src/_docs/attributes/main_align_cross_align.md @@ -10,7 +10,6 @@ Accepted values for both attributes are: - `space-between`(only for `main_align`): Distributed among the available space - `space-around` (only for `main_align`): Distributed among the available space with small margins in the sides - `space-evenly` (only for `main_align`): Distributed among the available space with the same size of margins in the sides and in between the elements. -- `flex`: TODO When using the `vertical` direction, `main_align` will be the Y axis and `cross_align` will be the X axis. But when using the `horizontal` direction, the `main_align` will be the X axis and the `cross_align` will be the Y axis. diff --git a/crates/state/src/values/alignment.rs b/crates/state/src/values/alignment.rs index c9779b275..93e4d5820 100644 --- a/crates/state/src/values/alignment.rs +++ b/crates/state/src/values/alignment.rs @@ -13,7 +13,6 @@ impl Parse for Alignment { "space-between" => Alignment::SpaceBetween, "space-evenly" => Alignment::SpaceEvenly, "space-around" => Alignment::SpaceAround, - "flex" => Alignment::Flex, _ => Alignment::Start, }) } diff --git a/crates/state/src/values/content.rs b/crates/state/src/values/content.rs index 7cd26f0a1..099599ba5 100644 --- a/crates/state/src/values/content.rs +++ b/crates/state/src/values/content.rs @@ -9,6 +9,7 @@ impl Parse for Content { fn parse(value: &str) -> Result { Ok(match value { "fit" => Content::Fit, + "flex" => Content::Flex, _ => Content::Normal, }) } diff --git a/crates/torin/src/measure.rs b/crates/torin/src/measure.rs index 2d4ea8ab4..141981311 100644 --- a/crates/torin/src/measure.rs +++ b/crates/torin/src/measure.rs @@ -20,6 +20,7 @@ use crate::{ AreaModel, DirectionMode, LayoutMetadata, + Length, Torin, }, }; @@ -296,7 +297,7 @@ where ) { let children = self.dom_adapter.children_of(parent_node_id); - let mut initial_phase_flex_factors = FxHashMap::default(); + let mut initial_phase_flex_grows = FxHashMap::default(); let mut initial_phase_sizes = FxHashMap::default(); let mut initial_phase_inner_sizes = Size2D::default(); @@ -331,16 +332,18 @@ where ) }; - // Initial phase: Measure the size and position of the children if the parent has a - // non-start cross alignment, non-start main aligment of a fit-content. - if parent_node.cross_alignment.is_not_start() + let needs_initial_phase = parent_node.cross_alignment.is_not_start() || parent_node.main_alignment.is_not_start() || parent_node.content.is_fit() - { - let mut initial_phase_area = *area; - let mut initial_phase_inner_area = *inner_area; - let mut initial_phase_available_area = *available_area; + || parent_node.content.is_flex(); + let mut initial_phase_area = *area; + let mut initial_phase_inner_area = *inner_area; + let mut initial_phase_available_area = *available_area; + + // Initial phase: Measure the size and position of the children if the parent has a + // non-start cross alignment, non-start main aligment of a fit-content. + if needs_initial_phase { // Measure the children for child_id in children.iter() { let Some(child_data) = self.dom_adapter.get_node(child_id) else { @@ -386,23 +389,60 @@ where initial_phase_sizes.insert(*child_id, child_areas.area.size); } - if parent_node.main_alignment.is_flex() { + if parent_node.content.is_flex() { match parent_node.direction { DirectionMode::Vertical => { - if let Some(ff) = child_data.height.flex_factor() { - initial_phase_flex_factors.insert(*child_id, ff); + if let Some(ff) = child_data.height.flex_grow() { + initial_phase_flex_grows.insert(*child_id, ff); } } DirectionMode::Horizontal => { - if let Some(ff) = child_data.width.flex_factor() { - initial_phase_flex_factors.insert(*child_id, ff); + if let Some(ff) = child_data.width.flex_grow() { + initial_phase_flex_grows.insert(*child_id, ff); } } } } } + } + + let initial_available_area = *available_area; + + let flex_grows = initial_phase_flex_grows + .values() + .cloned() + .reduce(|acc, v| acc + v) + .unwrap_or_default() + .max(Length::new(1.0)); + + let flex_axis = AlignAxis::new(&parent_node.direction, AlignmentDirection::Main); + + let flex_available_width = initial_available_area.width() - initial_phase_inner_sizes.width; + let flex_available_height = + initial_available_area.height() - initial_phase_inner_sizes.height; - if parent_node.main_alignment.is_not_start() && !parent_node.main_alignment.is_flex() { + let initial_phase_inner_sizes_with_flex = + initial_phase_flex_grows + .values() + .fold(initial_phase_inner_sizes, |mut acc, f| { + let flex_grow_per = f.get() / flex_grows.get() * 100.; + + match flex_axis { + AlignAxis::Height => { + let size = flex_available_height / 100. * flex_grow_per; + acc.height += size; + } + AlignAxis::Width => { + let size = flex_available_width / 100. * flex_grow_per; + acc.width += size; + } + } + + acc + }); + + if needs_initial_phase { + if parent_node.main_alignment.is_not_start() { // Adjust the available and inner areas of the Main axis Self::shrink_area_to_fit_when_unbounded( available_area, @@ -416,7 +456,7 @@ where Self::align_content( available_area, &initial_phase_inner_area, - &initial_phase_inner_sizes, + &initial_phase_inner_sizes_with_flex, &parent_node.main_alignment, &parent_node.direction, AlignmentDirection::Main, @@ -448,6 +488,25 @@ where let mut adapted_available_area = *available_area; + if parent_node.content.is_flex() { + let flex_grow = initial_phase_flex_grows.get(&child_id); + + if let Some(flex_grow) = flex_grow { + let flex_grow_per = flex_grow.get() / flex_grows.get() * 100.; + + match flex_axis { + AlignAxis::Height => { + let size = flex_available_height / 100. * flex_grow_per; + adapted_available_area.size.height = size; + } + AlignAxis::Width => { + let size = flex_available_width / 100. * flex_grow_per; + adapted_available_area.size.width = size; + } + } + } + } + // Only the stacked children will be aligned if parent_node.main_alignment.is_spaced() && !child_data.position.is_absolute() { // Align the Main axis if necessary @@ -455,7 +514,7 @@ where AlignmentDirection::Main, &mut adapted_available_area, &initial_available_area, - &initial_phase_inner_sizes, + &initial_phase_inner_sizes_with_flex, &parent_node.main_alignment, &parent_node.direction, non_absolute_children_len, @@ -463,36 +522,6 @@ where ); } - if parent_node.main_alignment.is_flex() { - let flex_factor = initial_phase_flex_factors.get(&child_id); - - if let Some(flex_factor) = flex_factor { - let flex_factors = initial_phase_flex_factors - .values() - .cloned() - .reduce(|acc, v| acc + v) - .unwrap_or_default(); - - let available_size = - initial_available_area.width() - initial_phase_inner_sizes.width; - - let flex_factor_per = flex_factor.get() / flex_factors.get() * 100.; - - let axis = AlignAxis::new(&parent_node.direction, AlignmentDirection::Main); - - let size = available_size / 100. * flex_factor_per; - - match axis { - AlignAxis::Height => { - adapted_available_area.size.height = size; - } - AlignAxis::Width => { - adapted_available_area.size.width = size; - } - } - } - } - if parent_node.cross_alignment.is_not_start() { let initial_phase_size = initial_phase_sizes.get(&child_id); diff --git a/crates/torin/src/values/alignment.rs b/crates/torin/src/values/alignment.rs index 53bb1b151..6ee467e4e 100644 --- a/crates/torin/src/values/alignment.rs +++ b/crates/torin/src/values/alignment.rs @@ -7,7 +7,6 @@ pub enum Alignment { SpaceBetween, SpaceEvenly, SpaceAround, - Flex, } impl Alignment { @@ -22,10 +21,6 @@ impl Alignment { ) } - pub fn is_flex(&self) -> bool { - matches!(self, Self::Flex) - } - pub fn pretty(&self) -> String { match self { Alignment::Start => "start".to_string(), @@ -34,7 +29,6 @@ impl Alignment { Alignment::SpaceBetween => "space-between".to_string(), Alignment::SpaceEvenly => "space-evenly".to_string(), Alignment::SpaceAround => "space-around".to_string(), - Alignment::Flex => "flex".to_string(), } } } diff --git a/crates/torin/src/values/content.rs b/crates/torin/src/values/content.rs index a6193e8b8..c96b38afb 100644 --- a/crates/torin/src/values/content.rs +++ b/crates/torin/src/values/content.rs @@ -3,12 +3,17 @@ pub enum Content { #[default] Normal, Fit, + Flex, } impl Content { pub fn is_fit(&self) -> bool { self == &Self::Fit } + + pub fn is_flex(&self) -> bool { + self == &Self::Flex + } } impl Content { @@ -16,6 +21,7 @@ impl Content { match self { Self::Normal => "normal".to_owned(), Self::Fit => "fit".to_owned(), + Self::Flex => "flex".to_owned(), } } } diff --git a/crates/torin/src/values/size.rs b/crates/torin/src/values/size.rs index 477c39a54..e3c4a609a 100644 --- a/crates/torin/src/values/size.rs +++ b/crates/torin/src/values/size.rs @@ -31,7 +31,7 @@ impl Default for Size { } impl Size { - pub fn flex_factor(&self) -> Option { + pub fn flex_grow(&self) -> Option { match self { Self::Flex(f) => Some(*f), _ => None, diff --git a/examples/flex.rs b/examples/flex.rs index 1cab2d63b..a091c705b 100644 --- a/examples/flex.rs +++ b/examples/flex.rs @@ -15,7 +15,7 @@ fn app() -> Element { height: "100%", width: "100%", direction: "horizontal", - main_align: "flex", + content: "flex", spacing: "4", padding: "4", rect { diff --git a/examples/flex_2.rs b/examples/flex_2.rs new file mode 100644 index 000000000..d07c3126e --- /dev/null +++ b/examples/flex_2.rs @@ -0,0 +1,34 @@ +#![cfg_attr( + all(not(debug_assertions), target_os = "windows"), + windows_subsystem = "windows" +)] + +use freya::prelude::*; + +fn main() { + launch(app); +} + +fn app() -> Element { + rsx!( + rect { + width: "100%", + height: "fill", + direction: "horizontal", + main_align: "space-around", + content: "flex", + + rect { + width: "flex(0.5)", + height: "fill", + background: "red", + } + + rect { + width: "120", + height: "fill", + background: "orange", + } + } + ) +} From 531d14b0eb73be71f40e673fae87dc24b33f4800 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Tue, 8 Oct 2024 15:16:42 +0200 Subject: [PATCH 3/5] flex tests --- crates/torin/tests/flex.rs | 321 +++++++++++++++++++++++++++++++++++++ 1 file changed, 321 insertions(+) create mode 100644 crates/torin/tests/flex.rs diff --git a/crates/torin/tests/flex.rs b/crates/torin/tests/flex.rs new file mode 100644 index 000000000..d5fcd0902 --- /dev/null +++ b/crates/torin/tests/flex.rs @@ -0,0 +1,321 @@ +use euclid::Length; +use torin::{ + prelude::*, + test_utils::*, +}; + +#[test] +pub fn flex_generic() { + let (mut layout, mut measurer) = test_utils(); + + let mut mocked_dom = TestingDOM::default(); + mocked_dom.add( + 0, + None, + vec![1, 2, 3, 4], + Node::from_size_and_content( + Size::Pixels(Length::new(200.0)), + Size::Pixels(Length::new(200.0)), + Content::Flex, + ), + ); + mocked_dom.add( + 1, + Some(0), + vec![], + Node::from_size_and_direction( + Size::Pixels(Length::new(100.0)), + Size::Percentage(Length::new(10.)), + DirectionMode::Vertical, + ), + ); + mocked_dom.add( + 2, + Some(0), + vec![], + Node::from_size_and_direction( + Size::Pixels(Length::new(100.0)), + Size::Flex(Length::new(1.0)), + DirectionMode::Vertical, + ), + ); + mocked_dom.add( + 3, + Some(0), + vec![], + Node::from_size_and_direction( + Size::Pixels(Length::new(100.0)), + Size::Pixels(Length::new(50.0)), + DirectionMode::Vertical, + ), + ); + mocked_dom.add( + 4, + Some(0), + vec![], + Node::from_size_and_direction( + Size::Pixels(Length::new(100.0)), + Size::Flex(Length::new(3.0)), + DirectionMode::Vertical, + ), + ); + + layout.measure( + 0, + Rect::new(Point2D::new(0.0, 0.0), Size2D::new(1000.0, 1000.0)), + &mut measurer, + &mut mocked_dom, + ); + + assert_eq!( + layout.get(0).unwrap().area, + Rect::new(Point2D::new(0.0, 0.0), Size2D::new(200.0, 200.0)), + ); + + assert_eq!( + layout.get(1).unwrap().area, + Rect::new(Point2D::new(0.0, 0.0), Size2D::new(100.0, 20.0)), + ); + assert_eq!( + layout.get(2).unwrap().area, + Rect::new(Point2D::new(0.0, 20.0), Size2D::new(100.0, 32.5)), + ); + assert_eq!( + layout.get(3).unwrap().area, + Rect::new(Point2D::new(0.0, 52.5), Size2D::new(100.0, 50.0)), + ); + assert_eq!( + layout.get(4).unwrap().area, + Rect::new(Point2D::new(0.0, 102.5), Size2D::new(100.0, 97.5)), + ); +} + +#[test] +pub fn flex_under_1_flex_grow() { + let (mut layout, mut measurer) = test_utils(); + + let mut mocked_dom = TestingDOM::default(); + mocked_dom.add( + 0, + None, + vec![1, 2], + Node::from_size_and_content( + Size::Pixels(Length::new(200.0)), + Size::Pixels(Length::new(200.0)), + Content::Flex, + ), + ); + mocked_dom.add( + 1, + Some(0), + vec![], + Node::from_size_and_direction( + Size::Pixels(Length::new(100.0)), + Size::Flex(Length::new(0.2)), + DirectionMode::Vertical, + ), + ); + mocked_dom.add( + 2, + Some(0), + vec![], + Node::from_size_and_direction( + Size::Pixels(Length::new(100.0)), + Size::Flex(Length::new(0.5)), + DirectionMode::Vertical, + ), + ); + + layout.measure( + 0, + Rect::new(Point2D::new(0.0, 0.0), Size2D::new(1000.0, 1000.0)), + &mut measurer, + &mut mocked_dom, + ); + + assert_eq!( + layout.get(0).unwrap().area, + Rect::new(Point2D::new(0.0, 0.0), Size2D::new(200.0, 200.0)), + ); + + assert_eq!( + layout.get(1).unwrap().area, + Rect::new(Point2D::new(0.0, 0.0), Size2D::new(100.0, 40.0)), + ); + assert_eq!( + layout.get(2).unwrap().area, + Rect::new(Point2D::new(0.0, 40.0), Size2D::new(100.0, 100.0)), + ); +} + +#[test] +pub fn flex_grow_balance() { + let (mut layout, mut measurer) = test_utils(); + + let mut mocked_dom = TestingDOM::default(); + mocked_dom.add( + 0, + None, + vec![1, 2, 3, 4], + Node::from_size_and_content( + Size::Pixels(Length::new(200.0)), + Size::Pixels(Length::new(200.0)), + Content::Flex, + ), + ); + mocked_dom.add( + 1, + Some(0), + vec![], + Node::from_size_and_direction( + Size::Pixels(Length::new(100.0)), + Size::Flex(Length::new(1.0)), + DirectionMode::Vertical, + ), + ); + mocked_dom.add( + 2, + Some(0), + vec![], + Node::from_size_and_direction( + Size::Pixels(Length::new(100.0)), + Size::Flex(Length::new(2.0)), + DirectionMode::Vertical, + ), + ); + mocked_dom.add( + 3, + Some(0), + vec![], + Node::from_size_and_direction( + Size::Pixels(Length::new(100.0)), + Size::Flex(Length::new(3.0)), + DirectionMode::Vertical, + ), + ); + mocked_dom.add( + 4, + Some(0), + vec![], + Node::from_size_and_direction( + Size::Pixels(Length::new(100.0)), + Size::Flex(Length::new(4.0)), + DirectionMode::Vertical, + ), + ); + + layout.measure( + 0, + Rect::new(Point2D::new(0.0, 0.0), Size2D::new(1000.0, 1000.0)), + &mut measurer, + &mut mocked_dom, + ); + + assert_eq!( + layout.get(0).unwrap().area, + Rect::new(Point2D::new(0.0, 0.0), Size2D::new(200.0, 200.0)), + ); + + assert_eq!( + layout.get(1).unwrap().area, + Rect::new(Point2D::new(0.0, 0.0), Size2D::new(100.0, 20.0)), + ); + assert_eq!( + layout.get(2).unwrap().area, + Rect::new(Point2D::new(0.0, 20.0), Size2D::new(100.0, 40.0)), + ); + assert_eq!( + layout.get(3).unwrap().area.round(), + Rect::new(Point2D::new(0.0, 60.0), Size2D::new(100.0, 60.0)), + ); + assert_eq!( + layout.get(4).unwrap().area.round(), + Rect::new(Point2D::new(0.0, 120.0), Size2D::new(100.0, 80.0)), + ); +} + +#[test] +pub fn flex_large_grow_balance() { + let (mut layout, mut measurer) = test_utils(); + + let mut mocked_dom = TestingDOM::default(); + mocked_dom.add( + 0, + None, + vec![1, 2, 3, 4], + Node::from_size_and_content( + Size::Pixels(Length::new(200.0)), + Size::Pixels(Length::new(200.0)), + Content::Flex, + ), + ); + mocked_dom.add( + 1, + Some(0), + vec![], + Node::from_size_and_direction( + Size::Pixels(Length::new(100.0)), + Size::Flex(Length::new(5.0)), + DirectionMode::Vertical, + ), + ); + mocked_dom.add( + 2, + Some(0), + vec![], + Node::from_size_and_direction( + Size::Pixels(Length::new(100.0)), + Size::Flex(Length::new(65.0)), + DirectionMode::Vertical, + ), + ); + mocked_dom.add( + 3, + Some(0), + vec![], + Node::from_size_and_direction( + Size::Pixels(Length::new(100.0)), + Size::Flex(Length::new(30.0)), + DirectionMode::Vertical, + ), + ); + mocked_dom.add( + 4, + Some(0), + vec![], + Node::from_size_and_direction( + Size::Pixels(Length::new(100.0)), + Size::Flex(Length::new(100.0)), + DirectionMode::Vertical, + ), + ); + + layout.measure( + 0, + Rect::new(Point2D::new(0.0, 0.0), Size2D::new(1000.0, 1000.0)), + &mut measurer, + &mut mocked_dom, + ); + + assert_eq!( + layout.get(0).unwrap().area, + Rect::new(Point2D::new(0.0, 0.0), Size2D::new(200.0, 200.0)), + ); + + assert_eq!( + layout.get(1).unwrap().area, + Rect::new(Point2D::new(0.0, 0.0), Size2D::new(100.0, 5.0)), + ); + assert_eq!( + layout.get(2).unwrap().area, + Rect::new(Point2D::new(0.0, 5.0), Size2D::new(100.0, 65.0)), + ); + assert_eq!( + layout.get(3).unwrap().area.round(), + Rect::new(Point2D::new(0.0, 70.0), Size2D::new(100.0, 30.0)), + ); + assert_eq!( + layout.get(4).unwrap().area.round(), + Rect::new(Point2D::new(0.0, 100.0), Size2D::new(100.0, 100.0)), + ); +} From 0b4cc7b716129273d45bf92a2c8c850fcc4401f8 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Fri, 1 Nov 2024 18:37:13 +0100 Subject: [PATCH 4/5] flex docs --- crates/elements/src/_docs/size_unit.rs | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/crates/elements/src/_docs/size_unit.rs b/crates/elements/src/_docs/size_unit.rs index 15a341bdb..fad34147b 100644 --- a/crates/elements/src/_docs/size_unit.rs +++ b/crates/elements/src/_docs/size_unit.rs @@ -102,3 +102,30 @@ //! ) //! } //! ``` +//! +//! #### Flex Factor +//! +//! When being a children of an element with `content: flex` you may change the growth factor of the size attributes. +//! +//! ```rust, no_run +//! # use freya::prelude::*; +//! fn app() -> Element { +//! rsx!( +//! rect { +//! content: "flex", +//! width: "200", +//! height: "200", +//! rect { +//! height: "flex(1)", +//! width: "100%", +//! background: "red" +//! } +//! rect { +//! height: "flex(3)", +//! width: "100%", +//! background: "blue" +//! } +//! } +//! ) +//! } +//! ``` From 3dd519d3a6978ba3a967a6e4d4c0b66edf9932bd Mon Sep 17 00:00:00 2001 From: marc2332 Date: Fri, 1 Nov 2024 19:37:14 +0100 Subject: [PATCH 5/5] fmt --- crates/elements/src/_docs/size_unit.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/elements/src/_docs/size_unit.rs b/crates/elements/src/_docs/size_unit.rs index fad34147b..ac2de49a9 100644 --- a/crates/elements/src/_docs/size_unit.rs +++ b/crates/elements/src/_docs/size_unit.rs @@ -102,11 +102,11 @@ //! ) //! } //! ``` -//! +//! //! #### Flex Factor //! //! When being a children of an element with `content: flex` you may change the growth factor of the size attributes. -//! +//! //! ```rust, no_run //! # use freya::prelude::*; //! fn app() -> Element {