diff options
author | Oriol Brufau <obrufau@igalia.com> | 2024-09-16 10:20:22 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-16 08:20:22 +0000 |
commit | b12cebd1ac3b2bc809e5ac69f708cf61b515590d (patch) | |
tree | 6fc8bae0f36ce8f267608cf76244689a280fe7a2 /components/layout_2020/table/layout.rs | |
parent | 679afe519591c3c36036154afb1e9b6d73ffa1ac (diff) | |
download | servo-b12cebd1ac3b2bc809e5ac69f708cf61b515590d.tar.gz servo-b12cebd1ac3b2bc809e5ac69f708cf61b515590d.zip |
Small improvements for table border collapse (#33452)
We were only collapsing the borders from adjacent cells. This patch also
handles the borders from rows, row groups, columns, and column groups.
Additionally, it takes the border style into account in order to decide
which border wins.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Diffstat (limited to 'components/layout_2020/table/layout.rs')
-rw-r--r-- | components/layout_2020/table/layout.rs | 121 |
1 files changed, 107 insertions, 14 deletions
diff --git a/components/layout_2020/table/layout.rs b/components/layout_2020/table/layout.rs index e52bab12400..ed4aef17bf4 100644 --- a/components/layout_2020/table/layout.rs +++ b/components/layout_2020/table/layout.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use core::cmp::Ordering; use std::ops::Range; use app_units::{Au, MAX_AU}; @@ -16,7 +17,9 @@ use style::computed_values::table_layout::T as TableLayoutMode; use style::computed_values::visibility::T as Visibility; use style::logical_geometry::WritingMode; use style::properties::ComputedValues; -use style::values::computed::{LengthPercentage as ComputedLengthPercentage, Percentage}; +use style::values::computed::{ + BorderStyle, LengthPercentage as ComputedLengthPercentage, Percentage, +}; use style::values::generics::box_::{GenericVerticalAlign as VerticalAlign, VerticalAlignKeyword}; use style::values::generics::length::GenericLengthPercentageOrAuto::{Auto, LengthPercentage}; use style::Zero; @@ -92,11 +95,79 @@ struct ColumnLayout { has_originating_cells: bool, } +/// A calculated collapsed border. +#[derive(Clone, Debug, Eq, PartialEq)] +struct CollapsedBorder { + style: BorderStyle, + width: Au, +} + +impl Default for CollapsedBorder { + fn default() -> Self { + Self::new(BorderStyle::None, Au::zero()) + } +} + +impl CollapsedBorder { + fn new(style: BorderStyle, width: Au) -> Self { + Self { style, width } + } + + fn from_style(style: &ComputedValues, writing_mode: WritingMode) -> LogicalSides<Self> { + let border_style = style.border_style(writing_mode); + let border_width = style.border_width(writing_mode); + LogicalSides { + inline_start: Self::new(border_style.inline_start, border_width.inline_start), + inline_end: Self::new(border_style.inline_end, border_width.inline_end), + block_start: Self::new(border_style.block_start, border_width.block_start), + block_end: Self::new(border_style.block_end, border_width.block_end), + } + } + + fn max_assign(&mut self, other: Self) { + if *self < other { + *self = other; + } + } +} + +/// <https://drafts.csswg.org/css-tables/#border-specificity> +/// > Given two borders styles, the border style having the most specificity is the border style which… +/// > 1. … has the value "hidden" as border-style, if only one does +/// > 2. … has the biggest border-width, once converted into css pixels +/// > 3. … has the border-style which comes first in the following list: +/// > double, solid, dashed, dotted, ridge, outset, groove, inset, none +impl Ord for CollapsedBorder { + fn cmp(&self, other: &Self) -> Ordering { + let style_specificity = |border: &Self| match border.style { + BorderStyle::None => 0, + BorderStyle::Inset => 1, + BorderStyle::Groove => 2, + BorderStyle::Outset => 3, + BorderStyle::Ridge => 4, + BorderStyle::Dotted => 5, + BorderStyle::Dashed => 6, + BorderStyle::Solid => 7, + BorderStyle::Double => 8, + BorderStyle::Hidden => 9, + }; + ((self.style == BorderStyle::Hidden).cmp(&(other.style == BorderStyle::Hidden))) + .then_with(|| self.width.cmp(&other.width)) + .then_with(|| style_specificity(self).cmp(&style_specificity(other))) + } +} + +impl PartialOrd for CollapsedBorder { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + /// The calculated collapsed borders. #[derive(Clone, Debug, Default)] struct CollapsedBorders { - block: Vec<Au>, - inline: Vec<Au>, + block: Vec<CollapsedBorder>, + inline: Vec<CollapsedBorder>, } /// A helper struct that performs the layout of the box tree version @@ -2149,10 +2220,32 @@ impl<'a> TableLayout<'a> { } let mut collapsed_borders = CollapsedBorders { - block: vec![Au::zero(); self.table.size.height + 1], - inline: vec![Au::zero(); self.table.size.width + 1], + block: vec![Default::default(); self.table.size.height + 1], + inline: vec![Default::default(); self.table.size.width + 1], }; + let mut apply_border = + |style: &ComputedValues, block: &Range<usize>, inline: &Range<usize>| { + let border = CollapsedBorder::from_style(style, writing_mode); + collapsed_borders.block[block.start].max_assign(border.block_start); + collapsed_borders.block[block.end].max_assign(border.block_end); + collapsed_borders.inline[inline.start].max_assign(border.inline_start); + collapsed_borders.inline[inline.end].max_assign(border.inline_end); + }; + let all_rows = 0..self.table.size.height; + let all_columns = 0..self.table.size.width; + for column_group in &self.table.column_groups { + apply_border(&column_group.style, &all_rows, &column_group.track_range); + } + for (column_index, column) in self.table.columns.iter().enumerate() { + apply_border(&column.style, &all_rows, &(column_index..column_index + 1)); + } + for row_group in &self.table.row_groups { + apply_border(&row_group.style, &row_group.track_range, &all_columns); + } + for (row_index, row) in self.table.rows.iter().enumerate() { + apply_border(&row.style, &(row_index..row_index + 1), &all_columns); + } for row_index in 0..self.table.size.height { for column_index in 0..self.table.size.width { let cell = match self.table.slots[row_index][column_index] { @@ -2160,11 +2253,11 @@ impl<'a> TableLayout<'a> { _ => continue, }; - let border = cell.style.border_width(writing_mode); - collapsed_borders.block[row_index].max_assign(border.block_start); - collapsed_borders.block[row_index + cell.rowspan].max_assign(border.block_end); - collapsed_borders.inline[column_index].max_assign(border.inline_start); - collapsed_borders.inline[column_index + cell.colspan].max_assign(border.inline_end); + apply_border( + &cell.style, + &(row_index..row_index + cell.rowspan), + &(column_index..column_index + cell.colspan), + ); } } @@ -2180,10 +2273,10 @@ impl<'a> TableLayout<'a> { let end_x = coordinates.x + cell.colspan; let end_y = coordinates.y + cell.rowspan; let mut result = LogicalSides { - inline_start: collapsed_borders.inline[coordinates.x], - inline_end: collapsed_borders.inline[end_x], - block_start: collapsed_borders.block[coordinates.y], - block_end: collapsed_borders.block[end_y], + inline_start: collapsed_borders.inline[coordinates.x].width, + inline_end: collapsed_borders.inline[end_x].width, + block_start: collapsed_borders.block[coordinates.y].width, + block_end: collapsed_borders.block[end_y].width, }; if coordinates.x != 0 { |