diff --git a/Cargo.toml b/Cargo.toml index 5b4ceec66..1dfcb1eed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -101,4 +101,5 @@ members = [ "scripts/format-fixtures", "scripts/import-yoga-tests", "benches", + "taffy_stylo", ] diff --git a/src/compute/grid/explicit_grid.rs b/src/compute/grid/explicit_grid.rs index d0e0b3afb..5ee2bfa84 100644 --- a/src/compute/grid/explicit_grid.rs +++ b/src/compute/grid/explicit_grid.rs @@ -15,12 +15,10 @@ 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: &impl GridContainerStyle, + template: &[TrackSizingFunction], inner_container_size: Size>, axis: AbsoluteAxis, ) -> u16 { - // Load the grid-template-rows or grid-template-columns definition (depending on the axis) - let template = style.grid_template_tracks(axis); - // If template contains no tracks, then there are trivially zero explicit tracks if template.is_empty() { return 0; @@ -294,8 +292,18 @@ mod test { fn explicit_grid_sizing_no_repeats() { let grid_style = (600.0, 600.0, 2, 4).into_grid(); let preferred_size = grid_style.size.map(|s| s.into_option()); - let width = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Horizontal); - let height = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Vertical); + let width = compute_explicit_grid_size_in_axis( + &grid_style, + &grid_style.grid_template_columns, + preferred_size, + AbsoluteAxis::Horizontal, + ); + let height = compute_explicit_grid_size_in_axis( + &grid_style, + &grid_style.grid_template_rows, + preferred_size, + AbsoluteAxis::Vertical, + ); assert_eq!(width, 2); assert_eq!(height, 4); } @@ -311,8 +319,18 @@ mod test { ..Default::default() }; let preferred_size = grid_style.size.map(|s| s.into_option()); - let width = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Horizontal); - let height = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Vertical); + let width = compute_explicit_grid_size_in_axis( + &grid_style, + &grid_style.grid_template_columns, + preferred_size, + AbsoluteAxis::Horizontal, + ); + let height = compute_explicit_grid_size_in_axis( + &grid_style, + &grid_style.grid_template_rows, + preferred_size, + AbsoluteAxis::Vertical, + ); assert_eq!(width, 3); assert_eq!(height, 4); } @@ -328,8 +346,18 @@ mod test { ..Default::default() }; let preferred_size = grid_style.size.map(|s| s.into_option()); - let width = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Horizontal); - let height = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Vertical); + let width = compute_explicit_grid_size_in_axis( + &grid_style, + &grid_style.grid_template_columns, + preferred_size, + AbsoluteAxis::Horizontal, + ); + let height = compute_explicit_grid_size_in_axis( + &grid_style, + &grid_style.grid_template_rows, + preferred_size, + AbsoluteAxis::Vertical, + ); assert_eq!(width, 3); assert_eq!(height, 4); } @@ -345,8 +373,18 @@ mod test { ..Default::default() }; let inner_container_size = Size { width: Some(120.0), height: Some(80.0) }; - let width = compute_explicit_grid_size_in_axis(&grid_style, inner_container_size, AbsoluteAxis::Horizontal); - let height = compute_explicit_grid_size_in_axis(&grid_style, inner_container_size, AbsoluteAxis::Vertical); + let width = compute_explicit_grid_size_in_axis( + &grid_style, + &grid_style.grid_template_columns, + inner_container_size, + AbsoluteAxis::Horizontal, + ); + let height = compute_explicit_grid_size_in_axis( + &grid_style, + &grid_style.grid_template_rows, + inner_container_size, + AbsoluteAxis::Vertical, + ); assert_eq!(width, 3); assert_eq!(height, 4); } @@ -362,8 +400,18 @@ mod test { ..Default::default() }; let inner_container_size = Size { width: Some(140.0), height: Some(90.0) }; - let width = compute_explicit_grid_size_in_axis(&grid_style, inner_container_size, AbsoluteAxis::Horizontal); - let height = compute_explicit_grid_size_in_axis(&grid_style, inner_container_size, AbsoluteAxis::Vertical); + let width = compute_explicit_grid_size_in_axis( + &grid_style, + &grid_style.grid_template_columns, + inner_container_size, + AbsoluteAxis::Horizontal, + ); + let height = compute_explicit_grid_size_in_axis( + &grid_style, + &grid_style.grid_template_rows, + inner_container_size, + AbsoluteAxis::Vertical, + ); assert_eq!(width, 4); assert_eq!(height, 5); } @@ -379,8 +427,18 @@ mod test { ..Default::default() }; let preferred_size = grid_style.size.map(|s| s.into_option()); - let width = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Horizontal); - let height = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Vertical); + let width = compute_explicit_grid_size_in_axis( + &grid_style, + &grid_style.grid_template_columns, + preferred_size, + AbsoluteAxis::Horizontal, + ); + let height = compute_explicit_grid_size_in_axis( + &grid_style, + &grid_style.grid_template_rows, + preferred_size, + AbsoluteAxis::Vertical, + ); assert_eq!(width, 4); // 2 repetitions * 2 repeated tracks = 4 tracks in total assert_eq!(height, 6); // 3 repetitions * 2 repeated tracks = 4 tracks in total } @@ -397,8 +455,18 @@ mod test { ..Default::default() }; let preferred_size = grid_style.size.map(|s| s.into_option()); - let width = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Horizontal); - let height = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Vertical); + let width = compute_explicit_grid_size_in_axis( + &grid_style, + &grid_style.grid_template_columns, + preferred_size, + AbsoluteAxis::Horizontal, + ); + let height = compute_explicit_grid_size_in_axis( + &grid_style, + &grid_style.grid_template_rows, + preferred_size, + AbsoluteAxis::Vertical, + ); assert_eq!(width, 2); // 2 tracks + 1 gap assert_eq!(height, 3); // 3 tracks + 2 gaps } @@ -414,8 +482,18 @@ mod test { ..Default::default() }; let preferred_size = grid_style.size.map(|s| s.into_option()); - let width = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Horizontal); - let height = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Vertical); + let width = compute_explicit_grid_size_in_axis( + &grid_style, + &grid_style.grid_template_columns, + preferred_size, + AbsoluteAxis::Horizontal, + ); + let height = compute_explicit_grid_size_in_axis( + &grid_style, + &grid_style.grid_template_rows, + preferred_size, + AbsoluteAxis::Vertical, + ); assert_eq!(width, 3); assert_eq!(height, 1); } @@ -432,8 +510,18 @@ mod test { ..Default::default() }; let preferred_size = grid_style.size.map(|s| s.into_option()); - let width = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Horizontal); - let height = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Vertical); + let width = compute_explicit_grid_size_in_axis( + &grid_style, + &grid_style.grid_template_columns, + preferred_size, + AbsoluteAxis::Horizontal, + ); + let height = compute_explicit_grid_size_in_axis( + &grid_style, + &grid_style.grid_template_rows, + preferred_size, + AbsoluteAxis::Vertical, + ); assert_eq!(width, 3); // 3 tracks + 2 gaps assert_eq!(height, 2); // 2 tracks + 1 gap } @@ -450,8 +538,18 @@ mod test { ..Default::default() }; let inner_container_size = Size { width: Some(100.0), height: Some(80.0) }; - let width = compute_explicit_grid_size_in_axis(&grid_style, inner_container_size, AbsoluteAxis::Horizontal); - let height = compute_explicit_grid_size_in_axis(&grid_style, inner_container_size, AbsoluteAxis::Vertical); + let width = compute_explicit_grid_size_in_axis( + &grid_style, + &grid_style.grid_template_columns, + inner_container_size, + AbsoluteAxis::Horizontal, + ); + let height = compute_explicit_grid_size_in_axis( + &grid_style, + &grid_style.grid_template_rows, + inner_container_size, + AbsoluteAxis::Vertical, + ); assert_eq!(width, 5); // 40px horizontal padding assert_eq!(height, 4); // 20px vertical padding } diff --git a/src/compute/grid/mod.rs b/src/compute/grid/mod.rs index c15c98e7b..5bba6bdfb 100644 --- a/src/compute/grid/mod.rs +++ b/src/compute/grid/mod.rs @@ -1,5 +1,7 @@ //! This module is a partial implementation of the CSS Grid Level 1 specification //! +use core::borrow::Borrow; + use crate::geometry::{AbsoluteAxis, AbstractAxis, InBothAbsAxis}; use crate::geometry::{Line, Point, Rect, Size}; use crate::style::{AlignItems, AlignSelf, AvailableSpace, Overflow, Position}; @@ -89,6 +91,13 @@ pub fn compute_grid_layout(tree: &mut impl LayoutGridContainer, node: NodeId, in let align_items = style.align_items(); let justify_items = style.justify_items(); + // Note: we avoid accessing the grid rows/columns methods more than once as this can + // cause an expensive-ish computation + let grid_template_columms = style.grid_template_columns(); + let grid_template_rows = style.grid_template_rows(); + let grid_auto_columms = style.grid_auto_columns(); + let grid_auto_rows = style.grid_auto_rows(); + let constrained_available_space = known_dimensions .or(preferred_size) .map(|size| size.map(AvailableSpace::Definite)) @@ -137,10 +146,18 @@ pub fn compute_grid_layout(tree: &mut impl LayoutGridContainer, node: NodeId, in .maybe_sub(content_box_inset.sum_axes()); // Exactly compute the number of rows and columns in the explicit grid. - let explicit_col_count = - compute_explicit_grid_size_in_axis(&style, auto_fit_container_size, AbsoluteAxis::Horizontal); - let explicit_row_count = - compute_explicit_grid_size_in_axis(&style, auto_fit_container_size, AbsoluteAxis::Vertical); + let explicit_col_count = compute_explicit_grid_size_in_axis( + &style, + grid_template_columms.borrow(), + auto_fit_container_size, + AbsoluteAxis::Horizontal, + ); + let explicit_row_count = compute_explicit_grid_size_in_axis( + &style, + grid_template_rows.borrow(), + auto_fit_container_size, + AbsoluteAxis::Vertical, + ); // 3. Implicit Grid: Estimate Track Counts // Estimate the number of rows and columns in the implicit grid (= the entire grid) @@ -181,20 +198,24 @@ pub fn compute_grid_layout(tree: &mut impl LayoutGridContainer, node: NodeId, in initialize_grid_tracks( &mut columns, final_col_counts, - style.grid_template_columns(), - style.grid_auto_columns(), + grid_template_columms.borrow(), + grid_auto_columms.borrow(), 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(), + grid_template_rows.borrow(), + grid_auto_rows.borrow(), style.gap().height, |row_index| cell_occupancy_matrix.row_is_occupied(row_index), ); + drop(grid_template_rows); + drop(grid_template_columms); + drop(grid_auto_rows); + drop(grid_auto_columms); drop(style); // 6. Track Sizing diff --git a/src/style/grid.rs b/src/style/grid.rs index d2597e69c..e13142794 100644 --- a/src/style/grid.rs +++ b/src/style/grid.rs @@ -4,31 +4,33 @@ use crate::compute::grid::{GridCoordinate, GridLine, OriginZeroLine}; use crate::geometry::{AbsoluteAxis, AbstractAxis, Line, MinMax, Size}; use crate::style_helpers::*; use crate::util::sys::GridTrackVec; +use core::borrow::Borrow; use core::cmp::{max, min}; use core::convert::Infallible; /// The set of styles required for a CSS Grid container pub trait GridContainerStyle: CoreStyle { + /// The type returned by grid_template_rows and grid_template_columns + type TemplateTrackList<'a>: Borrow<[TrackSizingFunction]> + where + Self: 'a; + /// The type returned by grid_auto_rows and grid_auto_columns + type AutoTrackList<'a>: Borrow<[NonRepeatedTrackSizingFunction]> + where + Self: 'a; + + // FIXME: re-add default implemenations for grid_{template,auto}_{rows,columns} once the + // associated_type_defaults feature (https://github.com/rust-lang/rust/issues/29661) is stabilised. + /// Defines the track sizing functions (heights) of the grid rows - #[inline(always)] - fn grid_template_rows(&self) -> &[TrackSizingFunction] { - &[] - } + fn grid_template_rows(&self) -> Self::TemplateTrackList<'_>; /// Defines the track sizing functions (widths) of the grid columns - #[inline(always)] - fn grid_template_columns(&self) -> &[TrackSizingFunction] { - &[] - } + fn grid_template_columns(&self) -> Self::TemplateTrackList<'_>; /// Defines the size of implicitly created rows - #[inline(always)] - fn grid_auto_rows(&self) -> &[NonRepeatedTrackSizingFunction] { - &[] - } + fn grid_auto_rows(&self) -> Self::AutoTrackList<'_>; /// Defined the size of implicitly created columns - #[inline(always)] - fn grid_auto_columns(&self) -> &[NonRepeatedTrackSizingFunction] { - &[] - } + fn grid_auto_columns(&self) -> Self::AutoTrackList<'_>; + /// Controls how items get placed into the grid for auto-placed items #[inline(always)] fn grid_auto_flow(&self) -> GridAutoFlow { @@ -66,7 +68,7 @@ pub trait GridContainerStyle: CoreStyle { /// Get a grid item's row or column placement depending on the axis passed #[inline(always)] - fn grid_template_tracks(&self, axis: AbsoluteAxis) -> &[TrackSizingFunction] { + fn grid_template_tracks(&self, axis: AbsoluteAxis) -> Self::TemplateTrackList<'_> { match axis { AbsoluteAxis::Horizontal => self.grid_template_columns(), AbsoluteAxis::Vertical => self.grid_template_rows(), diff --git a/src/style/mod.rs b/src/style/mod.rs index 9c35d202b..51563c0f2 100644 --- a/src/style/mod.rs +++ b/src/style/mod.rs @@ -777,6 +777,9 @@ impl FlexboxItemStyle for &'_ T { #[cfg(feature = "grid")] impl GridContainerStyle for Style { + type TemplateTrackList<'a> = &'a [TrackSizingFunction] where Self: 'a; + type AutoTrackList<'a> = &'a [NonRepeatedTrackSizingFunction] where Self: 'a; + #[inline(always)] fn grid_template_rows(&self) -> &[TrackSizingFunction] { &self.grid_template_rows @@ -821,20 +824,23 @@ impl GridContainerStyle for Style { #[cfg(feature = "grid")] impl GridContainerStyle for &'_ T { + type TemplateTrackList<'a> = T::TemplateTrackList<'a> where Self: 'a; + type AutoTrackList<'a> = T::AutoTrackList<'a> where Self: 'a; + #[inline(always)] - fn grid_template_rows(&self) -> &[TrackSizingFunction] { + fn grid_template_rows(&self) -> Self::TemplateTrackList<'_> { (*self).grid_template_rows() } #[inline(always)] - fn grid_template_columns(&self) -> &[TrackSizingFunction] { + fn grid_template_columns(&self) -> Self::TemplateTrackList<'_> { (*self).grid_template_columns() } #[inline(always)] - fn grid_auto_rows(&self) -> &[NonRepeatedTrackSizingFunction] { + fn grid_auto_rows(&self) -> Self::AutoTrackList<'_> { (*self).grid_auto_rows() } #[inline(always)] - fn grid_auto_columns(&self) -> &[NonRepeatedTrackSizingFunction] { + fn grid_auto_columns(&self) -> Self::AutoTrackList<'_> { (*self).grid_auto_columns() } #[inline(always)] diff --git a/taffy_stylo/Cargo.toml b/taffy_stylo/Cargo.toml new file mode 100644 index 000000000..02a73c8c9 --- /dev/null +++ b/taffy_stylo/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "taffy_stylo" +version = "0.0.1" +edition = "2021" +license = "MIT OR Apache-2.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +taffy = { path = ".." } +style = { git = "https://github.com/servo/stylo", branch = "2024-07-16", features = ["servo"] } +servo_arc = { git = "https://github.com/servo/stylo", branch = "2024-07-16" } \ No newline at end of file diff --git a/taffy_stylo/src/arc.rs b/taffy_stylo/src/arc.rs new file mode 100644 index 000000000..50f12cece --- /dev/null +++ b/taffy_stylo/src/arc.rs @@ -0,0 +1,261 @@ +use crate::convert; +use servo_arc::Arc; +use style::properties::generated::ComputedValues; + +pub struct TaffyStyloStyle(pub Arc); + +impl From> for TaffyStyloStyle { + fn from(value: Arc) -> Self { + Self(Arc::clone(&value)) + } +} + +impl taffy::CoreStyle for TaffyStyloStyle { + #[inline] + fn box_generation_mode(&self) -> taffy::BoxGenerationMode { + convert::box_generation_mode(self.0.get_box().display) + } + + #[inline] + fn is_block(&self) -> bool { + convert::is_block(self.0.get_box().display) + } + + #[inline] + fn box_sizing(&self) -> taffy::BoxSizing { + convert::box_sizing(self.0.get_position().box_sizing) + } + + #[inline] + fn overflow(&self) -> taffy::Point { + let box_styles = self.0.get_box(); + taffy::Point { x: convert::overflow(box_styles.overflow_x), y: convert::overflow(box_styles.overflow_y) } + } + + #[inline] + fn scrollbar_width(&self) -> f32 { + 0.0 + } + + #[inline] + fn position(&self) -> taffy::Position { + convert::position(self.0.get_box().position) + } + + #[inline] + fn inset(&self) -> taffy::Rect { + let position_styles = self.0.get_position(); + taffy::Rect { + left: convert::length_percentage_auto(&position_styles.left), + right: convert::length_percentage_auto(&position_styles.right), + top: convert::length_percentage_auto(&position_styles.top), + bottom: convert::length_percentage_auto(&position_styles.bottom), + } + } + + #[inline] + fn size(&self) -> taffy::Size { + let position_styles = self.0.get_position(); + taffy::Size { + width: convert::dimension(&position_styles.width), + height: convert::dimension(&position_styles.height), + } + } + + #[inline] + fn min_size(&self) -> taffy::Size { + let position_styles = self.0.get_position(); + taffy::Size { + width: convert::dimension(&position_styles.min_width), + height: convert::dimension(&position_styles.min_height), + } + } + + #[inline] + fn max_size(&self) -> taffy::Size { + let position_styles = self.0.get_position(); + taffy::Size { + width: convert::max_size_dimension(&position_styles.max_width), + height: convert::max_size_dimension(&position_styles.max_height), + } + } + + #[inline] + fn aspect_ratio(&self) -> Option { + convert::aspect_ratio(self.0.get_position().aspect_ratio) + } + + #[inline] + fn margin(&self) -> taffy::Rect { + let margin_styles = self.0.get_margin(); + taffy::Rect { + left: convert::length_percentage_auto(&margin_styles.margin_left), + right: convert::length_percentage_auto(&margin_styles.margin_right), + top: convert::length_percentage_auto(&margin_styles.margin_top), + bottom: convert::length_percentage_auto(&margin_styles.margin_bottom), + } + } + + #[inline] + fn padding(&self) -> taffy::Rect { + let padding_styles = self.0.get_padding(); + taffy::Rect { + left: convert::length_percentage(&padding_styles.padding_left.0), + right: convert::length_percentage(&padding_styles.padding_right.0), + top: convert::length_percentage(&padding_styles.padding_top.0), + bottom: convert::length_percentage(&padding_styles.padding_bottom.0), + } + } + + #[inline] + fn border(&self) -> taffy::Rect { + let border_styles = self.0.get_border(); + taffy::Rect { + left: taffy::LengthPercentage::Length(border_styles.border_left_width.to_f32_px()), + right: taffy::LengthPercentage::Length(border_styles.border_right_width.to_f32_px()), + top: taffy::LengthPercentage::Length(border_styles.border_top_width.to_f32_px()), + bottom: taffy::LengthPercentage::Length(border_styles.border_bottom_width.to_f32_px()), + } + } +} + +impl taffy::FlexboxContainerStyle for TaffyStyloStyle { + #[inline] + fn flex_direction(&self) -> taffy::FlexDirection { + convert::flex_direction(self.0.get_position().flex_direction) + } + + #[inline] + fn flex_wrap(&self) -> taffy::FlexWrap { + convert::flex_wrap(self.0.get_position().flex_wrap) + } + + #[inline] + fn gap(&self) -> taffy::Size { + let position_styles = self.0.get_position(); + taffy::Size { width: convert::gap(&position_styles.column_gap), height: convert::gap(&position_styles.row_gap) } + } + + #[inline] + fn align_content(&self) -> Option { + convert::content_alignment(self.0.get_position().align_content.0) + } + + #[inline] + fn align_items(&self) -> Option { + convert::item_alignment(self.0.get_position().align_items.0) + } + + #[inline] + fn justify_content(&self) -> Option { + convert::content_alignment(self.0.get_position().justify_content.0) + } +} + +impl taffy::FlexboxItemStyle for TaffyStyloStyle { + #[inline] + fn flex_basis(&self) -> taffy::Dimension { + convert::flex_basis(&self.0.get_position().flex_basis) + } + + #[inline] + fn flex_grow(&self) -> f32 { + self.0.get_position().flex_grow.0 + } + + #[inline] + fn flex_shrink(&self) -> f32 { + self.0.get_position().flex_shrink.0 + } + + #[inline] + fn align_self(&self) -> Option { + convert::item_alignment(self.0.get_position().align_self.0 .0) + } +} + +impl taffy::GridContainerStyle for TaffyStyloStyle { + type TemplateTrackList<'a> = Vec where Self: 'a; + type AutoTrackList<'a> = Vec where Self: 'a; + + #[inline] + fn grid_template_rows(&self) -> Self::TemplateTrackList<'_> { + convert::grid_template_tracks(&self.0.get_position().grid_template_rows) + } + + #[inline] + fn grid_template_columns(&self) -> Self::TemplateTrackList<'_> { + convert::grid_template_tracks(&self.0.get_position().grid_template_columns) + } + + #[inline] + fn grid_auto_rows(&self) -> Self::AutoTrackList<'_> { + convert::grid_auto_tracks(&self.0.get_position().grid_auto_rows) + } + + #[inline] + fn grid_auto_columns(&self) -> Self::AutoTrackList<'_> { + convert::grid_auto_tracks(&self.0.get_position().grid_auto_columns) + } + + #[inline] + fn grid_auto_flow(&self) -> taffy::GridAutoFlow { + convert::grid_auto_flow(self.0.get_position().grid_auto_flow) + } + + #[inline] + fn gap(&self) -> taffy::Size { + let position_styles = self.0.get_position(); + taffy::Size { width: convert::gap(&position_styles.column_gap), height: convert::gap(&position_styles.row_gap) } + } + + #[inline] + fn align_content(&self) -> Option { + convert::content_alignment(self.0.get_position().align_content.0) + } + + #[inline] + fn justify_content(&self) -> Option { + convert::content_alignment(self.0.get_position().justify_content.0) + } + + #[inline] + fn align_items(&self) -> Option { + convert::item_alignment(self.0.get_position().align_items.0) + } + + #[inline] + fn justify_items(&self) -> Option { + convert::item_alignment(self.0.get_position().justify_items.computed.0) + } +} + +impl taffy::GridItemStyle for TaffyStyloStyle { + #[inline] + fn grid_row(&self) -> taffy::Line { + let position_styles = self.0.get_position(); + taffy::Line { + start: convert::grid_line(&position_styles.grid_row_start), + end: convert::grid_line(&position_styles.grid_row_end), + } + } + + #[inline] + fn grid_column(&self) -> taffy::Line { + let position_styles = self.0.get_position(); + taffy::Line { + start: convert::grid_line(&position_styles.grid_column_start), + end: convert::grid_line(&position_styles.grid_column_end), + } + } + + #[inline] + fn align_self(&self) -> Option { + convert::item_alignment(self.0.get_position().align_self.0 .0) + } + + #[inline] + fn justify_self(&self) -> Option { + convert::item_alignment(self.0.get_position().justify_self.0 .0) + } +} diff --git a/taffy_stylo/src/borrowed.rs b/taffy_stylo/src/borrowed.rs new file mode 100644 index 000000000..d67753fe7 --- /dev/null +++ b/taffy_stylo/src/borrowed.rs @@ -0,0 +1,260 @@ +use crate::convert; +use style::properties::generated::ComputedValues; + +pub struct TaffyStyloStyleRef<'a>(pub &'a ComputedValues); + +impl<'a> From<&'a ComputedValues> for TaffyStyloStyleRef<'a> { + fn from(value: &'a ComputedValues) -> Self { + Self(value) + } +} + +impl taffy::CoreStyle for TaffyStyloStyleRef<'_> { + #[inline] + fn box_generation_mode(&self) -> taffy::BoxGenerationMode { + convert::box_generation_mode(self.0.get_box().display) + } + + #[inline] + fn is_block(&self) -> bool { + convert::is_block(self.0.get_box().display) + } + + #[inline] + fn box_sizing(&self) -> taffy::BoxSizing { + convert::box_sizing(self.0.get_position().box_sizing) + } + + #[inline] + fn overflow(&self) -> taffy::Point { + let box_styles = self.0.get_box(); + taffy::Point { x: convert::overflow(box_styles.overflow_x), y: convert::overflow(box_styles.overflow_y) } + } + + #[inline] + fn scrollbar_width(&self) -> f32 { + 0.0 + } + + #[inline] + fn position(&self) -> taffy::Position { + convert::position(self.0.get_box().position) + } + + #[inline] + fn inset(&self) -> taffy::Rect { + let position_styles = self.0.get_position(); + taffy::Rect { + left: convert::length_percentage_auto(&position_styles.left), + right: convert::length_percentage_auto(&position_styles.right), + top: convert::length_percentage_auto(&position_styles.top), + bottom: convert::length_percentage_auto(&position_styles.bottom), + } + } + + #[inline] + fn size(&self) -> taffy::Size { + let position_styles = self.0.get_position(); + taffy::Size { + width: convert::dimension(&position_styles.width), + height: convert::dimension(&position_styles.height), + } + } + + #[inline] + fn min_size(&self) -> taffy::Size { + let position_styles = self.0.get_position(); + taffy::Size { + width: convert::dimension(&position_styles.min_width), + height: convert::dimension(&position_styles.min_height), + } + } + + #[inline] + fn max_size(&self) -> taffy::Size { + let position_styles = self.0.get_position(); + taffy::Size { + width: convert::max_size_dimension(&position_styles.max_width), + height: convert::max_size_dimension(&position_styles.max_height), + } + } + + #[inline] + fn aspect_ratio(&self) -> Option { + convert::aspect_ratio(self.0.get_position().aspect_ratio) + } + + #[inline] + fn margin(&self) -> taffy::Rect { + let margin_styles = self.0.get_margin(); + taffy::Rect { + left: convert::length_percentage_auto(&margin_styles.margin_left), + right: convert::length_percentage_auto(&margin_styles.margin_right), + top: convert::length_percentage_auto(&margin_styles.margin_top), + bottom: convert::length_percentage_auto(&margin_styles.margin_bottom), + } + } + + #[inline] + fn padding(&self) -> taffy::Rect { + let padding_styles = self.0.get_padding(); + taffy::Rect { + left: convert::length_percentage(&padding_styles.padding_left.0), + right: convert::length_percentage(&padding_styles.padding_right.0), + top: convert::length_percentage(&padding_styles.padding_top.0), + bottom: convert::length_percentage(&padding_styles.padding_bottom.0), + } + } + + #[inline] + fn border(&self) -> taffy::Rect { + let border_styles = self.0.get_border(); + taffy::Rect { + left: taffy::LengthPercentage::Length(border_styles.border_left_width.to_f32_px()), + right: taffy::LengthPercentage::Length(border_styles.border_right_width.to_f32_px()), + top: taffy::LengthPercentage::Length(border_styles.border_top_width.to_f32_px()), + bottom: taffy::LengthPercentage::Length(border_styles.border_bottom_width.to_f32_px()), + } + } +} + +impl taffy::FlexboxContainerStyle for TaffyStyloStyleRef<'_> { + #[inline] + fn flex_direction(&self) -> taffy::FlexDirection { + convert::flex_direction(self.0.get_position().flex_direction) + } + + #[inline] + fn flex_wrap(&self) -> taffy::FlexWrap { + convert::flex_wrap(self.0.get_position().flex_wrap) + } + + #[inline] + fn gap(&self) -> taffy::Size { + let position_styles = self.0.get_position(); + taffy::Size { width: convert::gap(&position_styles.column_gap), height: convert::gap(&position_styles.row_gap) } + } + + #[inline] + fn align_content(&self) -> Option { + convert::content_alignment(self.0.get_position().align_content.0) + } + + #[inline] + fn align_items(&self) -> Option { + convert::item_alignment(self.0.get_position().align_items.0) + } + + #[inline] + fn justify_content(&self) -> Option { + convert::content_alignment(self.0.get_position().justify_content.0) + } +} + +impl taffy::FlexboxItemStyle for TaffyStyloStyleRef<'_> { + #[inline] + fn flex_basis(&self) -> taffy::Dimension { + convert::flex_basis(&self.0.get_position().flex_basis) + } + + #[inline] + fn flex_grow(&self) -> f32 { + self.0.get_position().flex_grow.0 + } + + #[inline] + fn flex_shrink(&self) -> f32 { + self.0.get_position().flex_shrink.0 + } + + #[inline] + fn align_self(&self) -> Option { + convert::item_alignment(self.0.get_position().align_self.0 .0) + } +} + +impl taffy::GridContainerStyle for TaffyStyloStyleRef<'_> { + type TemplateTrackList<'a> = Vec where Self: 'a; + type AutoTrackList<'a> = Vec where Self: 'a; + + #[inline] + fn grid_template_rows(&self) -> Self::TemplateTrackList<'_> { + convert::grid_template_tracks(&self.0.get_position().grid_template_rows) + } + + #[inline] + fn grid_template_columns(&self) -> Self::TemplateTrackList<'_> { + convert::grid_template_tracks(&self.0.get_position().grid_template_columns) + } + + #[inline] + fn grid_auto_rows(&self) -> Self::AutoTrackList<'_> { + convert::grid_auto_tracks(&self.0.get_position().grid_auto_rows) + } + + #[inline] + fn grid_auto_columns(&self) -> Self::AutoTrackList<'_> { + convert::grid_auto_tracks(&self.0.get_position().grid_auto_columns) + } + + #[inline] + fn grid_auto_flow(&self) -> taffy::GridAutoFlow { + convert::grid_auto_flow(self.0.get_position().grid_auto_flow) + } + + #[inline] + fn gap(&self) -> taffy::Size { + let position_styles = self.0.get_position(); + taffy::Size { width: convert::gap(&position_styles.column_gap), height: convert::gap(&position_styles.row_gap) } + } + + #[inline] + fn align_content(&self) -> Option { + convert::content_alignment(self.0.get_position().align_content.0) + } + + #[inline] + fn justify_content(&self) -> Option { + convert::content_alignment(self.0.get_position().justify_content.0) + } + + #[inline] + fn align_items(&self) -> Option { + convert::item_alignment(self.0.get_position().align_items.0) + } + + #[inline] + fn justify_items(&self) -> Option { + convert::item_alignment(self.0.get_position().justify_items.computed.0) + } +} + +impl taffy::GridItemStyle for TaffyStyloStyleRef<'_> { + #[inline] + fn grid_row(&self) -> taffy::Line { + let position_styles = self.0.get_position(); + taffy::Line { + start: convert::grid_line(&position_styles.grid_row_start), + end: convert::grid_line(&position_styles.grid_row_end), + } + } + + #[inline] + fn grid_column(&self) -> taffy::Line { + let position_styles = self.0.get_position(); + taffy::Line { + start: convert::grid_line(&position_styles.grid_column_start), + end: convert::grid_line(&position_styles.grid_column_end), + } + } + + #[inline] + fn align_self(&self) -> Option { + convert::item_alignment(self.0.get_position().align_self.0 .0) + } + + #[inline] + fn justify_self(&self) -> Option { + convert::item_alignment(self.0.get_position().justify_self.0 .0) + } +} diff --git a/taffy_stylo/src/convert.rs b/taffy_stylo/src/convert.rs new file mode 100644 index 000000000..66a7c6a03 --- /dev/null +++ b/taffy_stylo/src/convert.rs @@ -0,0 +1,312 @@ +// Module of type aliases so we can refer to stylo types with nicer names +mod stylo { + pub(crate) use style::computed_values::flex_direction::T as FlexDirection; + pub(crate) use style::computed_values::flex_wrap::T as FlexWrap; + pub(crate) use style::computed_values::grid_auto_flow::T as GridAutoFlow; + pub(crate) use style::properties::generated::longhands::box_sizing::computed_value::T as BoxSizing; + pub(crate) use style::properties::longhands::aspect_ratio::computed_value::T as AspectRatio; + pub(crate) use style::properties::longhands::position::computed_value::T as Position; + pub(crate) use style::values::specified::align::AlignFlags; + pub(crate) use style::values::specified::align::ContentDistribution; + // pub(crate) use style::properties::style_structs::{Margin, Padding}; + pub(crate) use style::values::computed::GridLine; + pub(crate) use style::values::computed::GridTemplateComponent; + pub(crate) use style::values::computed::ImplicitGridTracks; + pub(crate) use style::values::computed::LengthPercentage; + pub(crate) use style::values::generics::flex::GenericFlexBasis; + pub(crate) use style::values::generics::grid::RepeatCount; + pub(crate) use style::values::generics::grid::TrackBreadth; + pub(crate) use style::values::generics::grid::TrackListValue; + pub(crate) use style::values::generics::grid::TrackSize; + pub(crate) use style::values::generics::length::GenericLengthPercentageOrAuto; + pub(crate) use style::values::generics::length::GenericLengthPercentageOrNormal; + pub(crate) use style::values::generics::length::GenericMaxSize; + pub(crate) use style::values::generics::length::GenericSize; + pub(crate) use style::values::generics::position::PreferredRatio; + pub(crate) use style::values::generics::NonNegative; + pub(crate) use style::values::specified::box_::Display; + pub(crate) use style::values::specified::box_::DisplayInside; + pub(crate) use style::values::specified::box_::DisplayOutside; + pub(crate) use style::values::specified::box_::Overflow; + pub(crate) use style::values::specified::GenericGridTemplateComponent; + pub(crate) type LengthPercentageAuto = GenericLengthPercentageOrAuto; + pub(crate) type Size = GenericSize>; + pub(crate) type MaxSize = GenericMaxSize>; + pub(crate) type FlexBasis = GenericFlexBasis; + pub(crate) type Gap = GenericLengthPercentageOrNormal>; +} + +#[inline] +pub(crate) fn length_percentage(val: &stylo::LengthPercentage) -> taffy::LengthPercentage { + if let Some(length) = val.to_length() { + taffy::LengthPercentage::Length(length.px()) + } else if let Some(val) = val.to_percentage() { + taffy::LengthPercentage::Percent(val.0) + } else { + // TODO: Support calc + taffy::LengthPercentage::Percent(0.0) + } +} + +#[inline] +pub(crate) fn length_percentage_auto(val: &stylo::LengthPercentageAuto) -> taffy::LengthPercentageAuto { + match val { + stylo::LengthPercentageAuto::Auto => taffy::LengthPercentageAuto::Auto, + stylo::LengthPercentageAuto::LengthPercentage(val) => length_percentage(val).into(), + } +} + +#[inline] +pub(crate) fn dimension(val: &stylo::Size) -> taffy::Dimension { + match val { + stylo::Size::LengthPercentage(val) => length_percentage(&val.0).into(), + stylo::Size::Auto => taffy::Dimension::Auto, + // TODO: implement other values in Taffy (and servo configuration of stylo) + // _ => taffy::Dimension::Auto, + } +} + +#[inline] +pub(crate) fn max_size_dimension(val: &stylo::MaxSize) -> taffy::Dimension { + match val { + stylo::MaxSize::LengthPercentage(val) => length_percentage(&val.0).into(), + stylo::MaxSize::None => taffy::Dimension::Auto, + // TODO: implement other values in Taffy (and servo configuration of stylo) + // _ => taffy::Dimension::Auto, + } +} + +#[inline] +pub(crate) fn is_block(input: stylo::Display) -> bool { + matches!(input.outside(), stylo::DisplayOutside::Block) + && matches!(input.inside(), stylo::DisplayInside::Flow | stylo::DisplayInside::FlowRoot) +} + +#[inline] +pub(crate) fn box_generation_mode(input: stylo::Display) -> taffy::BoxGenerationMode { + match input.inside() { + stylo::DisplayInside::None => taffy::BoxGenerationMode::None, + // stylo::DisplayInside::Contents => display = taffy::BoxGenerationMode::Contents, + _ => taffy::BoxGenerationMode::Normal, + } +} + +#[inline] +pub(crate) fn box_sizing(input: stylo::BoxSizing) -> taffy::BoxSizing { + match input { + stylo::BoxSizing::BorderBox => taffy::BoxSizing::BorderBox, + stylo::BoxSizing::ContentBox => taffy::BoxSizing::ContentBox, + } +} + +#[inline] +pub(crate) fn position(input: stylo::Position) -> taffy::Position { + match input { + // TODO: support position:static + stylo::Position::Relative => taffy::Position::Relative, + stylo::Position::Static => taffy::Position::Relative, + + // TODO: support position:fixed and sticky + stylo::Position::Absolute => taffy::Position::Absolute, + stylo::Position::Fixed => taffy::Position::Absolute, + stylo::Position::Sticky => taffy::Position::Absolute, + } +} + +#[inline] +pub(crate) fn overflow(input: stylo::Overflow) -> taffy::Overflow { + // TODO: Enable Overflow::Clip in servo configuration of stylo + match input { + stylo::Overflow::Visible => taffy::Overflow::Visible, + stylo::Overflow::Hidden => taffy::Overflow::Hidden, + stylo::Overflow::Scroll => taffy::Overflow::Scroll, + // TODO: Support Overflow::Auto in Taffy + stylo::Overflow::Auto => taffy::Overflow::Scroll, + } +} + +#[inline] +pub(crate) fn aspect_ratio(input: stylo::AspectRatio) -> Option { + match input.ratio { + stylo::PreferredRatio::None => None, + stylo::PreferredRatio::Ratio(val) => Some(val.0 .0 / val.1 .0), + } +} + +#[inline] +pub(crate) fn gap(input: &stylo::Gap) -> taffy::LengthPercentage { + match input { + // For Flexbox and CSS Grid the "normal" value is 0px. This may need to be updated + // if we ever implement multi-column layout. + stylo::Gap::Normal => taffy::LengthPercentage::Length(0.0), + stylo::Gap::LengthPercentage(val) => length_percentage(&val.0), + } +} + +#[inline] +pub(crate) fn flex_basis(input: &stylo::FlexBasis) -> taffy::Dimension { + // TODO: Support flex-basis: content in Taffy + match input { + stylo::FlexBasis::Content => taffy::Dimension::Auto, + stylo::FlexBasis::Size(size) => dimension(size), + } +} + +#[inline] +pub(crate) fn flex_direction(input: stylo::FlexDirection) -> taffy::FlexDirection { + match input { + stylo::FlexDirection::Row => taffy::FlexDirection::Row, + stylo::FlexDirection::RowReverse => taffy::FlexDirection::RowReverse, + stylo::FlexDirection::Column => taffy::FlexDirection::Column, + stylo::FlexDirection::ColumnReverse => taffy::FlexDirection::ColumnReverse, + } +} + +#[inline] +pub(crate) fn flex_wrap(input: stylo::FlexWrap) -> taffy::FlexWrap { + match input { + stylo::FlexWrap::Wrap => taffy::FlexWrap::Wrap, + stylo::FlexWrap::WrapReverse => taffy::FlexWrap::WrapReverse, + stylo::FlexWrap::Nowrap => taffy::FlexWrap::NoWrap, + } +} + +#[inline] +pub(crate) fn content_alignment(input: stylo::ContentDistribution) -> Option { + match input.primary().value() { + stylo::AlignFlags::NORMAL => None, + stylo::AlignFlags::AUTO => None, + stylo::AlignFlags::START => Some(taffy::AlignContent::Start), + stylo::AlignFlags::END => Some(taffy::AlignContent::End), + stylo::AlignFlags::FLEX_START => Some(taffy::AlignContent::FlexStart), + stylo::AlignFlags::STRETCH => Some(taffy::AlignContent::Stretch), + stylo::AlignFlags::FLEX_END => Some(taffy::AlignContent::FlexEnd), + stylo::AlignFlags::CENTER => Some(taffy::AlignContent::Center), + stylo::AlignFlags::SPACE_BETWEEN => Some(taffy::AlignContent::SpaceBetween), + stylo::AlignFlags::SPACE_AROUND => Some(taffy::AlignContent::SpaceAround), + stylo::AlignFlags::SPACE_EVENLY => Some(taffy::AlignContent::SpaceEvenly), + // Should never be hit. But no real reason to panic here. + _ => None, + } +} + +#[inline] +pub(crate) fn item_alignment(input: stylo::AlignFlags) -> Option { + match input.value() { + stylo::AlignFlags::NORMAL => None, + stylo::AlignFlags::AUTO => None, + stylo::AlignFlags::STRETCH => Some(taffy::AlignItems::Stretch), + stylo::AlignFlags::FLEX_START => Some(taffy::AlignItems::FlexStart), + stylo::AlignFlags::FLEX_END => Some(taffy::AlignItems::FlexEnd), + stylo::AlignFlags::START => Some(taffy::AlignItems::Start), + stylo::AlignFlags::END => Some(taffy::AlignItems::End), + stylo::AlignFlags::CENTER => Some(taffy::AlignItems::Center), + stylo::AlignFlags::BASELINE => Some(taffy::AlignItems::Baseline), + // Should never be hit. But no real reason to panic here. + _ => None, + } +} + +#[inline] +pub(crate) fn grid_auto_flow(input: stylo::GridAutoFlow) -> taffy::GridAutoFlow { + let is_row = input.contains(stylo::GridAutoFlow::ROW); + let is_dense = input.contains(stylo::GridAutoFlow::DENSE); + + match (is_row, is_dense) { + (true, false) => taffy::GridAutoFlow::Row, + (true, true) => taffy::GridAutoFlow::RowDense, + (false, false) => taffy::GridAutoFlow::Column, + (false, true) => taffy::GridAutoFlow::ColumnDense, + } +} + +#[inline] +pub(crate) fn grid_line(input: &stylo::GridLine) -> taffy::GridPlacement { + if input.is_auto() { + taffy::GridPlacement::Auto + } else if input.is_span { + taffy::style_helpers::span(input.line_num.try_into().unwrap()) + } else if input.line_num == 0 { + taffy::GridPlacement::Auto + } else { + taffy::style_helpers::line(input.line_num.try_into().unwrap()) + } +} + +#[inline] +pub(crate) fn grid_template_tracks(input: &stylo::GridTemplateComponent) -> Vec { + match input { + stylo::GenericGridTemplateComponent::None => Vec::new(), + stylo::GenericGridTemplateComponent::TrackList(list) => list + .values + .iter() + .map(|track| match track { + stylo::TrackListValue::TrackSize(size) => taffy::TrackSizingFunction::Single(track_size(size)), + stylo::TrackListValue::TrackRepeat(repeat) => taffy::TrackSizingFunction::Repeat( + track_repeat(repeat.count), + repeat.track_sizes.iter().map(track_size).collect(), + ), + }) + .collect(), + + // TODO: Implement subgrid and masonry + stylo::GenericGridTemplateComponent::Subgrid(_) => Vec::new(), + stylo::GenericGridTemplateComponent::Masonry => Vec::new(), + } +} + +#[inline] +pub(crate) fn grid_auto_tracks(input: &stylo::ImplicitGridTracks) -> Vec { + input.0.iter().map(track_size).collect() +} + +#[inline] +pub(crate) fn track_repeat(input: stylo::RepeatCount) -> taffy::GridTrackRepetition { + match input { + stylo::RepeatCount::Number(val) => taffy::GridTrackRepetition::Count(val.try_into().unwrap()), + stylo::RepeatCount::AutoFill => taffy::GridTrackRepetition::AutoFill, + stylo::RepeatCount::AutoFit => taffy::GridTrackRepetition::AutoFit, + } +} + +#[inline] +pub(crate) fn track_size(input: &stylo::TrackSize) -> taffy::NonRepeatedTrackSizingFunction { + match input { + stylo::TrackSize::Breadth(breadth) => taffy::MinMax { min: min_track(breadth), max: max_track(breadth) }, + stylo::TrackSize::Minmax(min, max) => taffy::MinMax { min: min_track(min), max: max_track(max) }, + stylo::TrackSize::FitContent(limit) => taffy::MinMax { + min: taffy::MinTrackSizingFunction::Auto, + max: taffy::MaxTrackSizingFunction::FitContent(match limit { + stylo::TrackBreadth::Breadth(lp) => length_percentage(lp), + + // Are these valid? Taffy doesn't support this in any case + stylo::TrackBreadth::Fr(_) => unreachable!(), + stylo::TrackBreadth::Auto => unreachable!(), + stylo::TrackBreadth::MinContent => unreachable!(), + stylo::TrackBreadth::MaxContent => unreachable!(), + }), + }, + } +} + +#[inline] +pub(crate) fn min_track(input: &stylo::TrackBreadth) -> taffy::MinTrackSizingFunction { + match input { + stylo::TrackBreadth::Breadth(lp) => taffy::MinTrackSizingFunction::Fixed(length_percentage(lp)), + stylo::TrackBreadth::Fr(_) => taffy::MinTrackSizingFunction::Auto, + stylo::TrackBreadth::Auto => taffy::MinTrackSizingFunction::Auto, + stylo::TrackBreadth::MinContent => taffy::MinTrackSizingFunction::MinContent, + stylo::TrackBreadth::MaxContent => taffy::MinTrackSizingFunction::MaxContent, + } +} + +#[inline] +pub(crate) fn max_track(input: &stylo::TrackBreadth) -> taffy::MaxTrackSizingFunction { + match input { + stylo::TrackBreadth::Breadth(lp) => taffy::MaxTrackSizingFunction::Fixed(length_percentage(lp)), + stylo::TrackBreadth::Fr(val) => taffy::MaxTrackSizingFunction::Fraction(*val), + stylo::TrackBreadth::Auto => taffy::MaxTrackSizingFunction::Auto, + stylo::TrackBreadth::MinContent => taffy::MaxTrackSizingFunction::MinContent, + stylo::TrackBreadth::MaxContent => taffy::MaxTrackSizingFunction::MaxContent, + } +} diff --git a/taffy_stylo/src/lib.rs b/taffy_stylo/src/lib.rs new file mode 100644 index 000000000..c85bb7472 --- /dev/null +++ b/taffy_stylo/src/lib.rs @@ -0,0 +1,8 @@ +//! Conversion functions from Stylo types to Taffy types + +pub(crate) mod arc; +pub(crate) mod borrowed; +pub(crate) mod convert; + +pub use arc::TaffyStyloStyle; +pub use borrowed::TaffyStyloStyleRef;