diff options
author | Martin Robinson <mrobinson@igalia.com> | 2025-01-16 16:45:06 +0100 |
---|---|---|
committer | Martin Robinson <mrobinson@igalia.com> | 2025-01-16 16:47:08 +0100 |
commit | 7c61800653b2f2eb5051c2bad17ebfcb7ae130e5 (patch) | |
tree | e6afcb2a00a9d367d40f1a49511ab5097deee256 | |
parent | d58aa7fc0475e756d3b3cde13977a599339e495a (diff) | |
download | servo-7c61800653b2f2eb5051c2bad17ebfcb7ae130e5.tar.gz servo-7c61800653b2f2eb5051c2bad17ebfcb7ae130e5.zip |
layout: Make table column constraint distribution more genericmake-column-distribution-more-generic
This will be useful for distributing colspan constraints to their
columns
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
-rw-r--r-- | components/layout_2020/table/layout.rs | 118 |
1 files changed, 59 insertions, 59 deletions
diff --git a/components/layout_2020/table/layout.rs b/components/layout_2020/table/layout.rs index 91af3d59447..e14ea3a787c 100644 --- a/components/layout_2020/table/layout.rs +++ b/components/layout_2020/table/layout.rs @@ -107,6 +107,8 @@ struct RowLayout { struct ColumnLayout { constrained: bool, has_originating_cells: bool, + content_sizes: ContentSizes, + percentage: Percentage, } /// A calculated collapsed border. @@ -208,7 +210,6 @@ pub(crate) struct TableLayout<'a> { /// width that we will be able to allocate to the columns. assignable_width: Au, final_table_height: Au, - column_measures: Vec<CellOrTrackMeasure>, distributed_column_widths: Vec<Au>, row_sizes: Vec<Au>, /// The accumulated baseline of each row, relative to the top of the row. @@ -249,7 +250,6 @@ impl<'a> TableLayout<'a> { table_width: Au::zero(), assignable_width: Au::zero(), final_table_height: Au::zero(), - column_measures: Vec::new(), distributed_column_widths: Vec::new(), row_sizes: Vec::new(), row_baselines: Vec::new(), @@ -460,8 +460,6 @@ impl<'a> TableLayout<'a> { /// This is an implementation of *Computing Column Measures* from /// <https://drafts.csswg.org/css-tables/#computing-column-measures>. fn compute_column_measures(&mut self, writing_mode: WritingMode) { - let mut column_measures = Vec::new(); - // Compute the column measures only taking into account cells with colspan == 1. // This is the base case that will be used to iteratively account for cells with // larger colspans afterward. @@ -494,9 +492,13 @@ impl<'a> TableLayout<'a> { // TODO: Take into account changes to this computation for fixed table layout. let mut next_span_n = usize::MAX; for column_index in 0..self.table.size.width { - let mut column_measure = self + let column = &mut self.columns[column_index]; + + let column_measure = self .table .get_column_measure_for_column_at_index(writing_mode, column_index); + column.content_sizes = column_measure.content_sizes; + column.percentage = column_measure.percentage; for row_index in 0..self.table.size.height { let coords = TableSlotCoordinates::new(column_index, row_index); @@ -512,22 +514,20 @@ impl<'a> TableLayout<'a> { // This takes the max of `min_content`, `max_content`, and // intrinsic percentage width as described above. let cell_measure = &self.cell_measures[row_index][column_index].inline; - column_measure - .content_sizes - .max_assign(cell_measure.content_sizes); - column_measure.percentage = + column.content_sizes.max_assign(cell_measure.content_sizes); + column.percentage = Percentage(column_measure.percentage.0.max(cell_measure.percentage.0)); } - - column_measures.push(column_measure); } // Now we have the base computation complete, so iteratively take into account cells // with higher colspan. Using `next_span_n` we can skip over span counts that don't // correspond to any cells. while next_span_n < usize::MAX { - (next_span_n, column_measures) = self - .compute_content_sizes_for_columns_with_span_up_to_n(next_span_n, &column_measures); + let (new_next_span_n, new_columns) = + self.compute_content_sizes_for_columns_with_span_up_to_n(next_span_n); + self.columns = new_columns; + next_span_n = new_next_span_n; } // > intrinsic percentage width of a column: @@ -537,31 +537,28 @@ impl<'a> TableLayout<'a> { // > * 100% minus the sum of the intrinsic percentage width of all prior columns in // > the table (further left when direction is "ltr" (right for "rtl")) let mut total_intrinsic_percentage_width = 0.; - for column_measure in column_measures.iter_mut() { - let final_intrinsic_percentage_width = column_measure + for column in self.columns.iter_mut() { + let final_intrinsic_percentage_width = column .percentage .0 .min(1. - total_intrinsic_percentage_width); total_intrinsic_percentage_width += final_intrinsic_percentage_width; - column_measure.percentage = Percentage(final_intrinsic_percentage_width); + column.percentage = Percentage(final_intrinsic_percentage_width); } - - self.column_measures = column_measures; } fn compute_content_sizes_for_columns_with_span_up_to_n( &self, n: usize, - old_column_measures: &[CellOrTrackMeasure], - ) -> (usize, Vec<CellOrTrackMeasure>) { + ) -> (usize, Vec<ColumnLayout>) { let mut next_span_n = usize::MAX; - let mut new_content_sizes_for_columns = Vec::new(); + let mut new_columns = Vec::new(); let border_spacing = self.table.border_spacing(); for column_index in 0..self.table.size.width { - let old_column_measure = &old_column_measures[column_index]; - let mut new_column_content_sizes = old_column_measure.content_sizes; - let mut new_column_intrinsic_percentage_width = old_column_measure.percentage; + let old_column = &self.columns[column_index]; + let mut new_column_content_sizes = old_column.content_sizes; + let mut new_column_intrinsic_percentage_width = old_column.percentage; for row_index in 0..self.table.size.height { let coords = TableSlotCoordinates::new(column_index, row_index); @@ -587,11 +584,11 @@ impl<'a> TableLayout<'a> { let baseline_content_sizes: ContentSizes = columns_spanned.clone().fold( ContentSizes::zero(), |total: ContentSizes, spanned_column_index| { - total + old_column_measures[spanned_column_index].content_sizes + total + self.columns[spanned_column_index].content_sizes }, ); - let old_column_content_size = old_column_measure.content_sizes; + let old_column_content_size = old_column.content_sizes; // > **min-content width of a column based on cells of span up to N (N > 1)** // > @@ -711,7 +708,7 @@ impl<'a> TableLayout<'a> { // > Otherwise, it is the largest of the contributions of the cells in the column // > whose colSpan is N, where the contribution of a cell is the result of taking // > the following steps: - if old_column_measure.percentage.0 <= 0. && cell_measures.percentage.0 != 0. { + if old_column.percentage.0 <= 0. && cell_measures.percentage.0 != 0. { // > 1. Start with the percentage contribution of the cell. // > 2. Subtract the intrinsic percentage width of the column based on cells // > of span up to N-1 of all columns that the cell spans. If this gives a @@ -720,7 +717,7 @@ impl<'a> TableLayout<'a> { let other_column_percentages_sum = (columns_spanned).fold(0., |sum, spanned_column_index| { let spanned_column_percentage = - old_column_measures[spanned_column_index].percentage; + self.columns[spanned_column_index].percentage; if spanned_column_percentage.0 == 0. { spanned_columns_with_zero += 1; } @@ -745,12 +742,12 @@ impl<'a> TableLayout<'a> { Percentage(new_column_intrinsic_percentage_width.0.max(step_3)); } } - new_content_sizes_for_columns.push(CellOrTrackMeasure { - content_sizes: new_column_content_sizes, - percentage: new_column_intrinsic_percentage_width, - }); + let mut new_column = old_column.clone(); + new_column.content_sizes = new_column_content_sizes; + new_column.percentage = new_column_intrinsic_percentage_width; + new_columns.push(new_column); } - (next_span_n, new_content_sizes_for_columns) + (next_span_n, new_columns) } /// Compute the GRIDMIN and GRIDMAX. @@ -771,7 +768,7 @@ impl<'a> TableLayout<'a> { // > The row/column-grid width maximum (GRIDMAX) width is the sum of the max-content width of // > all the columns plus cell spacing or borders. let mut grid_min_max = self - .column_measures + .columns .iter() .fold(ContentSizes::zero(), |result, measure| { result + measure.content_sizes @@ -849,7 +846,11 @@ impl<'a> TableLayout<'a> { /// Distribute width to columns, performing step 2.4 of table layout from /// <https://drafts.csswg.org/css-tables/#table-layout-algorithm>. - fn distribute_width_to_columns(&self) -> Vec<Au> { + fn distribute_width_to_columns( + &self, + target_inline_size: Au, + columns: &[ColumnLayout], + ) -> Vec<Au> { // No need to do anything if there is no column. // Note that tables without rows may still have columns. if self.table.size.width.is_zero() { @@ -889,18 +890,17 @@ impl<'a> TableLayout<'a> { let mut min_content_specified_sizing_guesses = Vec::new(); let mut max_content_sizing_guesses = Vec::new(); - for column_idx in 0..self.table.size.width { - let column_measure = &self.column_measures[column_idx]; - let min_content_width = column_measure.content_sizes.min_content; - let max_content_width = column_measure.content_sizes.max_content; - let constrained = self.columns[column_idx].constrained; + for column in columns { + let min_content_width = column.content_sizes.min_content; + let max_content_width = column.content_sizes.max_content; + let constrained = column.constrained; let ( min_content_percentage_sizing_guess, min_content_specified_sizing_guess, max_content_sizing_guess, - ) = if !column_measure.percentage.is_zero() { - let resolved = self.assignable_width.scale_by(column_measure.percentage.0); + ) = if !column.percentage.is_zero() { + let resolved = target_inline_size.scale_by(column.percentage.0); let percent_guess = min_content_width.max(resolved); (percent_guess, percent_guess, percent_guess) } else if constrained { @@ -927,7 +927,7 @@ impl<'a> TableLayout<'a> { } let max_content_sizing_sum = sum(&max_content_sizing_guesses); - if self.assignable_width >= max_content_sizing_sum { + if target_inline_size >= max_content_sizing_sum { self.distribute_extra_width_to_columns( &mut max_content_sizing_guesses, max_content_sizing_sum, @@ -935,27 +935,26 @@ impl<'a> TableLayout<'a> { return max_content_sizing_guesses; } let min_content_specified_sizing_sum = sum(&min_content_specified_sizing_guesses); - if self.assignable_width == min_content_specified_sizing_sum { + if target_inline_size == min_content_specified_sizing_sum { return min_content_specified_sizing_guesses; } let min_content_percentage_sizing_sum = sum(&min_content_percentage_sizing_guesses); - if self.assignable_width == min_content_percentage_sizing_sum { + if target_inline_size == min_content_percentage_sizing_sum { return min_content_percentage_sizing_guesses; } let min_content_sizes_sum = sum(&min_content_sizing_guesses); - if self.assignable_width <= min_content_sizes_sum { + if target_inline_size <= min_content_sizes_sum { return min_content_sizing_guesses; } - let bounds = |sum_a, sum_b| self.assignable_width > sum_a && self.assignable_width < sum_b; + let bounds = |sum_a, sum_b| target_inline_size > sum_a && target_inline_size < sum_b; let blend = |a: &[Au], sum_a: Au, b: &[Au], sum_b: Au| { // First convert the Au units to f32 in order to do floating point division. - let weight_a = - (self.assignable_width - sum_b).to_f32_px() / (sum_a - sum_b).to_f32_px(); + let weight_a = (target_inline_size - sum_b).to_f32_px() / (sum_a - sum_b).to_f32_px(); let weight_b = 1.0 - weight_a; - let mut remaining_assignable_width = self.assignable_width; + let mut remaining_assignable_width = target_inline_size; let mut widths: Vec<Au> = a .iter() .zip(b.iter()) @@ -987,7 +986,7 @@ impl<'a> TableLayout<'a> { widths[0] += remaining_assignable_width; } - debug_assert!(widths.iter().sum::<Au>() == self.assignable_width); + debug_assert!(widths.iter().sum::<Au>() == target_inline_size); widths }; @@ -1036,17 +1035,17 @@ impl<'a> TableLayout<'a> { let is_constrained = |column_index: &usize| self.columns[*column_index].constrained; let is_unconstrained = |column_index: &usize| !is_constrained(column_index); let has_percent_greater_than_zero = - |column_index: &usize| self.column_measures[*column_index].percentage.0 > 0.; + |column_index: &usize| self.columns[*column_index].percentage.0 > 0.; let has_percent_zero = |column_index: &usize| !has_percent_greater_than_zero(column_index); let has_max_content = |column_index: &usize| { - !self.column_measures[*column_index] + !self.columns[*column_index] .content_sizes .max_content .is_zero() }; let max_content_sum = - |column_index: usize| self.column_measures[column_index].content_sizes.max_content; + |column_index: usize| self.columns[column_index].content_sizes.max_content; // > If there are non-constrained columns that have originating cells with intrinsic // > percentage width of 0% and with nonzero max-content width (aka the columns allowed to @@ -1066,7 +1065,7 @@ impl<'a> TableLayout<'a> { if total_max_content_width != Au::zero() { for column_index in unconstrained_max_content_columns { column_sizes[column_index] += extra_inline_size.scale_by( - self.column_measures[column_index] + self.columns[column_index] .content_sizes .max_content .to_f32_px() / @@ -1114,7 +1113,7 @@ impl<'a> TableLayout<'a> { if total_max_content_width != Au::zero() { for column_index in constrained_max_content_columns { column_sizes[column_index] += extra_inline_size.scale_by( - self.column_measures[column_index] + self.columns[column_index] .content_sizes .max_content .to_f32_px() / @@ -1132,12 +1131,12 @@ impl<'a> TableLayout<'a> { let columns_with_percentage = all_columns.clone().filter(has_percent_greater_than_zero); let total_percent = columns_with_percentage .clone() - .map(|column_index| self.column_measures[column_index].percentage.0) + .map(|column_index| self.columns[column_index].percentage.0) .sum::<f32>(); if total_percent > 0. { for column_index in columns_with_percentage { column_sizes[column_index] += extra_inline_size - .scale_by(self.column_measures[column_index].percentage.0 / total_percent); + .scale_by(self.columns[column_index].percentage.0 / total_percent); } return; } @@ -1858,7 +1857,8 @@ impl<'a> TableLayout<'a> { containing_block_for_logical_conversion: &ContainingBlock, containing_block_for_children: &ContainingBlock, ) -> BoxFragment { - self.distributed_column_widths = self.distribute_width_to_columns(); + self.distributed_column_widths = + self.distribute_width_to_columns(self.assignable_width, &self.columns); self.layout_cells_in_row( layout_context, containing_block_for_children, |