mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
layout: Add support for table rows, columns, rowgroups and colgroups (#31341)
This adds support for table rows, columns, rowgroups and colgroups. There are few additions here: 1. The createion of fragments, which allows script queries and hit testing to work properly. These fragments are empty as all cells are still direct descendants of the table fragment. 2. Properly handling size information from tracks and track groups as well as frustrating rules about reordering rowgroups. 3. Painting a background seemlessly across track groups and groups. This is a thing that isn't done in legacy layout (nor WebKit)! Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
parent
74c07db56c
commit
02ae1f448e
57 changed files with 4274 additions and 21000 deletions
|
@ -2,22 +2,26 @@
|
|||
* 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 std::ops::Add;
|
||||
|
||||
use app_units::{Au, MAX_AU};
|
||||
use euclid::num::Zero;
|
||||
use log::warn;
|
||||
use servo_arc::Arc;
|
||||
use style::computed_values::border_collapse::T as BorderCollapse;
|
||||
use style::logical_geometry::WritingMode;
|
||||
use style::values::computed::{CSSPixelLength, Length, Percentage};
|
||||
use style::properties::ComputedValues;
|
||||
use style::values::computed::{
|
||||
CSSPixelLength, Length, LengthPercentage as ComputedLengthPercentage, Percentage,
|
||||
};
|
||||
use style::values::generics::box_::{GenericVerticalAlign as VerticalAlign, VerticalAlignKeyword};
|
||||
use style::values::generics::length::GenericLengthPercentageOrAuto::{Auto, LengthPercentage};
|
||||
use style::Zero;
|
||||
|
||||
use super::{Table, TableSlot, TableSlotCell};
|
||||
use super::{Table, TableSlot, TableSlotCell, TableTrackGroup};
|
||||
use crate::context::LayoutContext;
|
||||
use crate::formatting_contexts::{Baselines, IndependentLayout};
|
||||
use crate::fragment_tree::{AnonymousFragment, BoxFragment, CollapsedBlockMargins, Fragment};
|
||||
use crate::geom::{AuOrAuto, LogicalRect, LogicalSides, LogicalVec2};
|
||||
use crate::fragment_tree::{
|
||||
BoxFragment, CollapsedBlockMargins, ExtraBackground, Fragment, PositioningFragment,
|
||||
};
|
||||
use crate::geom::{AuOrAuto, LengthPercentageOrAuto, LogicalRect, LogicalSides, LogicalVec2};
|
||||
use crate::positioned::{PositioningContext, PositioningContextLength};
|
||||
use crate::sizing::ContentSizes;
|
||||
use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin};
|
||||
|
@ -108,7 +112,7 @@ impl<'a> TableLayout<'a> {
|
|||
let writing_mode = containing_block.style.writing_mode;
|
||||
self.compute_column_constrainedness_and_has_originating_cells(writing_mode);
|
||||
self.compute_cell_measures(layout_context, containing_block);
|
||||
self.compute_column_measures();
|
||||
self.compute_column_measures(containing_block.style.writing_mode);
|
||||
self.compute_table_width(containing_block);
|
||||
self.distributed_column_widths = self.distribute_width_to_columns();
|
||||
self.do_row_layout_first_pass(layout_context, containing_block, positioning_context);
|
||||
|
@ -132,84 +136,47 @@ impl<'a> TableLayout<'a> {
|
|||
_ => continue,
|
||||
};
|
||||
|
||||
// TODO: Should `box_size` percentages be treated as zero here or resolved against
|
||||
// the containing block?
|
||||
let pbm = cell.style.padding_border_margin(containing_block);
|
||||
let min_inline_size: Au = cell
|
||||
.style
|
||||
.min_box_size(writing_mode)
|
||||
.inline
|
||||
.percentage_relative_to(Length::zero())
|
||||
.map(|value| value.into())
|
||||
.auto_is(Au::zero);
|
||||
let max_inline_size: Au = cell.style.max_box_size(writing_mode).inline.map_or_else(
|
||||
|| MAX_AU,
|
||||
|length_percentage| length_percentage.resolve(Length::zero()).into(),
|
||||
);
|
||||
let inline_size: Au = cell
|
||||
.style
|
||||
.box_size(writing_mode)
|
||||
.inline
|
||||
.percentage_relative_to(Length::zero())
|
||||
.map(|value| value.into())
|
||||
.auto_is(Au::zero);
|
||||
|
||||
let (size, min_size, max_size) = get_sizes_from_style(&cell.style, writing_mode);
|
||||
let content_sizes = cell
|
||||
.contents
|
||||
.contents
|
||||
.inline_content_sizes(layout_context, writing_mode);
|
||||
let percentage_contribution =
|
||||
get_size_percentage_contribution_from_style(&cell.style, writing_mode);
|
||||
|
||||
// > The outer min-content width of a table-cell is max(min-width, min-content width)
|
||||
// > adjusted by the cell intrinsic offsets.
|
||||
let mut outer_min_content_width = content_sizes.min_content.max(min_inline_size);
|
||||
let mut outer_min_content_width = content_sizes.min_content.max(min_size.inline);
|
||||
let mut outer_max_content_width = if !self.column_constrainedness[column_index] {
|
||||
// > The outer max-content width of a table-cell in a non-constrained column is
|
||||
// > max(min-width, width, min-content width, min(max-width, max-content width))
|
||||
// > adjusted by the cell intrinsic offsets.
|
||||
min_inline_size
|
||||
.max(inline_size)
|
||||
min_size
|
||||
.inline
|
||||
.max(size.inline)
|
||||
.max(content_sizes.min_content)
|
||||
.max(max_inline_size.min(content_sizes.max_content))
|
||||
.max(max_size.inline.min(content_sizes.max_content))
|
||||
} else {
|
||||
// > The outer max-content width of a table-cell in a constrained column is
|
||||
// > max(min-width, width, min-content width, min(max-width, width)) adjusted by the
|
||||
// > cell intrinsic offsets.
|
||||
min_inline_size
|
||||
.max(inline_size)
|
||||
min_size
|
||||
.inline
|
||||
.max(size.inline)
|
||||
.max(content_sizes.min_content)
|
||||
.max(max_inline_size.min(inline_size))
|
||||
.max(max_size.inline.min(size.inline))
|
||||
};
|
||||
|
||||
// > The percentage contribution of a table cell, column, or column group is defined
|
||||
// > in terms of the computed values of width and max-width that have computed values
|
||||
// > that are percentages:
|
||||
// > min(percentage width, percentage max-width).
|
||||
// > If the computed values are not percentages, then 0% is used for width, and an
|
||||
// > infinite percentage is used for max-width.
|
||||
let inline_size_percent = cell
|
||||
.style
|
||||
.box_size(writing_mode)
|
||||
.inline
|
||||
.non_auto()
|
||||
.and_then(|length_percentage| length_percentage.to_percentage())
|
||||
.unwrap_or(Percentage(0.));
|
||||
let max_inline_size_percent = cell
|
||||
.style
|
||||
.max_box_size(writing_mode)
|
||||
.inline
|
||||
.and_then(|length_percentage| length_percentage.to_percentage())
|
||||
.unwrap_or(Percentage(f32::INFINITY));
|
||||
let percentage_contribution =
|
||||
Percentage(inline_size_percent.0.min(max_inline_size_percent.0));
|
||||
|
||||
let pbm = cell.style.padding_border_margin(containing_block);
|
||||
outer_min_content_width += pbm.padding_border_sums.inline;
|
||||
outer_max_content_width += pbm.padding_border_sums.inline;
|
||||
|
||||
row_measures[column_index] = CellOrColumnMeasure {
|
||||
content_sizes: ContentSizes {
|
||||
min_content: outer_min_content_width,
|
||||
max_content: outer_max_content_width,
|
||||
},
|
||||
percentage_width: percentage_contribution,
|
||||
percentage_width: percentage_contribution.inline,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -254,7 +221,7 @@ 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) {
|
||||
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.
|
||||
|
@ -289,7 +256,9 @@ 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 = CellOrColumnMeasure::zero();
|
||||
let mut column_measure = self
|
||||
.table
|
||||
.get_column_measure_for_column_at_index(writing_mode, column_index);
|
||||
|
||||
for row_index in 0..self.table.size.height {
|
||||
let coords = TableSlotCoordinates::new(column_index, row_index);
|
||||
|
@ -652,8 +621,6 @@ impl<'a> TableLayout<'a> {
|
|||
let mut max_content_sizing_guesses = Vec::new();
|
||||
|
||||
for column_idx in 0..self.table.size.width {
|
||||
use style::Zero;
|
||||
|
||||
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;
|
||||
|
@ -1040,14 +1007,20 @@ impl<'a> TableLayout<'a> {
|
|||
assert_eq!(self.table.size.width, self.distributed_column_widths.len());
|
||||
|
||||
let mut baselines = Baselines::default();
|
||||
let border_spacing = self.table.border_spacing();
|
||||
let mut fragments = Vec::new();
|
||||
let mut row_offset = border_spacing.block;
|
||||
for row_index in 0..self.table.size.height {
|
||||
let mut column_offset = border_spacing.inline;
|
||||
let row_size = self.row_sizes[row_index];
|
||||
let row_baseline = self.row_baselines[row_index];
|
||||
|
||||
if self.table.size.width == 0 || self.table.size.height == 0 {
|
||||
return IndependentLayout {
|
||||
fragments,
|
||||
content_block_size: Au::zero(),
|
||||
baselines,
|
||||
};
|
||||
}
|
||||
|
||||
let dimensions = TableAndTrackDimensions::new(&self);
|
||||
self.make_fragments_for_columns_rows_and_groups(&dimensions, &mut fragments);
|
||||
|
||||
for row_index in 0..self.table.size.height {
|
||||
// From <https://drafts.csswg.org/css-align-3/#baseline-export>
|
||||
// > If any cells in the row participate in first baseline/last baseline alignment along
|
||||
// > the inline axis, the first/last baseline set of the row is generated from their
|
||||
|
@ -1058,8 +1031,9 @@ impl<'a> TableLayout<'a> {
|
|||
// If any cell below has baseline alignment, these values will be overwritten,
|
||||
// but they are initialized to the content edge of the first row.
|
||||
if row_index == 0 {
|
||||
baselines.first = Some(row_offset + row_size);
|
||||
baselines.last = Some(row_offset + row_size);
|
||||
let row_end = dimensions.get_row_rect(0).max_block_position();
|
||||
baselines.first = Some(row_end);
|
||||
baselines.last = Some(row_end);
|
||||
}
|
||||
|
||||
for column_index in 0..self.table.size.width {
|
||||
|
@ -1078,46 +1052,65 @@ impl<'a> TableLayout<'a> {
|
|||
},
|
||||
};
|
||||
|
||||
let cell_rect = dimensions.get_cell_rect(
|
||||
TableSlotCoordinates::new(column_index, row_index),
|
||||
cell.rowspan,
|
||||
cell.colspan,
|
||||
);
|
||||
|
||||
// If this cell has baseline alignment, it can adjust the table's overall baseline.
|
||||
let row_baseline = self.row_baselines[row_index];
|
||||
if cell.effective_vertical_align() == VerticalAlignKeyword::Baseline {
|
||||
let baseline = cell_rect.start_corner.block + row_baseline;
|
||||
if row_index == 0 {
|
||||
baselines.first = Some(row_offset + row_baseline);
|
||||
baselines.first = Some(baseline);
|
||||
}
|
||||
baselines.last = Some(row_offset + row_baseline);
|
||||
baselines.last = Some(baseline);
|
||||
}
|
||||
|
||||
// Calculate the inline and block size of all rows and columns that this cell spans.
|
||||
let inline_size: Au = (column_index..column_index + cell.colspan)
|
||||
.map(|index| self.distributed_column_widths[index])
|
||||
.fold(Au::zero(), Au::add) +
|
||||
((cell.colspan - 1) as i32 * border_spacing.inline);
|
||||
let block_size: Au = (row_index..row_index + cell.rowspan)
|
||||
.map(|index| self.row_sizes[index])
|
||||
.fold(Au::zero(), Au::add) +
|
||||
((cell.rowspan - 1) as i32 * border_spacing.block);
|
||||
let mut fragment =
|
||||
cell.create_fragment(layout, cell_rect, row_baseline, positioning_context);
|
||||
|
||||
let cell_rect: LogicalRect<Length> = LogicalRect {
|
||||
start_corner: LogicalVec2 {
|
||||
inline: column_offset.into(),
|
||||
block: row_offset.into(),
|
||||
},
|
||||
size: LogicalVec2 {
|
||||
inline: inline_size.into(),
|
||||
block: block_size.into(),
|
||||
},
|
||||
};
|
||||
let column = self.table.columns.get(column_index);
|
||||
let column_group = column
|
||||
.and_then(|column| column.group_index)
|
||||
.and_then(|index| self.table.column_groups.get(index));
|
||||
if let Some(column_group) = column_group {
|
||||
fragment.add_extra_background(ExtraBackground {
|
||||
style: column_group.style.clone(),
|
||||
rect: dimensions.get_column_group_rect(column_group),
|
||||
})
|
||||
}
|
||||
|
||||
fragments.push(Fragment::Box(cell.create_fragment(
|
||||
layout,
|
||||
cell_rect,
|
||||
row_baseline,
|
||||
positioning_context,
|
||||
)));
|
||||
if let Some(column) = column {
|
||||
if !column.is_anonymous {
|
||||
fragment.add_extra_background(ExtraBackground {
|
||||
style: column.style.clone(),
|
||||
rect: dimensions.get_column_rect(column_index),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
column_offset += inline_size + border_spacing.inline;
|
||||
let row = self.table.rows.get(row_index);
|
||||
let row_group = row
|
||||
.and_then(|row| row.group_index)
|
||||
.and_then(|index| self.table.row_groups.get(index));
|
||||
if let Some(row_group) = row_group {
|
||||
fragment.add_extra_background(ExtraBackground {
|
||||
style: row_group.style.clone(),
|
||||
rect: dimensions.get_row_group_rect(row_group),
|
||||
})
|
||||
}
|
||||
|
||||
if let Some(row) = row {
|
||||
fragment.add_extra_background(ExtraBackground {
|
||||
style: row.style.clone(),
|
||||
rect: dimensions.get_row_rect(row_index),
|
||||
})
|
||||
}
|
||||
|
||||
fragments.push(Fragment::Box(fragment));
|
||||
}
|
||||
|
||||
row_offset += row_size + border_spacing.block;
|
||||
}
|
||||
|
||||
if self.table.anonymous {
|
||||
|
@ -1127,10 +1120,173 @@ impl<'a> TableLayout<'a> {
|
|||
|
||||
IndependentLayout {
|
||||
fragments,
|
||||
content_block_size: row_offset,
|
||||
content_block_size: dimensions.table_rect.max_block_position(),
|
||||
baselines,
|
||||
}
|
||||
}
|
||||
|
||||
fn make_fragments_for_columns_rows_and_groups(
|
||||
&mut self,
|
||||
dimensions: &TableAndTrackDimensions,
|
||||
fragments: &mut Vec<Fragment>,
|
||||
) {
|
||||
for column_group in self.table.column_groups.iter() {
|
||||
if !column_group.is_empty() {
|
||||
fragments.push(Fragment::Positioning(PositioningFragment::new_empty(
|
||||
column_group.base_fragment_info,
|
||||
dimensions.get_column_group_rect(column_group).into(),
|
||||
column_group.style.clone(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
for (column_index, column) in self.table.columns.iter().enumerate() {
|
||||
fragments.push(Fragment::Positioning(PositioningFragment::new_empty(
|
||||
column.base_fragment_info,
|
||||
dimensions.get_column_rect(column_index).into(),
|
||||
column.style.clone(),
|
||||
)));
|
||||
}
|
||||
|
||||
for row_group in self.table.row_groups.iter() {
|
||||
if !row_group.is_empty() {
|
||||
fragments.push(Fragment::Positioning(PositioningFragment::new_empty(
|
||||
row_group.base_fragment_info,
|
||||
dimensions.get_row_group_rect(row_group).into(),
|
||||
row_group.style.clone(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
for (row_index, row) in self.table.rows.iter().enumerate() {
|
||||
fragments.push(Fragment::Positioning(PositioningFragment::new_empty(
|
||||
row.base_fragment_info,
|
||||
dimensions.get_row_rect(row_index).into(),
|
||||
row.style.clone(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TableAndTrackDimensions {
|
||||
/// The rect of the full table, not counting for borders, padding, and margin.
|
||||
table_rect: LogicalRect<Au>,
|
||||
/// The rect of the full table, not counting for borders, padding, and margin
|
||||
/// and offset by any border spacing and caption.
|
||||
table_cells_rect: LogicalRect<Au>,
|
||||
/// The min and max block offsets of each table row.
|
||||
row_dimensions: Vec<(Au, Au)>,
|
||||
/// The min and max inline offsets of each table column
|
||||
column_dimensions: Vec<(Au, Au)>,
|
||||
}
|
||||
|
||||
impl TableAndTrackDimensions {
|
||||
fn new(table_layout: &TableLayout) -> Self {
|
||||
let border_spacing = table_layout.table.border_spacing();
|
||||
|
||||
let mut column_dimensions = Vec::new();
|
||||
let mut column_offset = border_spacing.inline;
|
||||
for column_index in 0..table_layout.table.size.width {
|
||||
let column_size = table_layout.distributed_column_widths[column_index];
|
||||
column_dimensions.push((column_offset, column_offset + column_size));
|
||||
column_offset += column_size + border_spacing.inline;
|
||||
}
|
||||
|
||||
let mut row_dimensions = Vec::new();
|
||||
let mut row_offset = border_spacing.block;
|
||||
for row_index in 0..table_layout.table.size.height {
|
||||
let row_size = table_layout.row_sizes[row_index];
|
||||
row_dimensions.push((row_offset, row_offset + row_size));
|
||||
row_offset += row_size + border_spacing.block;
|
||||
}
|
||||
|
||||
let table_start_corner = LogicalVec2 {
|
||||
inline: column_dimensions[0].0,
|
||||
block: row_dimensions[0].0,
|
||||
};
|
||||
let table_size = &LogicalVec2 {
|
||||
inline: column_dimensions[column_dimensions.len() - 1].1,
|
||||
block: row_dimensions[row_dimensions.len() - 1].1,
|
||||
} - &table_start_corner;
|
||||
let table_cells_rect = LogicalRect {
|
||||
start_corner: table_start_corner,
|
||||
size: table_size,
|
||||
};
|
||||
|
||||
let table_rect = LogicalRect {
|
||||
start_corner: LogicalVec2::zero(),
|
||||
size: LogicalVec2 {
|
||||
inline: column_offset,
|
||||
block: row_offset,
|
||||
},
|
||||
};
|
||||
|
||||
Self {
|
||||
table_rect,
|
||||
table_cells_rect,
|
||||
row_dimensions,
|
||||
column_dimensions,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_row_rect(&self, row_index: usize) -> LogicalRect<Au> {
|
||||
let mut row_rect = self.table_cells_rect.clone();
|
||||
let row_dimensions = self.row_dimensions[row_index];
|
||||
row_rect.start_corner.block = row_dimensions.0;
|
||||
row_rect.size.block = row_dimensions.1 - row_dimensions.0;
|
||||
row_rect
|
||||
}
|
||||
|
||||
fn get_column_rect(&self, column_index: usize) -> LogicalRect<Au> {
|
||||
let mut row_rect = self.table_cells_rect.clone();
|
||||
let column_dimensions = self.column_dimensions[column_index];
|
||||
row_rect.start_corner.inline = column_dimensions.0;
|
||||
row_rect.size.inline = column_dimensions.1 - column_dimensions.0;
|
||||
row_rect
|
||||
}
|
||||
|
||||
fn get_row_group_rect(&self, row_group: &TableTrackGroup) -> LogicalRect<Au> {
|
||||
if row_group.is_empty() {
|
||||
return LogicalRect::zero();
|
||||
}
|
||||
|
||||
let mut row_group_rect = self.table_cells_rect.clone();
|
||||
let block_start = self.row_dimensions[row_group.track_range.start].0;
|
||||
let block_end = self.row_dimensions[row_group.track_range.end - 1].1;
|
||||
row_group_rect.start_corner.block = block_start;
|
||||
row_group_rect.size.block = block_end - block_start;
|
||||
row_group_rect
|
||||
}
|
||||
|
||||
fn get_column_group_rect(&self, column_group: &TableTrackGroup) -> LogicalRect<Au> {
|
||||
if column_group.is_empty() {
|
||||
return LogicalRect::zero();
|
||||
}
|
||||
|
||||
let mut column_group_rect = self.table_cells_rect.clone();
|
||||
let inline_start = self.column_dimensions[column_group.track_range.start].0;
|
||||
let inline_end = self.column_dimensions[column_group.track_range.end - 1].1;
|
||||
column_group_rect.start_corner.inline = inline_start;
|
||||
column_group_rect.size.inline = inline_end - inline_start;
|
||||
column_group_rect
|
||||
}
|
||||
|
||||
fn get_cell_rect(
|
||||
&self,
|
||||
coordinates: TableSlotCoordinates,
|
||||
rowspan: usize,
|
||||
colspan: usize,
|
||||
) -> LogicalRect<Au> {
|
||||
let start_corner = LogicalVec2 {
|
||||
inline: self.column_dimensions[coordinates.x].0,
|
||||
block: self.row_dimensions[coordinates.y].0,
|
||||
};
|
||||
let size = &LogicalVec2 {
|
||||
inline: self.column_dimensions[coordinates.x + colspan - 1].1,
|
||||
block: self.row_dimensions[coordinates.y + rowspan - 1].1,
|
||||
} - &start_corner;
|
||||
LogicalRect { start_corner, size }
|
||||
}
|
||||
}
|
||||
|
||||
impl Table {
|
||||
|
@ -1200,6 +1356,33 @@ impl Table {
|
|||
.0
|
||||
}
|
||||
|
||||
fn get_column_measure_for_column_at_index(
|
||||
&self,
|
||||
writing_mode: WritingMode,
|
||||
column_index: usize,
|
||||
) -> CellOrColumnMeasure {
|
||||
let column = match self.columns.get(column_index) {
|
||||
Some(column) => column,
|
||||
None => return CellOrColumnMeasure::zero(),
|
||||
};
|
||||
|
||||
let (size, min_size, max_size) = get_sizes_from_style(&column.style, writing_mode);
|
||||
let percentage_contribution =
|
||||
get_size_percentage_contribution_from_style(&column.style, writing_mode);
|
||||
|
||||
CellOrColumnMeasure {
|
||||
content_sizes: ContentSizes {
|
||||
// > The outer min-content width of a table-column or table-column-group is
|
||||
// > max(min-width, width).
|
||||
min_content: min_size.inline.max(size.inline),
|
||||
// > The outer max-content width of a table-column or table-column-group is
|
||||
// > max(min-width, min(max-width, width)).
|
||||
max_content: min_size.inline.max(max_size.inline.min(size.inline)),
|
||||
},
|
||||
percentage_width: percentage_contribution.inline,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn layout(
|
||||
&self,
|
||||
layout_context: &LayoutContext,
|
||||
|
@ -1250,13 +1433,14 @@ impl TableSlotCell {
|
|||
fn create_fragment(
|
||||
&self,
|
||||
mut layout: CellLayout,
|
||||
cell_rect: LogicalRect<Length>,
|
||||
cell_rect: LogicalRect<Au>,
|
||||
cell_baseline: Au,
|
||||
positioning_context: &mut PositioningContext,
|
||||
) -> BoxFragment {
|
||||
// This must be scoped to this function because it conflicts with euclid's Zero.
|
||||
use style::Zero as StyleZero;
|
||||
|
||||
let cell_rect: LogicalRect<Length> = cell_rect.into();
|
||||
let cell_content_rect = cell_rect.deflate(&(&layout.padding + &layout.border));
|
||||
let content_block_size = layout.layout.content_block_size.into();
|
||||
let vertical_align_offset = match self.effective_vertical_align() {
|
||||
|
@ -1278,7 +1462,7 @@ impl TableSlotCell {
|
|||
inline: Length::new(0.),
|
||||
block: vertical_align_offset,
|
||||
};
|
||||
let vertical_align_fragment = AnonymousFragment::new(
|
||||
let vertical_align_fragment = PositioningFragment::new_anonymous(
|
||||
vertical_align_fragment_rect,
|
||||
layout.layout.fragments,
|
||||
self.style.writing_mode,
|
||||
|
@ -1302,7 +1486,7 @@ impl TableSlotCell {
|
|||
BoxFragment::new(
|
||||
self.base_fragment_info,
|
||||
self.style.clone(),
|
||||
vec![Fragment::Anonymous(vertical_align_fragment)],
|
||||
vec![Fragment::Positioning(vertical_align_fragment)],
|
||||
cell_content_rect,
|
||||
layout.padding,
|
||||
layout.border,
|
||||
|
@ -1313,3 +1497,73 @@ impl TableSlotCell {
|
|||
.with_baselines(layout.layout.baselines)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_size_percentage_contribution_from_style(
|
||||
style: &Arc<ComputedValues>,
|
||||
writing_mode: WritingMode,
|
||||
) -> LogicalVec2<Percentage> {
|
||||
// From <https://drafts.csswg.org/css-tables/#percentage-contribution>
|
||||
// > The percentage contribution of a table cell, column, or column group is defined
|
||||
// > in terms of the computed values of width and max-width that have computed values
|
||||
// > that are percentages:
|
||||
// > min(percentage width, percentage max-width).
|
||||
// > If the computed values are not percentages, then 0% is used for width, and an
|
||||
// > infinite percentage is used for max-width.
|
||||
let size = style.box_size(writing_mode);
|
||||
let max_size = style.max_box_size(writing_mode);
|
||||
|
||||
let get_contribution_for_axis =
|
||||
|size: LengthPercentageOrAuto<'_>, max_size: Option<&ComputedLengthPercentage>| {
|
||||
let size_percentage = size
|
||||
.non_auto()
|
||||
.and_then(|length_percentage| length_percentage.to_percentage())
|
||||
.unwrap_or(Percentage(0.));
|
||||
let max_size_percentage = max_size
|
||||
.and_then(|length_percentage| length_percentage.to_percentage())
|
||||
.unwrap_or(Percentage(f32::INFINITY));
|
||||
Percentage(size_percentage.0.min(max_size_percentage.0))
|
||||
};
|
||||
|
||||
LogicalVec2 {
|
||||
inline: get_contribution_for_axis(size.inline, max_size.inline),
|
||||
block: get_contribution_for_axis(size.block, max_size.block),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_sizes_from_style(
|
||||
style: &Arc<ComputedValues>,
|
||||
writing_mode: WritingMode,
|
||||
) -> (LogicalVec2<Au>, LogicalVec2<Au>, LogicalVec2<Au>) {
|
||||
let get_max_size_for_axis = |size: Option<&ComputedLengthPercentage>| {
|
||||
size.map_or_else(
|
||||
|| MAX_AU,
|
||||
|length_percentage| length_percentage.resolve(Length::zero()).into(),
|
||||
)
|
||||
};
|
||||
|
||||
let max_size = style.max_box_size(writing_mode);
|
||||
let max_size = LogicalVec2 {
|
||||
inline: get_max_size_for_axis(max_size.inline),
|
||||
block: get_max_size_for_axis(max_size.block),
|
||||
};
|
||||
|
||||
let get_size_for_axis = |size: LengthPercentageOrAuto<'_>| {
|
||||
size.percentage_relative_to(Length::zero())
|
||||
.map(|value| value.into())
|
||||
.auto_is(Au::zero)
|
||||
};
|
||||
|
||||
let min_size = style.min_box_size(writing_mode);
|
||||
let min_size = LogicalVec2 {
|
||||
inline: get_size_for_axis(min_size.inline),
|
||||
block: get_size_for_axis(min_size.block),
|
||||
};
|
||||
|
||||
let size = style.box_size(writing_mode);
|
||||
let size = LogicalVec2 {
|
||||
inline: get_size_for_axis(size.inline),
|
||||
block: get_size_for_axis(size.block),
|
||||
};
|
||||
|
||||
(size, min_size, max_size)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue