From f5c638b5850c2864ba7230f1c2c6c2b9488a4eaf Mon Sep 17 00:00:00 2001 From: Marc Espin Date: Sun, 4 Aug 2024 16:24:41 +0200 Subject: [PATCH] feat: Support percentage of auto in layout (#784) --- crates/components/src/accordion.rs | 25 ++++++------- crates/state/src/values/size.rs | 7 ++++ crates/torin/src/geometry.rs | 23 ++++++++++-- crates/torin/src/measure.rs | 5 ++- crates/torin/src/torin.rs | 5 ++- crates/torin/src/values/size.rs | 13 ++++++- crates/torin/tests/size.rs | 59 ++++++++++++++++++++++++++++++ 7 files changed, 116 insertions(+), 21 deletions(-) diff --git a/crates/components/src/accordion.rs b/crates/components/src/accordion.rs index 9a7bdccdd..362b537ad 100644 --- a/crates/components/src/accordion.rs +++ b/crates/components/src/accordion.rs @@ -4,13 +4,14 @@ use freya_elements::{ events::MouseEvent, }; use freya_hooks::{ - use_animation_with_dependencies, + use_animation, use_applied_theme, - use_node, use_platform, AccordionTheme, AccordionThemeWith, AnimNum, + Ease, + Function, }; use winit::window::CursorIcon; @@ -43,10 +44,13 @@ pub struct AccordionProps { pub fn Accordion(props: AccordionProps) -> Element { let theme = use_applied_theme!(&props.theme, accordion); let mut open = use_signal(|| false); - let (node_ref, size) = use_node(); - - let animation = use_animation_with_dependencies(&size.area.height(), move |ctx, height| { - ctx.with(AnimNum::new(0., height).time(200)) + let animation = use_animation(move |ctx| { + ctx.with( + AnimNum::new(0., 100.) + .time(300) + .function(Function::Expo) + .ease(Ease::Out), + ) }); let mut status = use_signal(AccordionStatus::default); let platform = use_platform(); @@ -101,13 +105,8 @@ pub fn Accordion(props: AccordionProps) -> Element { rect { overflow: "clip", width: "100%", - height: "{animation_value}", - rect { - reference: node_ref, - height: "auto", - width: "100%", - {&props.children} - } + height: "{animation_value}a", + {&props.children} } } ) diff --git a/crates/state/src/values/size.rs b/crates/state/src/values/size.rs index 40604359d..c92e7bdcf 100644 --- a/crates/state/src/values/size.rs +++ b/crates/state/src/values/size.rs @@ -35,6 +35,13 @@ impl Parse for Size { .parse::() .map_err(|_| ParseError)?, ))) + } else if value.contains('a') { + Ok(Size::InnerPercentage(Length::new( + value + .replace('a', "") + .parse::() + .map_err(|_| ParseError)?, + ))) } else { Ok(Size::Pixels(Length::new( value.parse::().map_err(|_| ParseError)?, diff --git a/crates/torin/src/geometry.rs b/crates/torin/src/geometry.rs index 88033dd25..5767f9f95 100644 --- a/crates/torin/src/geometry.rs +++ b/crates/torin/src/geometry.rs @@ -1,7 +1,11 @@ -use crate::prelude::{ - Alignment, - DirectionMode, - Gaps, +use crate::{ + node::Node, + prelude::{ + Alignment, + DirectionMode, + Gaps, + Size, + }, }; #[derive(PartialEq)] @@ -42,6 +46,8 @@ pub trait AreaModel { siblings_len: usize, child_position: usize, ); + + fn adjust_size(&mut self, node: &Node); } impl AreaModel for Area { @@ -162,6 +168,15 @@ impl AreaModel for Area { }, } } + + fn adjust_size(&mut self, node: &Node) { + if let Size::InnerPercentage(p) = node.width { + self.size.width *= p.get() / 100.; + } + if let Size::InnerPercentage(p) = node.height { + self.size.height *= p.get() / 100.; + } + } } pub fn get_align_axis( diff --git a/crates/torin/src/measure.rs b/crates/torin/src/measure.rs index d5e173311..b667b8273 100644 --- a/crates/torin/src/measure.rs +++ b/crates/torin/src/measure.rs @@ -410,7 +410,7 @@ pub fn measure_inner_nodes( let inner_area = *mode.inner_area(); // Final measurement - let (child_revalidated, child_areas) = measure_node( + let (child_revalidated, mut child_areas) = measure_node( child_id, &child_data, layout, @@ -424,6 +424,9 @@ pub fn measure_inner_nodes( Phase::Final, ); + // Adjust the size of the area if needed + child_areas.area.adjust_size(&child_data); + // Stack the child into its parent mode.stack_into_node( parent_node, diff --git a/crates/torin/src/torin.rs b/crates/torin/src/torin.rs index c5398ff4c..accb34be3 100644 --- a/crates/torin/src/torin.rs +++ b/crates/torin/src/torin.rs @@ -273,7 +273,7 @@ impl Torin { available_area.move_with_offsets(&root_parent.offset_x, &root_parent.offset_y); } - let (root_revalidated, root_layout_node) = measure_node( + let (root_revalidated, mut root_layout_node) = measure_node( root_id, &root, self, @@ -287,6 +287,9 @@ impl Torin { Phase::Final, ); + // Adjust the size of the area if needed + root_layout_node.area.adjust_size(&root); + // Cache the root Node results if it was modified if root_revalidated { if let Some(measurer) = measurer { diff --git a/crates/torin/src/values/size.rs b/crates/torin/src/values/size.rs index 6ed78cb42..38c7ee46c 100644 --- a/crates/torin/src/values/size.rs +++ b/crates/torin/src/values/size.rs @@ -16,6 +16,7 @@ pub enum Size { Percentage(Length), Pixels(Length), RootPercentage(Length), + InnerPercentage(Length), DynamicCalculations(Box>), } @@ -27,7 +28,14 @@ impl Default for Size { impl Size { pub fn inner_sized(&self) -> bool { - matches!(self, Self::Inner | Self::FillMinimum) + matches!( + self, + Self::Inner | Self::FillMinimum | Self::InnerPercentage(_) + ) + } + + pub fn inner_percentage_sized(&self) -> bool { + matches!(self, Self::InnerPercentage(_)) } pub fn pretty(&self) -> String { @@ -46,6 +54,7 @@ impl Size { Size::Fill => "fill".to_string(), Size::FillMinimum => "fill-min".to_string(), Size::RootPercentage(p) => format!("{}% of root", p.get()), + Size::InnerPercentage(p) => format!("{}% of auto", p.get()), } } @@ -135,7 +144,7 @@ impl Size { pub fn most_fitting_size<'a>(&self, size: &'a f32, available_size: &'a f32) -> &'a f32 { match self { - Self::Inner => available_size, + Self::Inner | Self::InnerPercentage(_) => available_size, _ => size, } } diff --git a/crates/torin/tests/size.rs b/crates/torin/tests/size.rs index 9289339d2..1ec0bfd14 100644 --- a/crates/torin/tests/size.rs +++ b/crates/torin/tests/size.rs @@ -718,3 +718,62 @@ pub fn content_fit_fill_min() { Rect::new(Point2D::new(0.0, 600.0), Size2D::new(100.0, 300.0)), ); } + +#[test] +pub fn inner_percentage() { + 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_direction( + Size::Inner, + Size::InnerPercentage(Length::new(50.0)), + DirectionMode::Vertical, + ), + ); + mocked_dom.add( + 1, + Some(0), + vec![], + Node::from_size_and_direction( + Size::Inner, + Size::Percentage(Length::new(30.0)), + DirectionMode::Vertical, + ), + ); + mocked_dom.add( + 2, + Some(0), + vec![], + Node::from_size_and_direction( + Size::Pixels(Length::new(100.0)), + Size::Pixels(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().visible_area(), + Rect::new(Point2D::new(0.0, 0.0), Size2D::new(100.0, 200.0)), + ); + + assert_eq!( + layout.get(1).unwrap().visible_area(), + Rect::new(Point2D::new(0.0, 0.0), Size2D::new(0.0, 300.0)), + ); + + assert_eq!( + layout.get(2).unwrap().visible_area(), + Rect::new(Point2D::new(0.0, 300.0), Size2D::new(100.0, 100.0)), + ); +}