diff --git a/examples/custom_tree_owned_partial.rs b/examples/custom_tree_owned_partial.rs index 79f693296..f9fb37897 100644 --- a/examples/custom_tree_owned_partial.rs +++ b/examples/custom_tree_owned_partial.rs @@ -126,7 +126,12 @@ impl taffy::TraversePartialTree for Node { } impl taffy::LayoutPartialTree for Node { - fn get_style(&self, node_id: NodeId) -> &Style { + type CoreContainerStyle<'a> = &'a Style where + Self: 'a; + + type CacheMut<'b> = &'b mut Cache where Self: 'b; + + fn get_core_container_style(&self, node_id: NodeId) -> Self::CoreContainerStyle<'_> { &self.node_from_id(node_id).style } @@ -162,6 +167,38 @@ impl taffy::LayoutPartialTree for Node { } } +impl taffy::LayoutFlexboxContainer for Node { + type FlexboxContainerStyle<'a> = &'a Style where + Self: 'a; + + type FlexboxItemStyle<'a> = &'a Style where + Self: 'a; + + fn get_flexbox_container_style(&self, node_id: NodeId) -> Self::FlexboxContainerStyle<'_> { + &self.node_from_id(node_id).style + } + + fn get_flexbox_child_style(&self, child_node_id: NodeId) -> Self::FlexboxItemStyle<'_> { + &self.node_from_id(child_node_id).style + } +} + +impl taffy::LayoutGridContainer for Node { + type GridContainerStyle<'a> = &'a Style where + Self: 'a; + + type GridItemStyle<'a> = &'a Style where + Self: 'a; + + fn get_grid_container_style(&self, node_id: NodeId) -> Self::GridContainerStyle<'_> { + &self.node_from_id(node_id).style + } + + fn get_grid_child_style(&self, child_node_id: NodeId) -> Self::GridItemStyle<'_> { + &self.node_from_id(child_node_id).style + } +} + fn main() -> Result<(), taffy::TaffyError> { let mut root = Node::new_column(Style::DEFAULT); diff --git a/examples/custom_tree_owned_unsafe.rs b/examples/custom_tree_owned_unsafe.rs index 96f31527f..c6bf0d24e 100644 --- a/examples/custom_tree_owned_unsafe.rs +++ b/examples/custom_tree_owned_unsafe.rs @@ -130,7 +130,12 @@ impl TraversePartialTree for StatelessLayoutTree { impl TraverseTree for StatelessLayoutTree {} impl LayoutPartialTree for StatelessLayoutTree { - fn get_style(&self, node_id: NodeId) -> &Style { + type CoreContainerStyle<'a> = &'a Style where + Self: 'a; + + type CacheMut<'b> = &'b mut Cache where Self: 'b; + + fn get_core_container_style(&self, node_id: NodeId) -> Self::CoreContainerStyle<'_> { unsafe { &node_from_id(node_id).style } } @@ -166,6 +171,38 @@ impl LayoutPartialTree for StatelessLayoutTree { } } +impl taffy::LayoutFlexboxContainer for StatelessLayoutTree { + type FlexboxContainerStyle<'a> = &'a Style where + Self: 'a; + + type FlexboxItemStyle<'a> = &'a Style where + Self: 'a; + + fn get_flexbox_container_style(&self, node_id: NodeId) -> Self::FlexboxContainerStyle<'_> { + unsafe { &node_from_id(node_id).style } + } + + fn get_flexbox_child_style(&self, child_node_id: NodeId) -> Self::FlexboxItemStyle<'_> { + unsafe { &node_from_id(child_node_id).style } + } +} + +impl taffy::LayoutGridContainer for StatelessLayoutTree { + type GridContainerStyle<'a> = &'a Style where + Self: 'a; + + type GridItemStyle<'a> = &'a Style where + Self: 'a; + + fn get_grid_container_style(&self, node_id: NodeId) -> Self::GridContainerStyle<'_> { + unsafe { &node_from_id(node_id).style } + } + + fn get_grid_child_style(&self, child_node_id: NodeId) -> Self::GridItemStyle<'_> { + unsafe { &node_from_id(child_node_id).style } + } +} + impl RoundTree for StatelessLayoutTree { fn get_unrounded_layout(&self, node_id: NodeId) -> &Layout { unsafe { &node_from_id_mut(node_id).unrounded_layout } diff --git a/examples/custom_tree_vec.rs b/examples/custom_tree_vec.rs index f3e86568a..98ef83f7c 100644 --- a/examples/custom_tree_vec.rs +++ b/examples/custom_tree_vec.rs @@ -139,7 +139,12 @@ impl taffy::TraversePartialTree for Tree { impl taffy::TraverseTree for Tree {} impl taffy::LayoutPartialTree for Tree { - fn get_style(&self, node_id: NodeId) -> &Style { + type CoreContainerStyle<'a> = &'a Style where + Self: 'a; + + type CacheMut<'b> = &'b mut Cache where Self: 'b; + + fn get_core_container_style(&self, node_id: NodeId) -> Self::CoreContainerStyle<'_> { &self.node_from_id(node_id).style } @@ -175,6 +180,38 @@ impl taffy::LayoutPartialTree for Tree { } } +impl taffy::LayoutFlexboxContainer for Tree { + type FlexboxContainerStyle<'a> = &'a Style where + Self: 'a; + + type FlexboxItemStyle<'a> = &'a Style where + Self: 'a; + + fn get_flexbox_container_style(&self, node_id: NodeId) -> Self::FlexboxContainerStyle<'_> { + &self.node_from_id(node_id).style + } + + fn get_flexbox_child_style(&self, child_node_id: NodeId) -> Self::FlexboxItemStyle<'_> { + &self.node_from_id(child_node_id).style + } +} + +impl taffy::LayoutGridContainer for Tree { + type GridContainerStyle<'a> = &'a Style where + Self: 'a; + + type GridItemStyle<'a> = &'a Style where + Self: 'a; + + fn get_grid_container_style(&self, node_id: NodeId) -> Self::GridContainerStyle<'_> { + &self.node_from_id(node_id).style + } + + fn get_grid_child_style(&self, child_node_id: NodeId) -> Self::GridItemStyle<'_> { + &self.node_from_id(child_node_id).style + } +} + impl taffy::RoundTree for Tree { fn get_unrounded_layout(&self, node_id: NodeId) -> &Layout { &self.node_from_id(node_id).unrounded_layout diff --git a/src/compute/block.rs b/src/compute/block.rs index 8070dab04..7e0bc1aa5 100644 --- a/src/compute/block.rs +++ b/src/compute/block.rs @@ -1,6 +1,6 @@ //! Computes the CSS block layout algorithm in the case that the block container being laid out contains only block-level boxes use crate::geometry::{Line, Point, Rect, Size}; -use crate::style::{AvailableSpace, Display, LengthPercentageAuto, Overflow, Position}; +use crate::style::{AvailableSpace, CoreStyle, LengthPercentageAuto, Overflow, Position}; use crate::style_helpers::TaffyMaxContent; use crate::tree::{CollapsibleMarginSet, Layout, LayoutInput, LayoutOutput, RunMode, SizingMode}; use crate::tree::{LayoutPartialTree, LayoutPartialTreeExt, NodeId}; @@ -9,7 +9,7 @@ use crate::util::sys::f32_max; use crate::util::sys::Vec; use crate::util::MaybeMath; use crate::util::{MaybeResolve, ResolveOrZero}; -use crate::BoxSizing; +use crate::{BoxGenerationMode, BoxSizing, LayoutBlockContainer}; #[cfg(feature = "content_size")] use super::common::content_size::compute_content_size_contribution; @@ -58,31 +58,35 @@ struct BlockItem { } /// Computes the layout of [`LayoutPartialTree`] according to the block layout algorithm -pub fn compute_block_layout(tree: &mut impl LayoutPartialTree, node_id: NodeId, inputs: LayoutInput) -> LayoutOutput { +pub fn compute_block_layout( + tree: &mut impl LayoutBlockContainer, + node_id: NodeId, + inputs: LayoutInput, +) -> LayoutOutput { let LayoutInput { known_dimensions, parent_size, run_mode, .. } = inputs; - let style = tree.get_style(node_id); + let style = tree.get_block_container_style(node_id); // Pull these out earlier to avoid borrowing issues - let aspect_ratio = style.aspect_ratio; - let padding = style.padding.resolve_or_zero(parent_size.width); - let border = style.border.resolve_or_zero(parent_size.width); + let aspect_ratio = style.aspect_ratio(); + let padding = style.padding().resolve_or_zero(parent_size.width); + let border = style.border().resolve_or_zero(parent_size.width); let padding_border_size = (padding + border).sum_axes(); let box_sizing_adjustment = - if style.box_sizing == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO }; + if style.box_sizing() == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO }; let min_size = style - .min_size + .min_size() .maybe_resolve(parent_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment); let max_size = style - .max_size + .max_size() .maybe_resolve(parent_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment); let clamped_style_size = if inputs.sizing_mode == SizingMode::InherentSize { style - .size + .size() .maybe_resolve(parent_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment) @@ -91,6 +95,8 @@ pub fn compute_block_layout(tree: &mut impl LayoutPartialTree, node_id: NodeId, Size::NONE }; + drop(style); + // If both min and max in a given axis are set and max <= min then this determines the size in that axis let min_max_definite_size = min_size.zip_map(max_size, |min, max| match (min, max) { (Some(min), Some(max)) if max <= min => Some(min), @@ -112,26 +118,26 @@ pub fn compute_block_layout(tree: &mut impl LayoutPartialTree, node_id: NodeId, compute_inner(tree, node_id, LayoutInput { known_dimensions: styled_based_known_dimensions, ..inputs }) } -/// Computes the layout of [`LayoutPartialTree`] according to the block layout algorithm -fn compute_inner(tree: &mut impl LayoutPartialTree, node_id: NodeId, inputs: LayoutInput) -> LayoutOutput { +/// Computes the layout of [`LayoutBlockContainer`] according to the block layout algorithm +fn compute_inner(tree: &mut impl LayoutBlockContainer, node_id: NodeId, inputs: LayoutInput) -> LayoutOutput { let LayoutInput { known_dimensions, parent_size, available_space, run_mode, vertical_margins_are_collapsible, .. } = inputs; - let style = tree.get_style(node_id); - let raw_padding = style.padding; - let raw_border = style.border; - let raw_margin = style.margin; - let aspect_ratio = style.aspect_ratio; - let padding = style.padding.resolve_or_zero(parent_size.width); - let border = style.border.resolve_or_zero(parent_size.width); + let style = tree.get_block_container_style(node_id); + let raw_padding = style.padding(); + let raw_border = style.border(); + let raw_margin = style.margin(); + let aspect_ratio = style.aspect_ratio(); + let padding = raw_padding.resolve_or_zero(parent_size.width); + let border = raw_border.resolve_or_zero(parent_size.width); // Scrollbar gutters are reserved when the `overflow` property is set to `Overflow::Scroll`. // However, the axis are switched (transposed) because a node that scrolls vertically needs // *horizontal* space to be reserved for a scrollbar let scrollbar_gutter = { - let offsets = style.overflow.transpose().map(|overflow| match overflow { - Overflow::Scroll => style.scrollbar_width, + let offsets = style.overflow().transpose().map(|overflow| match overflow { + Overflow::Scroll => style.scrollbar_width(), _ => 0.0, }); // TODO: make side configurable based on the `direction` property @@ -143,16 +149,16 @@ fn compute_inner(tree: &mut impl LayoutPartialTree, node_id: NodeId, inputs: Lay let container_content_box_size = known_dimensions.maybe_sub(content_box_inset.sum_axes()); let box_sizing_adjustment = - if style.box_sizing == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO }; + if style.box_sizing() == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO }; let size = - style.size.maybe_resolve(parent_size).maybe_apply_aspect_ratio(aspect_ratio).maybe_add(box_sizing_adjustment); + style.size().maybe_resolve(parent_size).maybe_apply_aspect_ratio(aspect_ratio).maybe_add(box_sizing_adjustment); let min_size = style - .min_size + .min_size() .maybe_resolve(parent_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment); let max_size = style - .max_size + .max_size() .maybe_resolve(parent_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment); @@ -160,28 +166,30 @@ fn compute_inner(tree: &mut impl LayoutPartialTree, node_id: NodeId, inputs: Lay // Determine margin collapsing behaviour let own_margins_collapse_with_children = Line { start: vertical_margins_are_collapsible.start - && !style.overflow.x.is_scroll_container() - && !style.overflow.y.is_scroll_container() - && style.position == Position::Relative + && !style.overflow().x.is_scroll_container() + && !style.overflow().y.is_scroll_container() + && style.position() == Position::Relative && padding.top == 0.0 && border.top == 0.0, end: vertical_margins_are_collapsible.end - && !style.overflow.x.is_scroll_container() - && !style.overflow.y.is_scroll_container() - && style.position == Position::Relative + && !style.overflow().x.is_scroll_container() + && !style.overflow().y.is_scroll_container() + && style.position() == Position::Relative && padding.bottom == 0.0 && border.bottom == 0.0 && size.height.is_none(), }; - let has_styles_preventing_being_collapsed_through = style.display != Display::Block - || style.overflow.x.is_scroll_container() - || style.overflow.y.is_scroll_container() - || style.position == Position::Absolute + let has_styles_preventing_being_collapsed_through = !style.is_block() + || style.overflow().x.is_scroll_container() + || style.overflow().y.is_scroll_container() + || style.position() == Position::Absolute || padding.top > 0.0 || padding.bottom > 0.0 || border.top > 0.0 || border.bottom > 0.0; + drop(style); + // 1. Generate items let mut items = generate_item_list(tree, node_id, container_content_box_size); @@ -233,7 +241,7 @@ fn compute_inner(tree: &mut impl LayoutPartialTree, node_id: NodeId, inputs: Lay let len = tree.child_count(node_id); for order in 0..len { let child = tree.get_child_id(node_id, order); - if tree.get_style(child).display == Display::None { + if tree.get_block_child_style(child).box_generation_mode() == BoxGenerationMode::None { tree.set_unrounded_layout(child, &Layout::with_order(order as u32)); tree.perform_child_layout( child, @@ -279,48 +287,47 @@ fn compute_inner(tree: &mut impl LayoutPartialTree, node_id: NodeId, inputs: Lay /// Create a `Vec` of `BlockItem` structs where each item in the `Vec` represents a child of the current node #[inline] fn generate_item_list( - tree: &impl LayoutPartialTree, + tree: &impl LayoutBlockContainer, node: NodeId, node_inner_size: Size>, ) -> Vec { tree.child_ids(node) - .map(|child_node_id| (child_node_id, tree.get_style(child_node_id))) - .filter(|(_, style)| style.display != Display::None) + .map(|child_node_id| (child_node_id, tree.get_block_child_style(child_node_id))) + .filter(|(_, style)| style.box_generation_mode() != BoxGenerationMode::None) .enumerate() .map(|(order, (child_node_id, child_style))| { - let aspect_ratio = child_style.aspect_ratio; - let padding = child_style.padding.resolve_or_zero(node_inner_size); - let border = child_style.border.resolve_or_zero(node_inner_size); + let aspect_ratio = child_style.aspect_ratio(); + let padding = child_style.padding().resolve_or_zero(node_inner_size); + let border = child_style.border().resolve_or_zero(node_inner_size); let pb_sum = (padding + border).sum_axes(); let box_sizing_adjustment = - if child_style.box_sizing == BoxSizing::ContentBox { pb_sum } else { Size::ZERO }; + if child_style.box_sizing() == BoxSizing::ContentBox { pb_sum } else { Size::ZERO }; BlockItem { node_id: child_node_id, order: order as u32, - size: child_style - .size + .size() .maybe_resolve(node_inner_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment), min_size: child_style - .min_size + .min_size() .maybe_resolve(node_inner_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment), max_size: child_style - .max_size + .max_size() .maybe_resolve(node_inner_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment), - overflow: child_style.overflow, - scrollbar_width: child_style.scrollbar_width, - position: child_style.position, - inset: child_style.inset, - margin: child_style.margin, + overflow: child_style.overflow(), + scrollbar_width: child_style.scrollbar_width(), + position: child_style.position(), + inset: child_style.inset(), + margin: child_style.margin(), padding, border, - padding_border_sum: (padding + border).sum_axes(), + padding_border_sum: pb_sum, // Fields to be computed later (for now we initialise with dummy values) computed_size: Size::zero(), @@ -531,7 +538,7 @@ fn perform_final_layout_on_in_flow_children( /// Perform absolute layout on all absolutely positioned children. #[inline] fn perform_absolute_layout_on_absolute_children( - tree: &mut impl LayoutPartialTree, + tree: &mut impl LayoutBlockContainer, items: &[BlockItem], area_size: Size, area_offset: Point, @@ -543,47 +550,50 @@ fn perform_absolute_layout_on_absolute_children( let mut absolute_content_size = Size::ZERO; for item in items.iter().filter(|item| item.position == Position::Absolute) { - let child_style = tree.get_style(item.node_id); + let child_style = tree.get_block_child_style(item.node_id); // Skip items that are display:none or are not position:absolute - if child_style.display == Display::None || child_style.position != Position::Absolute { + if child_style.box_generation_mode() == BoxGenerationMode::None || child_style.position() != Position::Absolute + { continue; } - let aspect_ratio = child_style.aspect_ratio; - let margin = child_style.margin.map(|margin| margin.resolve_to_option(area_width)); - let padding = child_style.padding.resolve_or_zero(Some(area_width)); - let border = child_style.border.resolve_or_zero(Some(area_width)); + let aspect_ratio = child_style.aspect_ratio(); + let margin = child_style.margin().map(|margin| margin.resolve_to_option(area_width)); + let padding = child_style.padding().resolve_or_zero(Some(area_width)); + let border = child_style.border().resolve_or_zero(Some(area_width)); let padding_border_sum = (padding + border).sum_axes(); let box_sizing_adjustment = - if child_style.box_sizing == BoxSizing::ContentBox { padding_border_sum } else { Size::ZERO }; + if child_style.box_sizing() == BoxSizing::ContentBox { padding_border_sum } else { Size::ZERO }; // Resolve inset - let left = child_style.inset.left.maybe_resolve(area_width); - let right = child_style.inset.right.maybe_resolve(area_width); - let top = child_style.inset.top.maybe_resolve(area_height); - let bottom = child_style.inset.bottom.maybe_resolve(area_height); + let left = child_style.inset().left.maybe_resolve(area_width); + let right = child_style.inset().right.maybe_resolve(area_width); + let top = child_style.inset().top.maybe_resolve(area_height); + let bottom = child_style.inset().bottom.maybe_resolve(area_height); // Compute known dimensions from min/max/inherent size styles let style_size = child_style - .size + .size() .maybe_resolve(area_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment); let min_size = child_style - .min_size + .min_size() .maybe_resolve(area_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment) .or(padding_border_sum.map(Some)) .maybe_max(padding_border_sum); let max_size = child_style - .max_size + .max_size() .maybe_resolve(area_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment); let mut known_dimensions = style_size.maybe_clamp(min_size, max_size); + drop(child_style); + // Fill in width from left/right and reapply aspect ratio if: // - Width is not already known // - Item has both left and right inset properties set diff --git a/src/compute/flexbox.rs b/src/compute/flexbox.rs index c814711f9..358c76bf7 100644 --- a/src/compute/flexbox.rs +++ b/src/compute/flexbox.rs @@ -2,18 +2,18 @@ use crate::compute::common::alignment::compute_alignment_offset; use crate::geometry::{Line, Point, Rect, Size}; use crate::style::{ - AlignContent, AlignItems, AlignSelf, AvailableSpace, Dimension, Display, FlexWrap, JustifyContent, - LengthPercentageAuto, Overflow, Position, + AlignContent, AlignItems, AlignSelf, AvailableSpace, Dimension, FlexWrap, JustifyContent, LengthPercentageAuto, + Overflow, Position, }; -use crate::style::{FlexDirection, Style}; +use crate::style::{CoreStyle, FlexDirection, FlexboxContainerStyle, FlexboxItemStyle}; use crate::style_helpers::{TaffyMaxContent, TaffyMinContent}; use crate::tree::{Layout, LayoutInput, LayoutOutput, RunMode, SizingMode}; -use crate::tree::{LayoutPartialTree, LayoutPartialTreeExt, NodeId}; +use crate::tree::{LayoutFlexboxContainer, LayoutPartialTreeExt, NodeId}; use crate::util::debug::debug_log; use crate::util::sys::{f32_max, new_vec_with_capacity, Vec}; use crate::util::MaybeMath; use crate::util::{MaybeResolve, ResolveOrZero}; -use crate::BoxSizing; +use crate::{BoxGenerationMode, BoxSizing}; use super::common::alignment::apply_alignment_fallback; #[cfg(feature = "content_size")] @@ -154,30 +154,35 @@ struct AlgoConstants { } /// Computes the layout of [`LayoutPartialTree`] according to the flexbox algorithm -pub fn compute_flexbox_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inputs: LayoutInput) -> LayoutOutput { +pub fn compute_flexbox_layout( + tree: &mut impl LayoutFlexboxContainer, + node: NodeId, + inputs: LayoutInput, +) -> LayoutOutput { let LayoutInput { known_dimensions, parent_size, run_mode, .. } = inputs; - let style = tree.get_style(node); + let style = tree.get_flexbox_container_style(node); // Pull these out earlier to avoid borrowing issues - let aspect_ratio = style.aspect_ratio; - let padding = style.padding.resolve_or_zero(parent_size.width); - let border = style.border.resolve_or_zero(parent_size.width); + let aspect_ratio = style.aspect_ratio(); + let padding = style.padding().resolve_or_zero(parent_size.width); + let border = style.border().resolve_or_zero(parent_size.width); let padding_border_sum = padding.sum_axes() + border.sum_axes(); - let box_sizing_adjustment = if style.box_sizing == BoxSizing::ContentBox { padding_border_sum } else { Size::ZERO }; + let box_sizing_adjustment = + if style.box_sizing() == BoxSizing::ContentBox { padding_border_sum } else { Size::ZERO }; let min_size = style - .min_size + .min_size() .maybe_resolve(parent_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment); let max_size = style - .max_size + .max_size() .maybe_resolve(parent_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment); let clamped_style_size = if inputs.sizing_mode == SizingMode::InherentSize { style - .size + .size() .maybe_resolve(parent_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment) @@ -204,16 +209,18 @@ pub fn compute_flexbox_layout(tree: &mut impl LayoutPartialTree, node: NodeId, i } } - debug_log!("FLEX:", dbg:style.flex_direction); + debug_log!("FLEX:", dbg:style.flex_direction()); + drop(style); + compute_preliminary(tree, node, LayoutInput { known_dimensions: styled_based_known_dimensions, ..inputs }) } /// Compute a preliminary size for an item -fn compute_preliminary(tree: &mut impl LayoutPartialTree, node: NodeId, inputs: LayoutInput) -> LayoutOutput { +fn compute_preliminary(tree: &mut impl LayoutFlexboxContainer, node: NodeId, inputs: LayoutInput) -> LayoutOutput { let LayoutInput { known_dimensions, parent_size, available_space, run_mode, .. } = inputs; // Define some general constants we will need for the remainder of the algorithm. - let mut constants = compute_constants(tree.get_style(node), known_dimensions, parent_size); + let mut constants = compute_constants(tree.get_flexbox_container_style(node), known_dimensions, parent_size); // 9. Flex Layout Algorithm @@ -269,9 +276,9 @@ fn compute_preliminary(tree: &mut impl LayoutPartialTree, node: NodeId, inputs: debug_log!("constants.node_inner_size", dbg:constants.node_inner_size); // Re-resolve percentage gaps - let style = tree.get_style(node); + let style = tree.get_flexbox_container_style(node); let inner_container_size = constants.inner_container_size.main(constants.dir); - let new_gap = style.gap.main(constants.dir).maybe_resolve(inner_container_size).unwrap_or(0.0); + let new_gap = style.gap().main(constants.dir).maybe_resolve(inner_container_size).unwrap_or(0.0); constants.gap.set_main(constants.dir, new_gap); } @@ -359,7 +366,7 @@ fn compute_preliminary(tree: &mut impl LayoutPartialTree, node: NodeId, inputs: let len = tree.child_count(node); for order in 0..len { let child = tree.get_child_id(node, order); - if tree.get_style(child).display == Display::None { + if tree.get_flexbox_child_style(child).box_generation_mode() == BoxGenerationMode::None { tree.set_unrounded_layout(child, &Layout::with_order(order as u32)); tree.perform_child_layout( child, @@ -398,32 +405,33 @@ fn compute_preliminary(tree: &mut impl LayoutPartialTree, node: NodeId, inputs: /// Compute constants that can be reused during the flexbox algorithm. #[inline] fn compute_constants( - style: &Style, + style: impl FlexboxContainerStyle, known_dimensions: Size>, parent_size: Size>, ) -> AlgoConstants { - let dir = style.flex_direction; + let dir = style.flex_direction(); let is_row = dir.is_row(); let is_column = dir.is_column(); - let is_wrap = matches!(style.flex_wrap, FlexWrap::Wrap | FlexWrap::WrapReverse); - let is_wrap_reverse = style.flex_wrap == FlexWrap::WrapReverse; + let is_wrap = matches!(style.flex_wrap(), FlexWrap::Wrap | FlexWrap::WrapReverse); + let is_wrap_reverse = style.flex_wrap() == FlexWrap::WrapReverse; - let aspect_ratio = style.aspect_ratio; - 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 aspect_ratio = style.aspect_ratio(); + 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_sum = padding.sum_axes() + border.sum_axes(); - let box_sizing_adjustment = if style.box_sizing == BoxSizing::ContentBox { padding_border_sum } else { Size::ZERO }; + let box_sizing_adjustment = + if style.box_sizing() == BoxSizing::ContentBox { padding_border_sum } else { Size::ZERO }; - let align_items = style.align_items.unwrap_or(AlignItems::Stretch); - let align_content = style.align_content.unwrap_or(AlignContent::Stretch); - let justify_content = style.justify_content; + let align_items = style.align_items().unwrap_or(AlignItems::Stretch); + let align_content = style.align_content().unwrap_or(AlignContent::Stretch); + let justify_content = style.justify_content(); // Scrollbar gutters are reserved when the `overflow` property is set to `Overflow::Scroll`. // However, the axis are switched (transposed) because a node that scrolls vertically needs // *horizontal* space to be reserved for a scrollbar - let scrollbar_gutter = style.overflow.transpose().map(|overflow| match overflow { - Overflow::Scroll => style.scrollbar_width, + let scrollbar_gutter = style.overflow().transpose().map(|overflow| match overflow { + Overflow::Scroll => style.scrollbar_width(), _ => 0.0, }); // TODO: make side configurable based on the `direction` property @@ -433,7 +441,7 @@ fn compute_constants( let node_outer_size = known_dimensions; let node_inner_size = node_outer_size.maybe_sub(content_box_inset.sum_axes()); - let gap = style.gap.resolve_or_zero(node_inner_size.or(Size::zero())); + let gap = style.gap().resolve_or_zero(node_inner_size.or(Size::zero())); let container_size = Size::zero(); let inner_container_size = Size::zero(); @@ -445,12 +453,12 @@ fn compute_constants( is_wrap, is_wrap_reverse, min_size: style - .min_size + .min_size() .maybe_resolve(parent_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment), max_size: style - .max_size + .max_size() .maybe_resolve(parent_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment), @@ -476,51 +484,51 @@ fn compute_constants( /// - [**Generate anonymous flex items**](https://www.w3.org/TR/css-flexbox-1/#algo-anon-box) as described in [§4 Flex Items](https://www.w3.org/TR/css-flexbox-1/#flex-items). #[inline] fn generate_anonymous_flex_items( - tree: &impl LayoutPartialTree, + tree: &impl LayoutFlexboxContainer, node: NodeId, constants: &AlgoConstants, ) -> Vec { tree.child_ids(node) .enumerate() - .map(|(index, child)| (index, child, tree.get_style(child))) - .filter(|(_, _, style)| style.position != Position::Absolute) - .filter(|(_, _, style)| style.display != Display::None) + .map(|(index, child)| (index, child, tree.get_flexbox_child_style(child))) + .filter(|(_, _, style)| style.position() != Position::Absolute) + .filter(|(_, _, style)| style.box_generation_mode() != BoxGenerationMode::None) .map(|(index, child, child_style)| { - let aspect_ratio = child_style.aspect_ratio; - let padding = child_style.padding.resolve_or_zero(constants.node_inner_size.width); - let border = child_style.border.resolve_or_zero(constants.node_inner_size.width); + let aspect_ratio = child_style.aspect_ratio(); + let padding = child_style.padding().resolve_or_zero(constants.node_inner_size.width); + let border = child_style.border().resolve_or_zero(constants.node_inner_size.width); let pb_sum = (padding + border).sum_axes(); let box_sizing_adjustment = - if child_style.box_sizing == BoxSizing::ContentBox { pb_sum } else { Size::ZERO }; + if child_style.box_sizing() == BoxSizing::ContentBox { pb_sum } else { Size::ZERO }; FlexItem { node: child, order: index as u32, size: child_style - .size + .size() .maybe_resolve(constants.node_inner_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment), min_size: child_style - .min_size + .min_size() .maybe_resolve(constants.node_inner_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment), max_size: child_style - .max_size + .max_size() .maybe_resolve(constants.node_inner_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment), - inset: child_style.inset.zip_size(constants.node_inner_size, |p, s| p.maybe_resolve(s)), - margin: child_style.margin.resolve_or_zero(constants.node_inner_size.width), - margin_is_auto: child_style.margin.map(|m| m == LengthPercentageAuto::Auto), - padding: child_style.padding.resolve_or_zero(constants.node_inner_size.width), - border: child_style.border.resolve_or_zero(constants.node_inner_size.width), - align_self: child_style.align_self.unwrap_or(constants.align_items), - overflow: child_style.overflow, - scrollbar_width: child_style.scrollbar_width, - flex_grow: child_style.flex_grow, - flex_shrink: child_style.flex_shrink, + inset: child_style.inset().zip_size(constants.node_inner_size, |p, s| p.maybe_resolve(s)), + margin: child_style.margin().resolve_or_zero(constants.node_inner_size.width), + margin_is_auto: child_style.margin().map(|m| m == LengthPercentageAuto::Auto), + padding: child_style.padding().resolve_or_zero(constants.node_inner_size.width), + border: child_style.border().resolve_or_zero(constants.node_inner_size.width), + align_self: child_style.align_self().unwrap_or(constants.align_items), + overflow: child_style.overflow(), + scrollbar_width: child_style.scrollbar_width(), + flex_grow: child_style.flex_grow(), + flex_shrink: child_style.flex_shrink(), flex_basis: 0.0, inner_flex_basis: 0.0, violation: 0.0, @@ -608,7 +616,7 @@ fn determine_available_space( /// (For example, an item with a specified size of zero, positive padding, and box-sizing: border-box will have an outer flex base size of zero—and hence a negative inner flex base size.) #[inline] fn determine_flex_base_size( - tree: &mut impl LayoutPartialTree, + tree: &mut impl LayoutFlexboxContainer, constants: &AlgoConstants, available_space: Size, flex_items: &mut [FlexItem], @@ -616,7 +624,7 @@ fn determine_flex_base_size( let dir = constants.dir; for child in flex_items.iter_mut() { - let child_style = tree.get_style(child.node); + let child_style = tree.get_flexbox_child_style(child.node); // Parent size for child sizing let cross_axis_parent_size = constants.node_inner_size.cross(dir); @@ -643,6 +651,19 @@ fn determine_flex_base_size( ckd }; + let container_width = constants.node_inner_size.main(dir); + let box_sizing_adjustment = if child_style.box_sizing() == BoxSizing::ContentBox { + let padding = child_style.padding().resolve_or_zero(container_width); + let border = child_style.border().resolve_or_zero(container_width); + (padding + border).sum_axes() + } else { + Size::ZERO + } + .main(dir); + let flex_basis = child_style.flex_basis().maybe_resolve(container_width).maybe_add(box_sizing_adjustment); + + drop(child_style); + child.flex_basis = 'flex_basis: { // A. If the item has a definite used flex basis, that’s the flex base size. @@ -653,17 +674,6 @@ fn determine_flex_base_size( // Note: `child.size` has already been resolved against aspect_ratio in generate_anonymous_flex_items // So B will just work here by using main_size without special handling for aspect_ratio - - let container_width = constants.node_inner_size.main(dir); - let box_sizing_adjustment = if child_style.box_sizing == BoxSizing::ContentBox { - let padding = child_style.padding.resolve_or_zero(container_width); - let border = child_style.border.resolve_or_zero(container_width); - (padding + border).sum_axes() - } else { - Size::ZERO - } - .main(dir); - let flex_basis = child_style.flex_basis.maybe_resolve(container_width).maybe_add(box_sizing_adjustment); let main_size = child.size.main(dir); if let Some(flex_basis) = flex_basis.or(main_size) { break 'flex_basis flex_basis; @@ -859,7 +869,7 @@ fn collect_flex_lines<'a>( /// Determine the container's main size (if not already known) fn determine_container_main_size( - tree: &mut impl LayoutPartialTree, + tree: &mut impl LayoutFlexboxContainer, available_space: Size, lines: &mut [FlexLine<'_>], constants: &mut AlgoConstants, @@ -1280,7 +1290,7 @@ fn resolve_flexible_lengths(line: &mut FlexLine, constants: &AlgoConstants, orig /// by performing layout with the used main size and the available space, treating auto as fit-content. #[inline] fn determine_hypothetical_cross_size( - tree: &mut impl LayoutPartialTree, + tree: &mut impl LayoutFlexboxContainer, line: &mut FlexLine, constants: &AlgoConstants, available_space: Size, @@ -1330,7 +1340,7 @@ fn determine_hypothetical_cross_size( /// Calculate the base lines of the children. #[inline] fn calculate_children_base_lines( - tree: &mut impl LayoutPartialTree, + tree: &mut impl LayoutFlexboxContainer, node_size: Size>, available_space: Size, flex_lines: &mut [FlexLine], @@ -1525,31 +1535,36 @@ fn handle_align_content_stretch(flex_lines: &mut [FlexLine], node_size: Size, @@ -1939,7 +1954,7 @@ fn calculate_layout_line( /// Do a final layout pass and collect the resulting layouts. #[inline] fn final_layout_pass( - tree: &mut impl LayoutPartialTree, + tree: &mut impl LayoutFlexboxContainer, flex_lines: &mut [FlexLine], constants: &AlgoConstants, ) -> Size { @@ -1984,7 +1999,7 @@ fn final_layout_pass( /// Perform absolute layout on all absolutely positioned children. #[inline] fn perform_absolute_layout_on_absolute_children( - tree: &mut impl LayoutPartialTree, + tree: &mut impl LayoutFlexboxContainer, node: NodeId, constants: &AlgoConstants, ) -> Size { @@ -1998,58 +2013,59 @@ fn perform_absolute_layout_on_absolute_children( for order in 0..tree.child_count(node) { let child = tree.get_child_id(node, order); - let child_style = tree.get_style(child); + let child_style = tree.get_flexbox_child_style(child); // Skip items that are display:none or are not position:absolute - if child_style.display == Display::None || child_style.position != Position::Absolute { + if child_style.box_generation_mode() == BoxGenerationMode::None || child_style.position() != Position::Absolute + { continue; } - let overflow = child_style.overflow; - let scrollbar_width = child_style.scrollbar_width; - let aspect_ratio = child_style.aspect_ratio; - let align_self = child_style.align_self.unwrap_or(constants.align_items); - let margin = child_style.margin.map(|margin| margin.resolve_to_option(container_width)); - let padding = child_style.padding.resolve_or_zero(Some(container_width)); - let border = child_style.border.resolve_or_zero(Some(container_width)); + let overflow = child_style.overflow(); + let scrollbar_width = child_style.scrollbar_width(); + let aspect_ratio = child_style.aspect_ratio(); + let align_self = child_style.align_self().unwrap_or(constants.align_items); + let margin = child_style.margin().map(|margin| margin.resolve_to_option(inset_relative_size.width)); + let padding = child_style.padding().resolve_or_zero(Some(inset_relative_size.width)); + let border = child_style.border().resolve_or_zero(Some(inset_relative_size.width)); let padding_border_sum = (padding + border).sum_axes(); let box_sizing_adjustment = - if child_style.box_sizing == BoxSizing::ContentBox { padding_border_sum } else { Size::ZERO }; + if child_style.box_sizing() == BoxSizing::ContentBox { padding_border_sum } else { Size::ZERO }; // Resolve inset // Insets are resolved against the container size minus border - let left = child_style.inset.left.maybe_resolve(inset_relative_size.width); - let right = - child_style.inset.right.maybe_resolve(inset_relative_size.width).maybe_add(constants.scrollbar_gutter.x); - let top = child_style.inset.top.maybe_resolve(inset_relative_size.height); - let bottom = - child_style.inset.bottom.maybe_resolve(inset_relative_size.height).maybe_add(constants.scrollbar_gutter.y); + let left = child_style.inset().left.maybe_resolve(inset_relative_size.width); + let right = child_style.inset().right.maybe_resolve(inset_relative_size.width); + let top = child_style.inset().top.maybe_resolve(inset_relative_size.height); + let bottom = child_style.inset().bottom.maybe_resolve(inset_relative_size.height); // Compute known dimensions from min/max/inherent size styles let style_size = child_style - .size + .size() .maybe_resolve(inset_relative_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment); let min_size = child_style - .min_size + .min_size() .maybe_resolve(inset_relative_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment) .or(padding_border_sum.map(Some)) .maybe_max(padding_border_sum); let max_size = child_style - .max_size + .max_size() .maybe_resolve(inset_relative_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment); let mut known_dimensions = style_size.maybe_clamp(min_size, max_size); + drop(child_style); + // Fill in width from left/right and reapply aspect ratio if: // - Width is not already known // - Item has both left and right inset properties set if let (None, Some(left), Some(right)) = (known_dimensions.width, left, right) { - let new_width_raw = container_width.maybe_sub(margin.left).maybe_sub(margin.right) - left - right; + let new_width_raw = inset_relative_size.width.maybe_sub(margin.left).maybe_sub(margin.right) - left - right; known_dimensions.width = Some(f32_max(new_width_raw, 0.0)); known_dimensions = known_dimensions.maybe_apply_aspect_ratio(aspect_ratio).maybe_clamp(min_size, max_size); } @@ -2058,11 +2074,11 @@ fn perform_absolute_layout_on_absolute_children( // - Height is not already known // - Item has both top and bottom inset properties set if let (None, Some(top), Some(bottom)) = (known_dimensions.height, top, bottom) { - let new_height_raw = container_height.maybe_sub(margin.top).maybe_sub(margin.bottom) - top - bottom; + let new_height_raw = + inset_relative_size.height.maybe_sub(margin.top).maybe_sub(margin.bottom) - top - bottom; known_dimensions.height = Some(f32_max(new_height_raw, 0.0)); known_dimensions = known_dimensions.maybe_apply_aspect_ratio(aspect_ratio).maybe_clamp(min_size, max_size); } - let layout_output = tree.perform_child_layout( child, known_dimensions, @@ -2125,6 +2141,7 @@ fn perform_absolute_layout_on_absolute_children( } else if let Some(end) = end_main { constants.container_size.main(constants.dir) - constants.border.main_end(constants.dir) + - constants.scrollbar_gutter.main(constants.dir) - final_size.main(constants.dir) - end - resolved_margin.main_end(constants.dir) @@ -2167,6 +2184,7 @@ fn perform_absolute_layout_on_absolute_children( } else if let Some(end) = end_cross { constants.container_size.cross(constants.dir) - constants.border.cross_end(constants.dir) + - constants.scrollbar_gutter.cross(constants.dir) - final_size.cross(constants.dir) - end - resolved_margin.cross_end(constants.dir) diff --git a/src/compute/grid/alignment.rs b/src/compute/grid/alignment.rs index 3cec376d6..aeeed4424 100644 --- a/src/compute/grid/alignment.rs +++ b/src/compute/grid/alignment.rs @@ -2,14 +2,14 @@ use super::types::GridTrack; use crate::compute::common::alignment::{apply_alignment_fallback, compute_alignment_offset}; use crate::geometry::{InBothAbsAxis, Line, Point, Rect, Size}; -use crate::style::{AlignContent, AlignItems, AlignSelf, AvailableSpace, Overflow, Position}; -use crate::tree::{Layout, LayoutPartialTree, LayoutPartialTreeExt, NodeId, SizingMode}; +use crate::style::{AlignContent, AlignItems, AlignSelf, AvailableSpace, CoreStyle, GridItemStyle, Overflow, Position}; +use crate::tree::{Layout, LayoutPartialTreeExt, NodeId, SizingMode}; use crate::util::sys::f32_max; use crate::util::{MaybeMath, MaybeResolve, ResolveOrZero}; #[cfg(feature = "content_size")] use crate::compute::common::content_size::compute_content_size_contribution; -use crate::BoxSizing; +use crate::{BoxSizing, LayoutGridContainer}; /// Align the grid tracks within the grid according to the align-content (rows) or /// justify-content (columns) property. This only does anything if the size of the @@ -57,7 +57,7 @@ pub(super) fn align_tracks( /// Align and size a grid item into it's final position pub(super) fn align_and_position_item( - tree: &mut impl LayoutPartialTree, + tree: &mut impl LayoutGridContainer, node: NodeId, order: u32, grid_area: Rect, @@ -66,36 +66,39 @@ pub(super) fn align_and_position_item( ) -> (Size, f32, f32) { let grid_area_size = Size { width: grid_area.right - grid_area.left, height: grid_area.bottom - grid_area.top }; - let style = tree.get_style(node); + let style = tree.get_grid_child_style(node); - let overflow = style.overflow; - let scrollbar_width = style.scrollbar_width; - let aspect_ratio = style.aspect_ratio; - let justify_self = style.justify_self; - let align_self = style.align_self; + let overflow = style.overflow(); + let scrollbar_width = style.scrollbar_width(); + let aspect_ratio = style.aspect_ratio(); + let justify_self = style.justify_self(); + let align_self = style.align_self(); - let position = style.position; - let inset_horizontal = style.inset.horizontal_components().map(|size| size.resolve_to_option(grid_area_size.width)); - let inset_vertical = style.inset.vertical_components().map(|size| size.resolve_to_option(grid_area_size.height)); - let padding = style.padding.map(|p| p.resolve_or_zero(Some(grid_area_size.width))); - let border = style.border.map(|p| p.resolve_or_zero(Some(grid_area_size.width))); + let position = style.position(); + let inset_horizontal = + style.inset().horizontal_components().map(|size| size.resolve_to_option(grid_area_size.width)); + let inset_vertical = style.inset().vertical_components().map(|size| size.resolve_to_option(grid_area_size.height)); + let padding = style.padding().map(|p| p.resolve_or_zero(Some(grid_area_size.width))); + let border = style.border().map(|p| p.resolve_or_zero(Some(grid_area_size.width))); let padding_border_size = (padding + border).sum_axes(); + let box_sizing_adjustment = - if style.box_sizing == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO }; + if style.box_sizing() == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO }; + let inherent_size = style - .size + .size() .maybe_resolve(grid_area_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment); let min_size = style - .min_size + .min_size() .maybe_resolve(grid_area_size) .maybe_add(box_sizing_adjustment) .or(padding_border_size.map(Some)) .maybe_max(padding_border_size) .maybe_apply_aspect_ratio(aspect_ratio); let max_size = style - .max_size + .max_size() .maybe_resolve(grid_area_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment); @@ -123,7 +126,7 @@ pub(super) fn align_and_position_item( // Note: This is not a bug. It is part of the CSS spec that both horizontal and vertical margins // resolve against the WIDTH of the grid area. - let margin = style.margin.map(|margin| margin.resolve_to_option(grid_area_size.width)); + let margin = style.margin().map(|margin| margin.resolve_to_option(grid_area_size.width)); let grid_area_minus_item_margins_size = Size { width: grid_area_size.width.maybe_sub(margin.left).maybe_sub(margin.right), @@ -187,6 +190,7 @@ pub(super) fn align_and_position_item( let Size { width, height } = Size { width, height }.maybe_clamp(min_size, max_size); // Layout node + drop(style); let layout_output = tree.perform_child_layout( node, Size { width, height }, diff --git a/src/compute/grid/explicit_grid.rs b/src/compute/grid/explicit_grid.rs index dbc450627..793aa2477 100644 --- a/src/compute/grid/explicit_grid.rs +++ b/src/compute/grid/explicit_grid.rs @@ -2,18 +2,19 @@ //! This mainly consists of evaluating GridAutoTracks use super::types::{GridTrack, TrackCounts}; use crate::geometry::{AbsoluteAxis, Size}; -use crate::style::{GridTrackRepetition, LengthPercentage, NonRepeatedTrackSizingFunction, Style, TrackSizingFunction}; +use crate::style::{GridTrackRepetition, LengthPercentage, NonRepeatedTrackSizingFunction, TrackSizingFunction}; use crate::style_helpers::TaffyAuto; -use crate::util::sys::{GridTrackVec, Vec}; +use crate::util::sys::Vec; use crate::util::MaybeMath; use crate::util::ResolveOrZero; +use crate::GridContainerStyle; #[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, + style: &impl GridContainerStyle, inner_container_size: Size>, axis: AbsoluteAxis, ) -> u16 { @@ -93,7 +94,7 @@ pub(crate) fn compute_explicit_grid_size_in_axis( // - then the number of repetitions is the smallest possible positive integer that fulfills that minimum requirement // Otherwise, the specified track list repeats only once. let size_is_maximum = - style.size.get_abs(axis).into_option().is_some() || style.max_size.get_abs(axis).into_option().is_some(); + style.size().get_abs(axis).into_option().is_some() || style.max_size().get_abs(axis).into_option().is_some(); // Determine the number of repetitions let num_repetitions: u16 = match inner_container_size.get_abs(axis) { @@ -128,7 +129,7 @@ pub(crate) fn compute_explicit_grid_size_in_axis( } }) .sum(); - let gap_size = style.gap.get_abs(axis).resolve_or_zero(Some(inner_container_size)); + let gap_size = style.gap().get_abs(axis).resolve_or_zero(Some(inner_container_size)); // Compute the amount of space that a single repetition of the repeated track list takes let per_repetition_track_used_space: f32 = repetition_definition @@ -176,7 +177,7 @@ pub(crate) fn compute_explicit_grid_size_in_axis( pub(super) fn initialize_grid_tracks( tracks: &mut Vec, counts: TrackCounts, - track_template: &GridTrackVec, + track_template: &[TrackSizingFunction], auto_tracks: &[NonRepeatedTrackSizingFunction], gap: LengthPercentage, track_has_items: impl Fn(usize) -> bool, diff --git a/src/compute/grid/implicit_grid.rs b/src/compute/grid/implicit_grid.rs index 1836cbb5b..d1651218b 100644 --- a/src/compute/grid/implicit_grid.rs +++ b/src/compute/grid/implicit_grid.rs @@ -1,7 +1,8 @@ //! This module is not required for spec compliance, but is used as a performance optimisation //! to reduce the number of allocations required when creating a grid. use crate::geometry::Line; -use crate::style::{GenericGridPlacement, GridPlacement, Style}; +use crate::style::{GenericGridPlacement, GridPlacement}; +use crate::GridItemStyle; use core::cmp::{max, min}; use super::types::TrackCounts; @@ -15,10 +16,10 @@ use super::OriginZeroLine; /// in ways which are impossible to predict until the auto-placement algorithm is run. /// /// Note that this function internally mixes use of grid track numbers and grid line numbers -pub(crate) fn compute_grid_size_estimate<'a>( +pub(crate) fn compute_grid_size_estimate<'a, S: GridItemStyle + 'a>( explicit_col_count: u16, explicit_row_count: u16, - child_styles_iter: impl Iterator, + child_styles_iter: impl Iterator, ) -> (TrackCounts, TrackCounts) { // Iterate over children, producing an estimate of the min and max grid lines (in origin-zero coordinates where) // along with the span of each item @@ -60,20 +61,20 @@ pub(crate) fn compute_grid_size_estimate<'a>( /// /// Min and max grid lines are returned in origin-zero coordinates) /// The span is measured in tracks spanned -fn get_known_child_positions<'a>( - children_iter: impl Iterator, +fn get_known_child_positions<'a, S: GridItemStyle + 'a>( + children_iter: impl Iterator, explicit_col_count: u16, explicit_row_count: u16, ) -> (OriginZeroLine, OriginZeroLine, u16, OriginZeroLine, OriginZeroLine, u16) { let (mut col_min, mut col_max, mut col_max_span) = (OriginZeroLine(0), OriginZeroLine(0), 0); let (mut row_min, mut row_max, mut row_max_span) = (OriginZeroLine(0), OriginZeroLine(0), 0); - children_iter.for_each(|child_style: &Style| { + children_iter.for_each(|child_style| { // Note: that the children reference the lines in between (and around) the tracks not tracks themselves, // and thus we must subtract 1 to get an accurate estimate of the number of tracks let (child_col_min, child_col_max, child_col_span) = - child_min_line_max_line_span(child_style.grid_column, explicit_col_count); + child_min_line_max_line_span(child_style.grid_column(), explicit_col_count); let (child_row_min, child_row_max, child_row_span) = - child_min_line_max_line_span(child_style.grid_row, explicit_row_count); + child_min_line_max_line_span(child_style.grid_row(), explicit_row_count); col_min = min(col_min, child_col_min); col_max = max(col_max, child_col_max); col_max_span = max(col_max_span, child_col_span); diff --git a/src/compute/grid/mod.rs b/src/compute/grid/mod.rs index 2fc02e5bf..c15c98e7b 100644 --- a/src/compute/grid/mod.rs +++ b/src/compute/grid/mod.rs @@ -2,14 +2,16 @@ //! use crate::geometry::{AbsoluteAxis, AbstractAxis, InBothAbsAxis}; use crate::geometry::{Line, Point, Rect, Size}; -use crate::style::{AlignContent, AlignItems, AlignSelf, AvailableSpace, Display, Overflow, Position}; -use crate::tree::{Layout, LayoutInput, LayoutOutput, RunMode, SizingMode}; -use crate::tree::{LayoutPartialTree, LayoutPartialTreeExt, NodeId}; +use crate::style::{AlignItems, AlignSelf, AvailableSpace, Overflow, Position}; +use crate::tree::{Layout, LayoutInput, LayoutOutput, LayoutPartialTreeExt, NodeId, RunMode, SizingMode}; use crate::util::debug::debug_log; use crate::util::sys::{f32_max, GridTrackVec, Vec}; use crate::util::MaybeMath; use crate::util::{MaybeResolve, ResolveOrZero}; -use crate::{style_helpers::*, BoxSizing}; +use crate::{ + style_helpers::*, AlignContent, BoxGenerationMode, BoxSizing, CoreStyle, GridContainerStyle, GridItemStyle, + JustifyContent, LayoutGridContainer, +}; use alignment::{align_and_position_item, align_tracks}; use explicit_grid::{compute_explicit_grid_size_in_axis, initialize_grid_tracks}; use implicit_grid::compute_grid_size_estimate; @@ -35,36 +37,36 @@ mod util; /// - Placing items (which also resolves the implicit grid) /// - Track (row/column) sizing /// - Alignment & Final item placement -pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inputs: LayoutInput) -> LayoutOutput { +pub fn compute_grid_layout(tree: &mut impl LayoutGridContainer, node: NodeId, inputs: LayoutInput) -> LayoutOutput { let LayoutInput { known_dimensions, parent_size, available_space, run_mode, .. } = inputs; - let style = tree.get_style(node).clone(); + let style = tree.get_grid_container_style(node); // 1. Compute "available grid space" // https://www.w3.org/TR/css-grid-1/#available-grid-space - let aspect_ratio = style.aspect_ratio; - let padding = style.padding.resolve_or_zero(parent_size.width); - let border = style.border.resolve_or_zero(parent_size.width); + let aspect_ratio = style.aspect_ratio(); + 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; let padding_border_size = padding_border.sum_axes(); let box_sizing_adjustment = - if style.box_sizing == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO }; + if style.box_sizing() == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO }; let min_size = style - .min_size + .min_size() .maybe_resolve(parent_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment); let max_size = style - .max_size + .max_size() .maybe_resolve(parent_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment); let preferred_size = if inputs.sizing_mode == SizingMode::InherentSize { style - .size + .size() .maybe_resolve(parent_size) - .maybe_apply_aspect_ratio(style.aspect_ratio) + .maybe_apply_aspect_ratio(style.aspect_ratio()) .maybe_add(box_sizing_adjustment) } else { Size::NONE @@ -73,8 +75,8 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu // Scrollbar gutters are reserved when the `overflow` property is set to `Overflow::Scroll`. // However, the axis are switched (transposed) because a node that scrolls vertically needs // *horizontal* space to be reserved for a scrollbar - let scrollbar_gutter = style.overflow.transpose().map(|overflow| match overflow { - Overflow::Scroll => style.scrollbar_width, + let scrollbar_gutter = style.overflow().transpose().map(|overflow| match overflow { + Overflow::Scroll => style.scrollbar_width(), _ => 0.0, }); // TODO: make side configurable based on the `direction` property @@ -82,6 +84,11 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu content_box_inset.right += scrollbar_gutter.x; content_box_inset.bottom += scrollbar_gutter.y; + let align_content = style.align_content().unwrap_or(AlignContent::Stretch); + let justify_content = style.justify_content().unwrap_or(JustifyContent::Stretch); + let align_items = style.align_items(); + let justify_items = style.justify_items(); + let constrained_available_space = known_dimensions .or(preferred_size) .map(|size| size.map(AvailableSpace::Definite)) @@ -114,7 +121,8 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu return LayoutOutput::from_outer_size(Size { width, height }); } - let get_child_styles_iter = |node| tree.child_ids(node).map(|child_node: NodeId| tree.get_style(child_node)); + let get_child_styles_iter = + |node| tree.child_ids(node).map(|child_node: NodeId| tree.get_grid_child_style(child_node)); let child_styles_iter = get_child_styles_iter(node); // 2. Resolve the explicit grid @@ -147,16 +155,18 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu let in_flow_children_iter = || { tree.child_ids(node) .enumerate() - .map(|(index, child_node)| (index, child_node, tree.get_style(child_node))) - .filter(|(_, _, style)| style.display != Display::None && style.position != Position::Absolute) + .map(|(index, child_node)| (index, child_node, tree.get_grid_child_style(child_node))) + .filter(|(_, _, style)| { + style.box_generation_mode() != BoxGenerationMode::None && style.position() != Position::Absolute + }) }; place_grid_items( &mut cell_occupancy_matrix, &mut items, in_flow_children_iter, - style.grid_auto_flow, - style.align_items.unwrap_or(AlignItems::Stretch), - style.justify_items.unwrap_or(AlignItems::Stretch), + style.grid_auto_flow(), + align_items.unwrap_or(AlignItems::Stretch), + justify_items.unwrap_or(AlignItems::Stretch), ); // Extract track counts from previous step (auto-placement can expand the number of tracks) @@ -171,20 +181,22 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu initialize_grid_tracks( &mut columns, final_col_counts, - &style.grid_template_columns, - &style.grid_auto_columns, - style.gap.width, + style.grid_template_columns(), + style.grid_auto_columns(), + style.gap().width, |column_index| cell_occupancy_matrix.column_is_occupied(column_index), ); initialize_grid_tracks( &mut rows, final_row_counts, - &style.grid_template_rows, - &style.grid_auto_rows, - style.gap.height, + style.grid_template_rows(), + style.grid_auto_rows(), + style.gap().height, |row_index| cell_occupancy_matrix.row_is_occupied(row_index), ); + drop(style); + // 6. Track Sizing // Convert grid placements in origin-zero coordinates to indexes into the GridTrack (rows and columns) vectors @@ -205,7 +217,7 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu AbstractAxis::Inline, min_size.get(AbstractAxis::Inline), max_size.get(AbstractAxis::Inline), - style.grid_align_content(AbstractAxis::Block), + align_content, available_grid_space, inner_node_size, &mut columns, @@ -225,7 +237,7 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu AbstractAxis::Block, min_size.get(AbstractAxis::Block), max_size.get(AbstractAxis::Block), - style.grid_align_content(AbstractAxis::Inline), + justify_content, available_grid_space, inner_node_size, &mut rows, @@ -338,7 +350,7 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu AbstractAxis::Inline, min_size.get(AbstractAxis::Inline), max_size.get(AbstractAxis::Inline), - style.grid_align_content(AbstractAxis::Block), + align_content, available_grid_space, inner_node_size, &mut columns, @@ -400,7 +412,7 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu AbstractAxis::Block, min_size.get(AbstractAxis::Block), max_size.get(AbstractAxis::Block), - style.grid_align_content(AbstractAxis::Inline), + justify_content, available_grid_space, inner_node_size, &mut rows, @@ -420,7 +432,7 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu Line { start: padding.left, end: padding.right }, Line { start: border.left, end: border.right }, &mut columns, - style.justify_content.unwrap_or(AlignContent::Stretch), + justify_content, ); // Align rows align_tracks( @@ -428,7 +440,7 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu Line { start: padding.top, end: padding.bottom }, Line { start: border.top, end: border.bottom }, &mut rows, - style.align_content.unwrap_or(AlignContent::Stretch), + align_content, ); // 9. Size, Align, and Position Grid Items @@ -439,7 +451,7 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu // Sort items back into original order to allow them to be matched up with styles items.sort_by_key(|item| item.source_order); - let container_alignment_styles = InBothAbsAxis { horizontal: style.justify_items, vertical: style.align_items }; + let container_alignment_styles = InBothAbsAxis { horizontal: justify_items, vertical: align_items }; // Position in-flow children (stored in items vector) for (index, item) in items.iter_mut().enumerate() { @@ -471,10 +483,11 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu let mut order = items.len() as u32; (0..tree.child_count(node)).for_each(|index| { let child = tree.get_child_id(node, index); - let child_style = tree.get_style(child); + let child_style = tree.get_grid_child_style(child); // Position hidden child - if child_style.display == Display::None { + if child_style.box_generation_mode() == BoxGenerationMode::None { + drop(child_style); tree.set_unrounded_layout(child, &Layout::with_order(order)); tree.perform_child_layout( child, @@ -489,11 +502,11 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu } // Position absolutely positioned child - if child_style.position == Position::Absolute { + if child_style.position() == Position::Absolute { // Convert grid-col-{start/end} into Option's of indexes into the columns vector // The Option is None if the style property is Auto and an unresolvable Span let maybe_col_indexes = child_style - .grid_column + .grid_column() .into_origin_zero(final_col_counts.explicit) .resolve_absolutely_positioned_grid_tracks() .map(|maybe_grid_line| { @@ -502,7 +515,7 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu // Convert grid-row-{start/end} into Option's of indexes into the row vector // The Option is None if the style property is Auto and an unresolvable Span let maybe_row_indexes = child_style - .grid_row + .grid_row() .into_origin_zero(final_row_counts.explicit) .resolve_absolutely_positioned_grid_tracks() .map(|maybe_grid_line| { @@ -521,6 +534,8 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu .map(|index| columns[index].offset) .unwrap_or(container_border_box.width - border.right - scrollbar_gutter.x), }; + drop(child_style); + // TODO: Baseline alignment support for absolutely positioned items (should check if is actuallty specified) #[cfg_attr(not(feature = "content_size"), allow(unused_variables))] let (content_size_contribution, _, _) = diff --git a/src/compute/grid/placement.rs b/src/compute/grid/placement.rs index bd760accb..695ac0291 100644 --- a/src/compute/grid/placement.rs +++ b/src/compute/grid/placement.rs @@ -4,15 +4,16 @@ use super::types::{CellOccupancyMatrix, CellOccupancyState, GridItem}; use super::OriginZeroLine; use crate::geometry::Line; use crate::geometry::{AbsoluteAxis, InBothAbsAxis}; -use crate::style::{AlignItems, GridAutoFlow, OriginZeroGridPlacement, Style}; +use crate::style::{AlignItems, GridAutoFlow, OriginZeroGridPlacement}; use crate::tree::NodeId; use crate::util::sys::Vec; +use crate::GridItemStyle; /// 8.5. Grid Item Placement Algorithm /// Place items into the grid, generating new rows/column into the implicit grid as required /// /// [Specification](https://www.w3.org/TR/css-grid-2/#auto-placement-algo) -pub(super) fn place_grid_items<'a, ChildIter>( +pub(super) fn place_grid_items<'a, S, ChildIter>( cell_occupancy_matrix: &mut CellOccupancyMatrix, items: &mut Vec, children_iter: impl Fn() -> ChildIter, @@ -20,7 +21,8 @@ pub(super) fn place_grid_items<'a, ChildIter>( align_items: AlignItems, justify_items: AlignItems, ) where - ChildIter: Iterator, + S: GridItemStyle + 'a, + ChildIter: Iterator, { let primary_axis = grid_auto_flow.primary_axis(); let secondary_axis = primary_axis.other_axis(); @@ -28,10 +30,12 @@ pub(super) fn place_grid_items<'a, ChildIter>( let map_child_style_to_origin_zero_placement = { let explicit_col_count = cell_occupancy_matrix.track_counts(AbsoluteAxis::Horizontal).explicit; let explicit_row_count = cell_occupancy_matrix.track_counts(AbsoluteAxis::Vertical).explicit; - move |(index, node, style): (usize, NodeId, &'a Style)| -> (_, _, _, &'a Style) { + move |(index, node, style): (usize, NodeId, S)| -> (_, _, _, S) { let origin_zero_placement = InBothAbsAxis { - horizontal: style.grid_column.map(|placement| placement.into_origin_zero_placement(explicit_col_count)), - vertical: style.grid_row.map(|placement| placement.into_origin_zero_placement(explicit_row_count)), + horizontal: style + .grid_column() + .map(|placement| placement.into_origin_zero_placement(explicit_col_count)), + vertical: style.grid_row().map(|placement| placement.into_origin_zero_placement(explicit_row_count)), }; (index, node, origin_zero_placement, style) } @@ -40,7 +44,7 @@ pub(super) fn place_grid_items<'a, ChildIter>( // 1. Place children with definite positions let mut idx = 0; children_iter() - .filter(|(_, _, child_style)| child_style.grid_row.is_definite() && child_style.grid_column.is_definite()) + .filter(|(_, _, child_style)| child_style.grid_row().is_definite() && child_style.grid_column().is_definite()) .map(map_child_style_to_origin_zero_placement) .for_each(|(index, child_node, child_placement, style)| { idx += 1; @@ -296,12 +300,12 @@ fn place_indefinitely_positioned_item( /// Record the grid item in both CellOccupancyMatric and the GridItems list /// once a definite placement has been determined #[allow(clippy::too_many_arguments)] -fn record_grid_placement( +fn record_grid_placement( cell_occupancy_matrix: &mut CellOccupancyMatrix, items: &mut Vec, node: NodeId, index: usize, - style: &Style, + style: S, parent_align_items: AlignItems, parent_justify_items: AlignItems, primary_axis: AbsoluteAxis, diff --git a/src/compute/grid/types/grid_item.rs b/src/compute/grid/types/grid_item.rs index b77f7de02..3df863d82 100644 --- a/src/compute/grid/types/grid_item.rs +++ b/src/compute/grid/types/grid_item.rs @@ -5,11 +5,11 @@ use crate::geometry::AbstractAxis; use crate::geometry::{Line, Point, Rect, Size}; use crate::style::{ AlignItems, AlignSelf, AvailableSpace, Dimension, LengthPercentageAuto, MaxTrackSizingFunction, - MinTrackSizingFunction, Overflow, Style, + MinTrackSizingFunction, Overflow, }; use crate::tree::{LayoutPartialTree, LayoutPartialTreeExt, NodeId, SizingMode}; use crate::util::{MaybeMath, MaybeResolve, ResolveOrZero}; -use crate::{BoxSizing, LengthPercentage}; +use crate::{BoxSizing, GridItemStyle, LengthPercentage}; use core::ops::Range; /// Represents a single grid item @@ -93,11 +93,11 @@ pub(in super::super) struct GridItem { impl GridItem { /// Create a new item given a concrete placement in both axes - pub fn new_with_placement_style_and_order( + pub fn new_with_placement_style_and_order( node: NodeId, col_span: Line, row_span: Line, - style: &Style, + style: S, parent_align_items: AlignItems, parent_justify_items: AlignItems, source_order: u16, @@ -107,17 +107,17 @@ impl GridItem { source_order, row: row_span, column: col_span, - overflow: style.overflow, - box_sizing: style.box_sizing, - size: style.size, - min_size: style.min_size, - max_size: style.max_size, - aspect_ratio: style.aspect_ratio, - padding: style.padding, - border: style.border, - margin: style.margin, - align_self: style.align_self.unwrap_or(parent_align_items), - justify_self: style.justify_self.unwrap_or(parent_justify_items), + overflow: style.overflow(), + box_sizing: style.box_sizing(), + size: style.size(), + min_size: style.min_size(), + max_size: style.max_size(), + aspect_ratio: style.aspect_ratio(), + padding: style.padding(), + border: style.border(), + margin: style.margin(), + align_self: style.align_self().unwrap_or(parent_align_items), + justify_self: style.justify_self().unwrap_or(parent_justify_items), baseline: None, baseline_shim: 0.0, row_indexes: Line { start: 0, end: 0 }, // Properly initialised later diff --git a/src/compute/leaf.rs b/src/compute/leaf.rs index 117d4284f..0c3f5a0af 100644 --- a/src/compute/leaf.rs +++ b/src/compute/leaf.rs @@ -1,20 +1,20 @@ //! Computes size using styles and measure functions use crate::geometry::{Point, Size}; -use crate::style::{AvailableSpace, Display, Overflow, Position, Style}; +use crate::style::{AvailableSpace, Overflow, Position}; use crate::tree::{CollapsibleMarginSet, RunMode}; use crate::tree::{LayoutInput, LayoutOutput, SizingMode}; use crate::util::debug::debug_log; use crate::util::sys::f32_max; use crate::util::MaybeMath; use crate::util::{MaybeResolve, ResolveOrZero}; -use crate::BoxSizing; +use crate::{BoxSizing, CoreStyle}; use core::unreachable; /// Compute the size of a leaf node (node with no children) pub fn compute_leaf_layout( inputs: LayoutInput, - style: &Style, + style: &impl CoreStyle, measure_function: MeasureFunction, ) -> LayoutOutput where @@ -24,12 +24,12 @@ where // 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 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; let pb_sum = padding_border.sum_axes(); - let box_sizing_adjustment = if style.box_sizing == BoxSizing::ContentBox { pb_sum } else { Size::ZERO }; + let box_sizing_adjustment = if style.box_sizing() == BoxSizing::ContentBox { pb_sum } else { Size::ZERO }; // Resolve node's preferred/min/max sizes (width/heights) against the available space (percentages resolve to pixel values) // For ContentSize mode, we pretend that the node has no size styles as these should be ignored. @@ -41,18 +41,18 @@ where (node_size, node_min_size, node_max_size, None) } SizingMode::InherentSize => { - let aspect_ratio = style.aspect_ratio; + let aspect_ratio = style.aspect_ratio(); let style_size = style - .size + .size() .maybe_resolve(parent_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment); let style_min_size = style - .min_size + .min_size() .maybe_resolve(parent_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment); - let style_max_size = style.max_size.maybe_resolve(parent_size).maybe_add(box_sizing_adjustment); + let style_max_size = style.max_size().maybe_resolve(parent_size).maybe_add(box_sizing_adjustment); let node_size = known_dimensions.or(style_size); (node_size, style_min_size, style_max_size, aspect_ratio) @@ -62,8 +62,8 @@ where // Scrollbar gutters are reserved when the `overflow` property is set to `Overflow::Scroll`. // However, the axis are switched (transposed) because a node that scrolls vertically needs // *horizontal* space to be reserved for a scrollbar - let scrollbar_gutter = style.overflow.transpose().map(|overflow| match overflow { - Overflow::Scroll => style.scrollbar_width, + let scrollbar_gutter = style.overflow().transpose().map(|overflow| match overflow { + Overflow::Scroll => style.scrollbar_width(), _ => 0.0, }); // TODO: make side configurable based on the `direction` property @@ -71,15 +71,10 @@ where content_box_inset.right += scrollbar_gutter.x; content_box_inset.bottom += scrollbar_gutter.y; - #[cfg(feature = "block_layout")] - let is_block = style.display == Display::Block; - #[cfg(not(feature = "block_layout"))] - let is_block = false; - - let has_styles_preventing_being_collapsed_through = !is_block - || style.overflow.x.is_scroll_container() - || style.overflow.y.is_scroll_container() - || style.position == Position::Absolute + let has_styles_preventing_being_collapsed_through = !style.is_block() + || style.overflow().x.is_scroll_container() + || style.overflow().y.is_scroll_container() + || style.position() == Position::Absolute || padding.top > 0.0 || padding.bottom > 0.0 || border.top > 0.0 diff --git a/src/compute/mod.rs b/src/compute/mod.rs index 00d4a8cb9..8a23c051e 100644 --- a/src/compute/mod.rs +++ b/src/compute/mod.rs @@ -45,14 +45,14 @@ pub use self::flexbox::compute_flexbox_layout; pub use self::grid::compute_grid_layout; use crate::geometry::{Line, Point, Size}; -use crate::style::{AvailableSpace, Overflow}; +use crate::style::{AvailableSpace, CoreStyle, Overflow}; use crate::tree::{ Layout, LayoutInput, LayoutOutput, LayoutPartialTree, LayoutPartialTreeExt, NodeId, RoundTree, SizingMode, }; use crate::util::debug::{debug_log, debug_log_node, debug_pop_node, debug_push_node}; use crate::util::sys::round; use crate::util::ResolveOrZero; -use crate::{BoxSizing, Display, MaybeMath, MaybeResolve}; +use crate::{BoxSizing, MaybeMath, MaybeResolve}; /// Compute layout for the root node in the tree pub fn compute_root_layout(tree: &mut impl LayoutPartialTree, root: NodeId, available_space: Size) { @@ -61,30 +61,30 @@ pub fn compute_root_layout(tree: &mut impl LayoutPartialTree, root: NodeId, avai #[cfg(feature = "block_layout")] { let parent_size = available_space.into_options(); - let style = tree.get_style(root); + let style = tree.get_core_container_style(root); - if style.display == Display::Block { + if style.is_block() { // Pull these out earlier to avoid borrowing issues - let aspect_ratio = style.aspect_ratio; - 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 aspect_ratio = style.aspect_ratio(); + 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_size = (padding + border).sum_axes(); let box_sizing_adjustment = - if style.box_sizing == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO }; + if style.box_sizing() == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO }; let min_size = style - .min_size + .min_size() .maybe_resolve(parent_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment); let max_size = style - .max_size + .max_size() .maybe_resolve(parent_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment); let clamped_style_size = style - .size + .size() .maybe_resolve(parent_size) .maybe_apply_aspect_ratio(aspect_ratio) .maybe_add(box_sizing_adjustment) @@ -122,14 +122,15 @@ pub fn compute_root_layout(tree: &mut impl LayoutPartialTree, root: NodeId, avai Line::FALSE, ); - let style = tree.get_style(root); - let padding = style.padding.resolve_or_zero(available_space.width.into_option()); - let border = style.border.resolve_or_zero(available_space.width.into_option()); - let margin = style.margin.resolve_or_zero(available_space.width.into_option()); + let style = tree.get_core_container_style(root); + let padding = style.padding().resolve_or_zero(available_space.width.into_option()); + let border = style.border().resolve_or_zero(available_space.width.into_option()); + let margin = style.margin().resolve_or_zero(available_space.width.into_option()); let scrollbar_size = Size { - width: if style.overflow.y == Overflow::Scroll { style.scrollbar_width } else { 0.0 }, - height: if style.overflow.x == Overflow::Scroll { style.scrollbar_width } else { 0.0 }, + width: if style.overflow().y == Overflow::Scroll { style.scrollbar_width() } else { 0.0 }, + height: if style.overflow().x == Overflow::Scroll { style.scrollbar_width() } else { 0.0 }, }; + drop(style); tree.set_unrounded_layout( root, diff --git a/src/style/flex.rs b/src/style/flex.rs index c24f488d8..f04f3a454 100644 --- a/src/style/flex.rs +++ b/src/style/flex.rs @@ -1,4 +1,70 @@ //! Style types for Flexbox layout +use super::{AlignContent, AlignItems, AlignSelf, CoreStyle, Dimension, JustifyContent, LengthPercentage, Style}; +use crate::geometry::Size; + +/// The set of styles required for a Flexbox container +pub trait FlexboxContainerStyle: CoreStyle { + /// Which direction does the main axis flow in? + #[inline(always)] + fn flex_direction(&self) -> FlexDirection { + Style::DEFAULT.flex_direction + } + /// Should elements wrap, or stay in a single line? + #[inline(always)] + fn flex_wrap(&self) -> FlexWrap { + Style::DEFAULT.flex_wrap + } + + /// How large should the gaps between items in a grid or flex container be? + #[inline(always)] + fn gap(&self) -> Size { + Style::DEFAULT.gap + } + + // Alignment properties + + /// How should content contained within this item be aligned in the cross/block axis + #[inline(always)] + fn align_content(&self) -> Option { + Style::DEFAULT.align_content + } + /// How this node's children aligned in the cross/block axis? + #[inline(always)] + fn align_items(&self) -> Option { + Style::DEFAULT.align_items + } + /// How this node's children should be aligned in the inline axis + #[inline(always)] + fn justify_content(&self) -> Option { + Style::DEFAULT.justify_content + } +} + +/// The set of styles required for a Flexbox item (child of a Flexbox container) +pub trait FlexboxItemStyle: CoreStyle { + /// Sets the initial main axis size of the item + #[inline(always)] + fn flex_basis(&self) -> Dimension { + Style::DEFAULT.flex_basis + } + /// The relative rate at which this item grows when it is expanding to fill space + #[inline(always)] + fn flex_grow(&self) -> f32 { + Style::DEFAULT.flex_grow + } + /// The relative rate at which this item shrinks when it is contracting to fit into space + #[inline(always)] + fn flex_shrink(&self) -> f32 { + Style::DEFAULT.flex_shrink + } + + /// How this node should be aligned in the cross/block axis + /// Falls back to the parents [`AlignItems`] if not set + #[inline(always)] + fn align_self(&self) -> Option { + Style::DEFAULT.align_self + } +} use crate::geometry::AbsoluteAxis; diff --git a/src/style/grid.rs b/src/style/grid.rs index b2d2b04b4..d2597e69c 100644 --- a/src/style/grid.rs +++ b/src/style/grid.rs @@ -1,13 +1,124 @@ //! Style types for CSS Grid layout -use super::{AlignContent, LengthPercentage, Style}; +use super::{AlignContent, AlignItems, AlignSelf, CoreStyle, JustifyContent, LengthPercentage, Style}; use crate::compute::grid::{GridCoordinate, GridLine, OriginZeroLine}; -use crate::geometry::{AbsoluteAxis, AbstractAxis}; -use crate::geometry::{Line, MinMax}; +use crate::geometry::{AbsoluteAxis, AbstractAxis, Line, MinMax, Size}; use crate::style_helpers::*; use crate::util::sys::GridTrackVec; use core::cmp::{max, min}; use core::convert::Infallible; +/// The set of styles required for a CSS Grid container +pub trait GridContainerStyle: CoreStyle { + /// Defines the track sizing functions (heights) of the grid rows + #[inline(always)] + fn grid_template_rows(&self) -> &[TrackSizingFunction] { + &[] + } + /// Defines the track sizing functions (widths) of the grid columns + #[inline(always)] + fn grid_template_columns(&self) -> &[TrackSizingFunction] { + &[] + } + /// Defines the size of implicitly created rows + #[inline(always)] + fn grid_auto_rows(&self) -> &[NonRepeatedTrackSizingFunction] { + &[] + } + /// Defined the size of implicitly created columns + #[inline(always)] + fn grid_auto_columns(&self) -> &[NonRepeatedTrackSizingFunction] { + &[] + } + /// Controls how items get placed into the grid for auto-placed items + #[inline(always)] + fn grid_auto_flow(&self) -> GridAutoFlow { + Style::DEFAULT.grid_auto_flow + } + + /// How large should the gaps between items in a grid or flex container be? + #[inline(always)] + fn gap(&self) -> Size { + Style::DEFAULT.gap + } + + // Alignment properties + + /// How should content contained within this item be aligned in the cross/block axis + #[inline(always)] + fn align_content(&self) -> Option { + Style::DEFAULT.align_content + } + /// How should contained within this item be aligned in the main/inline axis + #[inline(always)] + fn justify_content(&self) -> Option { + Style::DEFAULT.justify_content + } + /// How this node's children aligned in the cross/block axis? + #[inline(always)] + fn align_items(&self) -> Option { + Style::DEFAULT.align_items + } + /// How this node's children should be aligned in the inline axis + #[inline(always)] + fn justify_items(&self) -> Option { + Style::DEFAULT.justify_items + } + + /// Get a grid item's row or column placement depending on the axis passed + #[inline(always)] + fn grid_template_tracks(&self, axis: AbsoluteAxis) -> &[TrackSizingFunction] { + match axis { + AbsoluteAxis::Horizontal => self.grid_template_columns(), + AbsoluteAxis::Vertical => self.grid_template_rows(), + } + } + + /// Get a grid container's align-content or justify-content alignment depending on the axis passed + #[inline(always)] + fn grid_align_content(&self, axis: AbstractAxis) -> AlignContent { + match axis { + AbstractAxis::Inline => self.justify_content().unwrap_or(AlignContent::Stretch), + AbstractAxis::Block => self.align_content().unwrap_or(AlignContent::Stretch), + } + } +} + +/// The set of styles required for a CSS Grid item (child of a CSS Grid container) +pub trait GridItemStyle: CoreStyle { + /// Defines which row in the grid the item should start and end at + #[inline(always)] + fn grid_row(&self) -> Line { + Style::DEFAULT.grid_row + } + /// Defines which column in the grid the item should start and end at + #[inline(always)] + fn grid_column(&self) -> Line { + Style::DEFAULT.grid_column + } + + /// How this node should be aligned in the cross/block axis + /// Falls back to the parents [`AlignItems`] if not set + #[inline(always)] + fn align_self(&self) -> Option { + Style::DEFAULT.align_self + } + /// How this node should be aligned in the inline axis + /// Falls back to the parents [`super::JustifyItems`] if not set + #[inline(always)] + fn justify_self(&self) -> Option { + Style::DEFAULT.justify_self + } + + /// Get a grid item's row or column placement depending on the axis passed + #[inline(always)] + fn grid_placement(&self, axis: AbsoluteAxis) -> Line { + match axis { + AbsoluteAxis::Horizontal => self.grid_column(), + AbsoluteAxis::Vertical => self.grid_row(), + } + } +} + /// Controls whether grid items are placed row-wise or column-wise. And whether the sparse or dense packing algorithm is used. /// /// The "dense" packing algorithm attempts to fill in holes earlier in the grid, if smaller items come up later. This may cause items to appear out-of-order, when doing so would fill in holes left by larger items. @@ -600,30 +711,3 @@ impl From> for TrackSizin Self::Single(input) } } - -// Grid extensions to the Style struct -impl Style { - /// Get a grid item's row or column placement depending on the axis passed - pub(crate) fn grid_template_tracks(&self, axis: AbsoluteAxis) -> &GridTrackVec { - match axis { - AbsoluteAxis::Horizontal => &self.grid_template_columns, - AbsoluteAxis::Vertical => &self.grid_template_rows, - } - } - - /// Get a grid item's row or column placement depending on the axis passed - pub(crate) fn grid_placement(&self, axis: AbsoluteAxis) -> Line { - match axis { - AbsoluteAxis::Horizontal => self.grid_column, - AbsoluteAxis::Vertical => self.grid_row, - } - } - - /// Get a grid container's align-content or justify-content alignment depending on the axis passed - pub(crate) fn grid_align_content(&self, axis: AbstractAxis) -> AlignContent { - match axis { - AbstractAxis::Inline => self.justify_content.unwrap_or(AlignContent::Stretch), - AbstractAxis::Block => self.align_content.unwrap_or(AlignContent::Stretch), - } - } -} diff --git a/src/style/mod.rs b/src/style/mod.rs index 31f4419b0..d32924879 100644 --- a/src/style/mod.rs +++ b/src/style/mod.rs @@ -9,7 +9,7 @@ pub use self::alignment::{AlignContent, AlignItems, AlignSelf, JustifyContent, J pub use self::dimension::{AvailableSpace, Dimension, LengthPercentage, LengthPercentageAuto}; #[cfg(feature = "flexbox")] -pub use self::flex::{FlexDirection, FlexWrap}; +pub use self::flex::{FlexDirection, FlexWrap, FlexboxContainerStyle, FlexboxItemStyle}; #[cfg(feature = "grid")] mod grid; @@ -17,8 +17,8 @@ mod grid; pub(crate) use self::grid::{GenericGridPlacement, OriginZeroGridPlacement}; #[cfg(feature = "grid")] pub use self::grid::{ - GridAutoFlow, GridPlacement, GridTrackRepetition, MaxTrackSizingFunction, MinTrackSizingFunction, - NonRepeatedTrackSizingFunction, TrackSizingFunction, + GridAutoFlow, GridContainerStyle, GridItemStyle, GridPlacement, GridTrackRepetition, MaxTrackSizingFunction, + MinTrackSizingFunction, NonRepeatedTrackSizingFunction, TrackSizingFunction, }; use crate::geometry::{Point, Rect, Size}; @@ -29,6 +29,93 @@ use crate::style_helpers; #[cfg(feature = "grid")] use crate::util::sys::GridTrackVec; +/// The core set of styles that are shared between all CSS layout nodes +/// +/// Note that all methods come with a default implementation which simply returns the default value for that style property +/// but this is a just a convenience to save on boilerplate for styles that your implementation doesn't support. You will need +/// to override the default implementation for each style property that your style type actually supports. +pub trait CoreStyle { + /// Which box generation mode should be used + #[inline(always)] + fn box_generation_mode(&self) -> BoxGenerationMode { + BoxGenerationMode::DEFAULT + } + /// Is block layout? + #[inline(always)] + fn is_block(&self) -> bool { + false + } + /// Which box do size styles apply to + #[inline(always)] + fn box_sizing(&self) -> BoxSizing { + BoxSizing::BorderBox + } + + // Overflow properties + /// How children overflowing their container should affect layout + #[inline(always)] + fn overflow(&self) -> Point { + Style::DEFAULT.overflow + } + /// How much space (in points) should be reserved for the scrollbars of `Overflow::Scroll` and `Overflow::Auto` nodes. + #[inline(always)] + fn scrollbar_width(&self) -> f32 { + 0.0 + } + + // Position properties + /// What should the `position` value of this struct use as a base offset? + #[inline(always)] + fn position(&self) -> Position { + Style::DEFAULT.position + } + /// How should the position of this element be tweaked relative to the layout defined? + #[inline(always)] + fn inset(&self) -> Rect { + Style::DEFAULT.inset + } + + // Size properies + /// Sets the initial size of the item + #[inline(always)] + fn size(&self) -> Size { + Style::DEFAULT.size + } + /// Controls the minimum size of the item + #[inline(always)] + fn min_size(&self) -> Size { + Style::DEFAULT.min_size + } + /// Controls the maximum size of the item + #[inline(always)] + fn max_size(&self) -> Size { + Style::DEFAULT.max_size + } + /// Sets the preferred aspect ratio for the item + /// The ratio is calculated as width divided by height. + #[inline(always)] + fn aspect_ratio(&self) -> Option { + Style::DEFAULT.aspect_ratio + } + + // Spacing Properties + /// How large should the margin be on each side? + #[inline(always)] + fn margin(&self) -> Rect { + Style::DEFAULT.margin + } + /// How large should the padding be on each side? + #[inline(always)] + fn padding(&self) -> Rect { + Style::DEFAULT.padding + } + /// How large should the border be on each side? + #[inline(always)] + fn border(&self) -> Rect { + Style::DEFAULT.border + } +} + /// Sets the layout used for the children of this node /// /// The default values depends on on which feature flags are enabled. The order of precedence is: Flex, Grid, Block, None. @@ -44,28 +131,34 @@ pub enum Display { /// The children will follow the CSS Grid layout algorithm #[cfg(feature = "grid")] Grid, - /// The children will not be laid out, and will follow absolute positioning + /// The node is hidden, and it's children will also be hidden None, } impl Display { - /// The default of Display. + /// The default Display mode #[cfg(feature = "flexbox")] pub const DEFAULT: Display = Display::Flex; - /// The default of Display. + /// The default Display mode #[cfg(all(feature = "grid", not(feature = "flexbox")))] pub const DEFAULT: Display = Display::Grid; - /// The default of Display. + /// The default Display mode #[cfg(all(feature = "block_layout", not(feature = "flexbox"), not(feature = "grid")))] pub const DEFAULT: Display = Display::Block; - /// The default of Display. + /// The default Display mode #[cfg(all(not(feature = "flexbox"), not(feature = "grid"), not(feature = "block_layout")))] pub const DEFAULT: Display = Display::None; } +impl Default for Display { + fn default() -> Self { + Self::DEFAULT + } +} + impl core::fmt::Display for Display { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { @@ -80,7 +173,23 @@ impl core::fmt::Display for Display { } } -impl Default for Display { +/// An abstracted version of the CSS `display` property where any value other than "none" is represented by "normal" +/// See: +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum BoxGenerationMode { + /// The node generates a box in the regular way + Normal, + /// The node and it's descendants generate no boxes (they are hidden) + None, +} + +impl BoxGenerationMode { + /// The default of BoxGenerationMode + pub const DEFAULT: BoxGenerationMode = BoxGenerationMode::Normal; +} + +impl Default for BoxGenerationMode { fn default() -> Self { Self::DEFAULT } @@ -306,11 +415,11 @@ pub struct Style { #[cfg(feature = "flexbox")] pub flex_shrink: f32, - // Grid container properties - /// Defines the track sizing functions (widths) of the grid rows + // Grid container properies + /// Defines the track sizing functions (heights) of the grid rows #[cfg(feature = "grid")] pub grid_template_rows: GridTrackVec, - /// Defines the track sizing functions (heights) of the grid columns + /// Defines the track sizing functions (widths) of the grid columns #[cfg(feature = "grid")] pub grid_template_columns: GridTrackVec, /// Defines the size of implicitly created rows @@ -398,6 +507,352 @@ impl Default for Style { } } +impl CoreStyle for Style { + #[inline(always)] + fn box_generation_mode(&self) -> BoxGenerationMode { + match self.display { + Display::None => BoxGenerationMode::None, + _ => BoxGenerationMode::Normal, + } + } + #[inline(always)] + #[cfg(feature = "block_layout")] + fn is_block(&self) -> bool { + matches!(self.display, Display::Block) + } + #[inline(always)] + fn box_sizing(&self) -> BoxSizing { + self.box_sizing + } + #[inline(always)] + fn overflow(&self) -> Point { + self.overflow + } + #[inline(always)] + fn scrollbar_width(&self) -> f32 { + self.scrollbar_width + } + #[inline(always)] + fn position(&self) -> Position { + self.position + } + #[inline(always)] + fn inset(&self) -> Rect { + self.inset + } + #[inline(always)] + fn size(&self) -> Size { + self.size + } + #[inline(always)] + fn min_size(&self) -> Size { + self.min_size + } + #[inline(always)] + fn max_size(&self) -> Size { + self.max_size + } + #[inline(always)] + fn aspect_ratio(&self) -> Option { + self.aspect_ratio + } + #[inline(always)] + fn margin(&self) -> Rect { + self.margin + } + #[inline(always)] + fn padding(&self) -> Rect { + self.padding + } + #[inline(always)] + fn border(&self) -> Rect { + self.border + } +} + +impl CoreStyle for &'_ T { + #[inline(always)] + fn box_generation_mode(&self) -> BoxGenerationMode { + (*self).box_generation_mode() + } + #[inline(always)] + fn is_block(&self) -> bool { + (*self).is_block() + } + #[inline(always)] + fn box_sizing(&self) -> BoxSizing { + (*self).box_sizing() + } + #[inline(always)] + fn overflow(&self) -> Point { + (*self).overflow() + } + #[inline(always)] + fn scrollbar_width(&self) -> f32 { + (*self).scrollbar_width() + } + #[inline(always)] + fn position(&self) -> Position { + (*self).position() + } + #[inline(always)] + fn inset(&self) -> Rect { + (*self).inset() + } + #[inline(always)] + fn size(&self) -> Size { + (*self).size() + } + #[inline(always)] + fn min_size(&self) -> Size { + (*self).min_size() + } + #[inline(always)] + fn max_size(&self) -> Size { + (*self).max_size() + } + #[inline(always)] + fn aspect_ratio(&self) -> Option { + (*self).aspect_ratio() + } + #[inline(always)] + fn margin(&self) -> Rect { + (*self).margin() + } + #[inline(always)] + fn padding(&self) -> Rect { + (*self).padding() + } + #[inline(always)] + fn border(&self) -> Rect { + (*self).border() + } +} + +#[cfg(feature = "flexbox")] +impl FlexboxContainerStyle for &Style { + #[inline(always)] + fn flex_direction(&self) -> FlexDirection { + self.flex_direction + } + #[inline(always)] + fn flex_wrap(&self) -> FlexWrap { + self.flex_wrap + } + #[inline(always)] + fn gap(&self) -> Size { + self.gap + } + #[inline(always)] + fn align_content(&self) -> Option { + self.align_content + } + #[inline(always)] + fn align_items(&self) -> Option { + self.align_items + } + #[inline(always)] + fn justify_content(&self) -> Option { + self.justify_content + } +} + +#[cfg(feature = "flexbox")] +impl FlexboxContainerStyle for &'_ T { + #[inline(always)] + fn flex_direction(&self) -> FlexDirection { + (*self).flex_direction() + } + #[inline(always)] + fn flex_wrap(&self) -> FlexWrap { + (*self).flex_wrap() + } + #[inline(always)] + fn gap(&self) -> Size { + (*self).gap() + } + #[inline(always)] + fn align_content(&self) -> Option { + (*self).align_content() + } + #[inline(always)] + fn align_items(&self) -> Option { + (*self).align_items() + } + #[inline(always)] + fn justify_content(&self) -> Option { + (*self).justify_content() + } +} + +#[cfg(feature = "flexbox")] +impl FlexboxItemStyle for Style { + #[inline(always)] + fn flex_basis(&self) -> Dimension { + self.flex_basis + } + #[inline(always)] + fn flex_grow(&self) -> f32 { + self.flex_grow + } + #[inline(always)] + fn flex_shrink(&self) -> f32 { + self.flex_shrink + } + #[inline(always)] + fn align_self(&self) -> Option { + self.align_self + } +} + +#[cfg(feature = "flexbox")] +impl FlexboxItemStyle for &'_ T { + #[inline(always)] + fn flex_basis(&self) -> Dimension { + (*self).flex_basis() + } + #[inline(always)] + fn flex_grow(&self) -> f32 { + (*self).flex_grow() + } + #[inline(always)] + fn flex_shrink(&self) -> f32 { + (*self).flex_shrink() + } + #[inline(always)] + fn align_self(&self) -> Option { + (*self).align_self() + } +} + +#[cfg(feature = "grid")] +impl GridContainerStyle for Style { + #[inline(always)] + fn grid_template_rows(&self) -> &[TrackSizingFunction] { + &self.grid_template_rows + } + #[inline(always)] + fn grid_template_columns(&self) -> &[TrackSizingFunction] { + &self.grid_template_columns + } + #[inline(always)] + fn grid_auto_rows(&self) -> &[NonRepeatedTrackSizingFunction] { + &self.grid_auto_rows + } + #[inline(always)] + fn grid_auto_columns(&self) -> &[NonRepeatedTrackSizingFunction] { + &self.grid_auto_columns + } + #[inline(always)] + fn grid_auto_flow(&self) -> GridAutoFlow { + self.grid_auto_flow + } + #[inline(always)] + fn gap(&self) -> Size { + self.gap + } + #[inline(always)] + fn align_content(&self) -> Option { + self.align_content + } + #[inline(always)] + fn justify_content(&self) -> Option { + self.justify_content + } + #[inline(always)] + fn align_items(&self) -> Option { + self.align_items + } + #[inline(always)] + fn justify_items(&self) -> Option { + self.justify_items + } +} + +#[cfg(feature = "grid")] +impl GridContainerStyle for &'_ T { + #[inline(always)] + fn grid_template_rows(&self) -> &[TrackSizingFunction] { + (*self).grid_template_rows() + } + #[inline(always)] + fn grid_template_columns(&self) -> &[TrackSizingFunction] { + (*self).grid_template_columns() + } + #[inline(always)] + fn grid_auto_rows(&self) -> &[NonRepeatedTrackSizingFunction] { + (*self).grid_auto_rows() + } + #[inline(always)] + fn grid_auto_columns(&self) -> &[NonRepeatedTrackSizingFunction] { + (*self).grid_auto_columns() + } + #[inline(always)] + fn grid_auto_flow(&self) -> GridAutoFlow { + (*self).grid_auto_flow() + } + #[inline(always)] + fn gap(&self) -> Size { + (*self).gap() + } + #[inline(always)] + fn align_content(&self) -> Option { + (*self).align_content() + } + #[inline(always)] + fn justify_content(&self) -> Option { + (*self).justify_content() + } + #[inline(always)] + fn align_items(&self) -> Option { + (*self).align_items() + } + #[inline(always)] + fn justify_items(&self) -> Option { + (*self).justify_items() + } +} + +#[cfg(feature = "grid")] +impl GridItemStyle for &'_ Style { + #[inline(always)] + fn grid_row(&self) -> Line { + self.grid_row + } + #[inline(always)] + fn grid_column(&self) -> Line { + self.grid_column + } + #[inline(always)] + fn align_self(&self) -> Option { + self.align_self + } + #[inline(always)] + fn justify_self(&self) -> Option { + self.justify_self + } +} + +#[cfg(feature = "grid")] +impl GridItemStyle for &'_ T { + #[inline(always)] + fn grid_row(&self) -> Line { + (*self).grid_row() + } + #[inline(always)] + fn grid_column(&self) -> Line { + (*self).grid_column() + } + #[inline(always)] + fn align_self(&self) -> Option { + (*self).align_self() + } + #[inline(always)] + fn justify_self(&self) -> Option { + (*self).justify_self() + } +} + #[cfg(test)] mod tests { use super::Style; diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 7a8961f3a..ccb5b625e 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -15,6 +15,15 @@ pub use node::NodeId; pub(crate) use traits::LayoutPartialTreeExt; pub use traits::{LayoutPartialTree, PrintTree, RoundTree, TraversePartialTree, TraverseTree}; +#[cfg(feature = "flexbox")] +pub use traits::LayoutFlexboxContainer; + +#[cfg(feature = "grid")] +pub use traits::LayoutGridContainer; + +#[cfg(feature = "block_layout")] +pub use traits::LayoutBlockContainer; + #[cfg(feature = "taffy_tree")] mod taffy_tree; #[cfg(feature = "taffy_tree")] diff --git a/src/tree/taffy_tree.rs b/src/tree/taffy_tree.rs index 7ec8deea5..788941fdf 100644 --- a/src/tree/taffy_tree.rs +++ b/src/tree/taffy_tree.rs @@ -14,15 +14,15 @@ use crate::tree::{ use crate::util::debug::{debug_log, debug_log_node}; use crate::util::sys::{new_vec_with_capacity, ChildrenVec, Vec}; -#[cfg(feature = "block_layout")] -use crate::compute::compute_block_layout; -#[cfg(feature = "flexbox")] -use crate::compute::compute_flexbox_layout; -#[cfg(feature = "grid")] -use crate::compute::compute_grid_layout; use crate::compute::{ compute_cached_layout, compute_hidden_layout, compute_leaf_layout, compute_root_layout, round_layout, }; +#[cfg(feature = "block_layout")] +use crate::{compute::compute_block_layout, LayoutBlockContainer}; +#[cfg(feature = "flexbox")] +use crate::{compute::compute_flexbox_layout, LayoutFlexboxContainer}; +#[cfg(feature = "grid")] +use crate::{compute::compute_grid_layout, LayoutGridContainer}; /// The error Taffy generates on invalid operations pub type TaffyResult = Result; @@ -271,9 +271,12 @@ where MeasureFunction: FnMut(Size>, Size, NodeId, Option<&mut NodeContext>, &Style) -> Size, { + type CoreContainerStyle<'a> = &'a Style where Self : 'a; + type CacheMut<'b> = &'b mut Cache where Self : 'b; + #[inline(always)] - fn get_style(&self, node: NodeId) -> &Style { - &self.taffy.nodes[node.into()].style + fn get_core_container_style(&self, node_id: NodeId) -> Self::CoreContainerStyle<'_> { + &self.taffy.nodes[node_id.into()].style } #[inline(always)] @@ -301,7 +304,7 @@ where // // If there was no cache match and a new result needs to be computed then that result will be added to the cache compute_cached_layout(self, node, inputs, |tree, node, inputs| { - let display_mode = tree.get_style(node).display; + let display_mode = tree.taffy.nodes[node.into()].style.display; let has_children = tree.child_count(node) > 0; debug_log!(display_mode); @@ -337,6 +340,66 @@ where } } +#[cfg(feature = "block_layout")] +impl<'t, NodeContext, MeasureFunction> LayoutBlockContainer for TaffyView<'t, NodeContext, MeasureFunction> +where + MeasureFunction: + FnMut(Size>, Size, NodeId, Option<&mut NodeContext>, &Style) -> Size, +{ + type BlockContainerStyle<'a> = &'a Style where Self: 'a; + type BlockItemStyle<'a> = &'a Style where Self: 'a; + + #[inline(always)] + fn get_block_container_style(&self, node_id: NodeId) -> Self::BlockContainerStyle<'_> { + self.get_core_container_style(node_id) + } + + #[inline(always)] + fn get_block_child_style(&self, child_node_id: NodeId) -> Self::BlockItemStyle<'_> { + self.get_core_container_style(child_node_id) + } +} + +#[cfg(feature = "flexbox")] +impl<'t, NodeContext, MeasureFunction> LayoutFlexboxContainer for TaffyView<'t, NodeContext, MeasureFunction> +where + MeasureFunction: + FnMut(Size>, Size, NodeId, Option<&mut NodeContext>, &Style) -> Size, +{ + type FlexboxContainerStyle<'a> = &'a Style where Self: 'a; + type FlexboxItemStyle<'a> = &'a Style where Self: 'a; + + #[inline(always)] + fn get_flexbox_container_style(&self, node_id: NodeId) -> Self::FlexboxContainerStyle<'_> { + &self.taffy.nodes[node_id.into()].style + } + + #[inline(always)] + fn get_flexbox_child_style(&self, child_node_id: NodeId) -> Self::FlexboxItemStyle<'_> { + &self.taffy.nodes[child_node_id.into()].style + } +} + +#[cfg(feature = "grid")] +impl<'t, NodeContext, MeasureFunction> LayoutGridContainer for TaffyView<'t, NodeContext, MeasureFunction> +where + MeasureFunction: + FnMut(Size>, Size, NodeId, Option<&mut NodeContext>, &Style) -> Size, +{ + type GridContainerStyle<'a> = &'a Style where Self: 'a; + type GridItemStyle<'a> = &'a Style where Self: 'a; + + #[inline(always)] + fn get_grid_container_style(&self, node_id: NodeId) -> Self::GridContainerStyle<'_> { + &self.taffy.nodes[node_id.into()].style + } + + #[inline(always)] + fn get_grid_child_style(&self, child_node_id: NodeId) -> Self::GridItemStyle<'_> { + &self.taffy.nodes[child_node_id.into()].style + } +} + // RoundTree impl for TaffyView impl<'t, NodeContext, MeasureFunction> RoundTree for TaffyView<'t, NodeContext, MeasureFunction> where diff --git a/src/tree/traits.rs b/src/tree/traits.rs index ce6688294..a549232ea 100644 --- a/src/tree/traits.rs +++ b/src/tree/traits.rs @@ -128,7 +128,12 @@ //! use super::{Cache, Layout, LayoutInput, LayoutOutput, NodeId, RequestedAxis, RunMode, SizingMode}; use crate::geometry::{AbsoluteAxis, Line, Size}; -use crate::style::{AvailableSpace, Style}; +use crate::style::{AvailableSpace, CoreStyle}; +#[cfg(feature = "flexbox")] +use crate::style::{FlexboxContainerStyle, FlexboxItemStyle}; +#[cfg(feature = "grid")] +use crate::style::{GridContainerStyle, GridItemStyle}; +use core::ops::{Deref, DerefMut}; /// This trait is Taffy's abstraction for downward tree traversal. /// However, this trait does *not* require access to any node's other than a single container node's immediate children unless you also intend to implement `TraverseTree`. @@ -157,14 +162,26 @@ pub trait TraverseTree: TraversePartialTree {} /// Note that this trait extends [`TraversePartialTree`] (not [`TraverseTree`]). Taffy's algorithm implementations have been designed such that they can be used for a laying out a single /// node that only has access to it's immediate children. pub trait LayoutPartialTree: TraversePartialTree { - /// Get a reference to the [`Style`] for this node. - fn get_style(&self, node_id: NodeId) -> &Style; + /// The style type representing the core container styles that all containers should have + /// Used when laying out the root node of a tree + type CoreContainerStyle<'a>: CoreStyle + where + Self: 'a; + + /// A mutable reference to the cache. This is an associated type to allow for different + /// types of mutable reference such as mutex or refcell guards + type CacheMut<'b>: Deref + DerefMut + where + Self: 'b; + + /// Get core style + fn get_core_container_style(&self, node_id: NodeId) -> Self::CoreContainerStyle<'_>; /// Set the node's unrounded layout fn set_unrounded_layout(&mut self, node_id: NodeId, layout: &Layout); /// Get a mutable reference to the [`Cache`] for this node. - fn get_cache_mut(&mut self, node_id: NodeId) -> &mut Cache; + fn get_cache_mut(&mut self, node_id: NodeId) -> Self::CacheMut<'_>; /// Compute the specified node's size or full layout given the specified constraints fn compute_child_layout(&mut self, node_id: NodeId, inputs: LayoutInput) -> LayoutOutput; @@ -191,6 +208,64 @@ pub trait PrintTree: TraverseTree { fn get_final_layout(&self, node_id: NodeId) -> &Layout; } +#[cfg(feature = "flexbox")] +/// Extends [`LayoutPartialTree`] with getters for the styles required for Flexbox layout +pub trait LayoutFlexboxContainer: LayoutPartialTree { + /// The style type representing the Flexbox container's styles + type FlexboxContainerStyle<'a>: FlexboxContainerStyle + where + Self: 'a; + /// The style type representing each Flexbox item's styles + type FlexboxItemStyle<'a>: FlexboxItemStyle + where + Self: 'a; + + /// Get the container's styles + fn get_flexbox_container_style(&self, node_id: NodeId) -> Self::FlexboxContainerStyle<'_>; + + /// Get the child's styles + fn get_flexbox_child_style(&self, child_node_id: NodeId) -> Self::FlexboxItemStyle<'_>; +} + +#[cfg(feature = "grid")] +/// Extends [`LayoutPartialTree`] with getters for the styles required for CSS Grid layout +pub trait LayoutGridContainer: LayoutPartialTree { + /// The style type representing the CSS Grid container's styles + type GridContainerStyle<'a>: GridContainerStyle + where + Self: 'a; + + /// The style type representing each CSS Grid item's styles + type GridItemStyle<'a>: GridItemStyle + where + Self: 'a; + + /// Get the container's styles + fn get_grid_container_style(&self, node_id: NodeId) -> Self::GridContainerStyle<'_>; + + /// Get the child's styles + fn get_grid_child_style(&self, child_node_id: NodeId) -> Self::GridItemStyle<'_>; +} + +#[cfg(feature = "block_layout")] +/// Extends [`LayoutPartialTree`] with getters for the styles required for CSS Block layout +pub trait LayoutBlockContainer: LayoutPartialTree { + /// The style type representing the CSS Block container's styles + type BlockContainerStyle<'a>: CoreStyle + where + Self: 'a; + /// The style type representing each CSS Block item's styles + type BlockItemStyle<'a>: CoreStyle + where + Self: 'a; + + /// Get the container's styles + fn get_block_container_style(&self, node_id: NodeId) -> Self::BlockContainerStyle<'_>; + + /// Get the child's styles + fn get_block_child_style(&self, child_node_id: NodeId) -> Self::BlockItemStyle<'_>; +} + // --- PRIVATE TRAITS /// A private trait which allows us to add extra convenience methods to types which implement