Skip to content

Commit

Permalink
Auto merge of #20128 - Manishearth:rowspan, r=mbrubeck
Browse files Browse the repository at this point in the history
Rowspan support for tables

fixes #20092

This just contains the first steps.

We apply a naive algorithm: Spanning cells apply a pressure equal to `block_size / rowspan` on each row they are in. We move table row block size computation into the tables, and make it two pass. In the first pass we compute the sizes of each row, and in the
second pass we assign them, adding them up for any involved cells.

This is missing:

 - [x] Accounting for border sizes
 - [x] Applying pressure to rows that are not the row containing the cell
 - [ ] Reducing pressure on future rows if the current row is able to accomodate more of the cell
 - [x] For tables containing both rows and rowgroups, reset the rowspan info when we hit a rowgroup
 - [x] Correctly handle overflowing rowspans

cc @mbrubeck @pcwalton

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/20128)
<!-- Reviewable:end -->
  • Loading branch information
bors-servo committed Feb 28, 2018
2 parents af92c06 + ffe68e8 commit e2f2814
Show file tree
Hide file tree
Showing 8 changed files with 295 additions and 103 deletions.
93 changes: 74 additions & 19 deletions components/layout/table.rs
Expand Up @@ -496,10 +496,10 @@ impl Flow for TableFlow {
});
}

fn assign_block_size(&mut self, _: &LayoutContext) {
fn assign_block_size(&mut self, lc: &LayoutContext) {
debug!("assign_block_size: assigning block_size for table");
let vertical_spacing = self.spacing().vertical();
self.block_flow.assign_block_size_for_table_like_flow(vertical_spacing)
self.block_flow.assign_block_size_for_table_like_flow(vertical_spacing, lc)
}

fn compute_stacking_relative_position(&mut self, layout_context: &LayoutContext) {
Expand Down Expand Up @@ -567,13 +567,6 @@ impl Flow for TableFlow {
}

#[derive(Debug)]
// XXXManishearth We might be able to avoid the Arc<T>s if
// the table is structured such that the columns always come
// first in the flow tree, at which point we can
// reuse the iterator that we use for colgroups
// for rows (and have no borrowing issues between
// holding on to both ColumnStyle<'table> and
// the rows)
struct ColumnStyle<'table> {
span: u32,
colgroup_style: Option<&'table ComputedValues>,
Expand Down Expand Up @@ -771,34 +764,95 @@ fn perform_border_collapse_for_row(child_table_row: &mut TableRowFlow,
/// rowgroups.
pub trait TableLikeFlow {
/// Lays out the rows of a table.
fn assign_block_size_for_table_like_flow(&mut self, block_direction_spacing: Au);
fn assign_block_size_for_table_like_flow(&mut self, block_direction_spacing: Au,
layout_context: &LayoutContext);
}

impl TableLikeFlow for BlockFlow {
fn assign_block_size_for_table_like_flow(&mut self, block_direction_spacing: Au) {
fn assign_block_size_for_table_like_flow(&mut self, block_direction_spacing: Au,
layout_context: &LayoutContext) {
debug_assert!(self.fragment.style.get_inheritedtable().border_collapse ==
border_collapse::T::Separate || block_direction_spacing == Au(0));

fn border_spacing_for_row(fragment: &Fragment, row: &TableRowFlow,
block_direction_spacing: Au) -> Au {
match fragment.style.get_inheritedtable().border_collapse {
border_collapse::T::Separate => block_direction_spacing,
border_collapse::T::Collapse => {
row.collapsed_border_spacing.block_start
}
}
}

if self.base.restyle_damage.contains(ServoRestyleDamage::REFLOW) {
// (size, cumulative_border_spacing)
let mut sizes = vec![(Au(0), Au(0))];
// The amount of border spacing up to and including this row,
// but not including the spacing beneath it
let mut cumulative_border = Au(0);
let mut incoming_rowspan_data = vec![];

// First pass: Compute block-direction border spacings
// XXXManishearth this can be done in tandem with the second pass,
// provided we never hit any rowspan cases
for kid in self.base.child_iter_mut()
.filter(|k| k.is_table_row())
.skip(1) {
cumulative_border +=
border_spacing_for_row(&self.fragment, kid.as_table_row(),
block_direction_spacing);
// we haven't calculated sizes yet
sizes.push((Au(0), cumulative_border));
}

// Second pass: Compute row block sizes
// [expensive: iterates over cells]
let mut i = 0;
let mut overflow = Au(0);
for kid in self.base.child_iter_mut() {
if kid.is_table_row() {
let (size, oflo) = kid.as_mut_table_row()
.compute_block_size_table_row_base(layout_context,
&mut incoming_rowspan_data,
&sizes,
i);
sizes[i].0 = size;
overflow = oflo;
i += 1;
// new rowgroups stop rowspans
} else if kid.is_table_rowgroup() {
if i > 0 {
sizes[i - 1].0 = cmp::max(sizes[i - 1].0, overflow);
}
}
}
if i > 0 {
sizes[i - 1].0 = cmp::max(sizes[i - 1].0, overflow);
}


// Our current border-box position.
let block_start_border_padding = self.fragment.border_padding.block_start;
let mut current_block_offset = block_start_border_padding;
let mut has_rows = false;

// Third pass: Assign block sizes and positions to rows, cells, and other children
// [expensive: iterates over cells]
// At this point, `current_block_offset` is at the content edge of our box. Now iterate
// over children.
let mut i = 0;
for kid in self.base.child_iter_mut() {
// Account for spacing or collapsed borders.
if kid.is_table_row() {
has_rows = true;
let child_table_row = kid.as_table_row();
let row = kid.as_mut_table_row();
row.assign_block_size_to_self_and_children(&sizes, i);
row.mut_base().restyle_damage
.remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW |
ServoRestyleDamage::REFLOW);
current_block_offset = current_block_offset +
match self.fragment.style.get_inheritedtable().border_collapse {
border_collapse::T::Separate => block_direction_spacing,
border_collapse::T::Collapse => {
child_table_row.collapsed_border_spacing.block_start
}
}
border_spacing_for_row(&self.fragment, row,
block_direction_spacing);
i += 1;
}

// At this point, `current_block_offset` is at the border edge of the child.
Expand Down Expand Up @@ -843,6 +897,7 @@ impl TableLikeFlow for BlockFlow {
self.fragment.border_box.start.b = Au(0);
self.base.position.size.block = current_block_offset;

// Fourth pass: Assign absolute position info
// Write in the size of the relative containing block for children. (This information
// is also needed to handle RTL.)
for kid in self.base.child_iter_mut() {
Expand Down
11 changes: 11 additions & 0 deletions components/layout/table_cell.rs
Expand Up @@ -148,6 +148,17 @@ impl TableCellFlow {
}
}
}

// Total block size of child
//
// Call after block size calculation
pub fn total_block_size(&mut self) -> Au {
// TODO: Percentage block-size
let specified = MaybeAuto::from_style(self.fragment().style()
.content_block_size(),
Au(0)).specified_or_zero();
specified + self.fragment().border_padding.block_start_end()
}
}

impl Flow for TableCellFlow {
Expand Down

0 comments on commit e2f2814

Please sign in to comment.