Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[css-tables-3] Table height redistribution algorithm #4418

Open
atotic opened this issue Oct 14, 2019 · 11 comments
Open

[css-tables-3] Table height redistribution algorithm #4418

atotic opened this issue Oct 14, 2019 · 11 comments
Labels
css-tables-3 Current Work

Comments

@atotic
Copy link
Contributor

atotic commented Oct 14, 2019

I am reimplementing tables in Chrome. Height redistribution algorithm for tall cells
(rowspan > 1) is not fully defined in current spec.
I've investigated what Chrome and other browsers currently do. Every browser does something different. It'd be nice to spec a web compatible algorithm, so that implementations can converge.

I expect row group and table height redistribution algorithms to be similar.

Based upon Chrome's behavior, I came up with a following algorithm.

Algorithm

  1. Compute row groups.

    • Group1 are rows shorter than intrinsic height. These are percentage
      heights, as row layout algorithm (first pass) has already added fixed heights to row height.
    • Group2 are unconstrained cells.
    • Group3 are all cells.
  2. If there are any rows in Group1, distribute excess heights until rows
    reach their existing height. Distribution of heights is not proportional
    to %ge (like columns). It is greedy. Cells are traversed, and each cell
    is resized to its desired %ge height. When we run out of excess height,
    distribution stops.
    Example:
    <tr height:100%> // this row gets all the excess height, because it is 100%.
    <tr height:100%> // this row gets nothing, because there is nothing left

  3. If there is excess height left over distribute to Group2, or Group3 if
    Group2 is empty. Zero-height rows do not grow. Excess height is distributed in
    proportion to existing cell size.

Current spec:
https://drafts.csswg.org/css-tables-3/#height-distribution-principles
https://drafts.csswg.org/css-tables-3/#row-layout

I also have a w-p-t tentative testcase
computing-row-measure-2.tentative.html

Chrome passes (I modeled the algorithm on Chrome's behavior)

PASS Unconstrained cells heights are distributed proportionally.
PASS Constrained fixed cells not resized if unconstrained exist.
PASS Constrained %ge cells grow to %ge size, but no more.
PASS %ge grow greedily.
PASS Zero height rows do not grow.
PASS Unconstrained cells are redistributed proportionally

Firefox has trouble with growing %ge cells:

FAIL Constrained %ge cells grow to %ge size, but no more. expected 30 but got 19
FAIL %ge grow greedily. assert_equals: expected 60 but got 19

Safari, like FF, does not like to grow %ge cells. I am unclear how Safari decides on which cell to grow.

FAIL Unconstrained cells heights are distributed proportionally. assert_equals: expected 50 but got 18
FAIL Constrained %ge cells grow to %ge size, but no more. assert_equals: expected 30 but got 18
FAIL %ge grow greedily. assert_equals: expected 60 but got 18
FAIL Unconstrained cells are redistributed proportionally assert_equals: expected 75 but got 30

Edge 17: Edge grows %ge cells beyond its intrinsic height, and does not grow greedily.

FAIL Constrained fixed cells not resized if unconstrained exist. expected 30, got 62
FAIL Constrained %ge cells grow to %ge size, but no more. expected 30, got 50
FAIL %ge grow greedily. expected 60, got 33

@atotic atotic added the css-tables-3 Current Work label Oct 14, 2019
@atotic
Copy link
Contributor Author

atotic commented Oct 14, 2019

Test will land as:
css/css-tables/height-distribution/computing-row-measure-2.tentative.html

@FremyCompany I am getting to implementing the height redistribution. Happy to assist with specs.

@FremyCompany
Copy link
Contributor

Thanks for looking into this!

I am uncertain about whether the greedy approach makes a lot of sense. Based on your findings only Chrome behaves that way, and I think it's unfortunate that reordering rows also changes their size.

I'd rather we interpolate linearly between all such cells in Group 1 between their current and preferred size. That is likely to produce the more visually pleasing result. Would that be an option for you, do you think?

Other than that, the steps outlined look reasonable to me, I'd be happy to write that down in the spec instead of the blank section :)

@atotic
Copy link
Contributor Author

atotic commented Oct 15, 2019

I am uncertain about whether the greedy approach makes a lot of sense. Based on your findings only Chrome behaves that way, and I think it's unfortunate that reordering rows also changes their size.

I agree that greedy algorithm is not great, and that proportional is nicer for webdevs.

If we are going to grow proportionally, there are a few details to be decided on. One is a fundamental problem, and two are clean problems.

Fundamental problem:

  • (*) what is the percentage resolution size for row here? The default answer is row group height. The problem is that final row group height is not known. This is why:
    • row group height =~ sum of row heights
    • row heights depend on how tall cells distribute their heights.
    • we need row heights to resolve percentage row heights. @#$@#$
    • we could use "intrinsic" row group height, before tall cells are distributed. That is not what Chrome does today. Firefox just does not distribute excess heights at all. IE treats %ge as proportional. So does Edge.
    • we could try and get fancy and use tall cell heights to estimate section height: "we can't compute heights for row 1, but we know that tall cell in rows 1:3 takes at least 100px. So we can estimate height of rows 1-3 is max(tall_cell_height, sum(rows 1:3)"
    • but this trick does not quite work. If multiple tall cells partially overlap, we cannot estimate final section height correctly, because one redistribution can shape the other. I think what Chrome does is try not to find general solution, but makes a row group height guesstimate based upon tall cell height.

The clean solution would be to use "intrinsic" row group height, but that is not what happens on majority of the browsers today.
Edge/IE (back to 8) solution is something like, "make all cells grow proportionally", which ignores pixel and % sizes. IE was dominant browser when complex tables were popular.
I think Firefox solution is "estimate row group height as 0", so rows do not grow beyond minimum height. Firefox's solution is clean, but differs a lot for everyone else.
Chrome solution is "make an educated guess about row group height based on current row group height and tall cell height". Results are not too different from IE.

Since a lot of table work happened during IE's dominant market share, IE's solution might be web compatible, and is easiest to spec and implement.

Clean problems:

  • how do we limit total %ge to 100%?. Columns have an algorithm step where they add all column percentages until 100%, then zero out anything over 100%. I think we should do the same for rows.

  • cells grow proportionally to what? Existing size, or desired percentage? My suggestion is desired percentage.'

(*) initial comment was edited with additional concern.

@atotic
Copy link
Contributor Author

atotic commented Oct 16, 2019

Testcase that shows how 50% tall row gets resolved incorrectly by everyone.

test2.txt

@FremyCompany
Copy link
Contributor

@atotic All good questions. Here is how I thought about things:

  • Step 1: all rows have their minimum size
  • Step 2: compute the minimum height of every row group by summing them
  • Step 3: compute the size of each row, resolving percentages based on the previous estimate.
  • Step 4: compute the sum of all row heights, reduced by their minimum height of step 1. This is the space requested for distribution. It could be higher than the height available to distribute.
  • Step 5: find alpha such that alpha = requested-to-be-distributed-height / available-to-distribute-height; the final height of each row will be minimum + alpha * requested increase.
  • Step 6: if alpha is an undefined number (dividing by 0) then increase all rows proportionally to their current height : final height = beta * minimum height (where beta > 1)

How does that sound to you?

@FremyCompany
Copy link
Contributor

The reason this does not require capping is that if the sum of percentages is higher than 100% there just won't be enough available space to distribute, so alpha will be smaller than 1, and all is well.

@atotic
Copy link
Contributor Author

atotic commented Oct 16, 2019

  • Step 1: all rows have their minimum size
    ........
  • Step 6: if alpha is an undefined number (dividing by 0) then increase all rows proportionally to their current height : final height = beta * minimum height (where beta > 1)

How does that sound to you?

I am confused. What height are you redistributing?

In my model there are three separate height redistributions that happen during table layout:

  • tall cell redistribution
  • row group height redistribution
  • table height redistribution

This issue concerns only tall cell redistribution. I have not looked into the other 2 closely yet.

Step 3: compute the size of each row, resolving percentages based on the previous estimate.

Row percentage heights are not used to compute initial row height. They are only used as a limit for excess height redistribution.

@FremyCompany
Copy link
Contributor

Oh sorry, I was talking about table height redistribution.

What do you mean by table-cell redistribution? Are you talking about row-spanning cells?

@atotic
Copy link
Contributor Author

atotic commented Oct 16, 2019

Yes!

@atotic
Copy link
Contributor Author

atotic commented Oct 17, 2019

There are 3 height redistributions in table layout algorithm:

  • tall cells -> rows
  • fixed size row groups -> rows
  • table -> row groups -> rows

I think they'll all use the same basic algorithm. The only difference between the three is "what height to use for percentage resolution". I will have a proposal soon, after some tests.

@atotic
Copy link
Contributor Author

atotic commented Oct 18, 2019

I've ~finished my investgation. I think all redistributions can use the same basic algorithm I've initially proposed here (with modifications for empty rows, and what to do if percentage resolution height is undefined).

There is very little agreement between browsers about what should be done. Details about my proposal are in the investigation. Briefly, I think these rules are clean, and hopefully web compatible:

Use algorithm I initially proposed for height redistribution.

PRH means percentage resolution height.
Tall cell -> row height redistribution: PRH is undefined.
Section height -> row height redistribution: PRH is section height
Table height -> section height redistribution: PRH is table height

Section CSS height should not be ignored.
Empty rows never grow in size.
If PRH is undefined, percentage rows are treated as unconstrained rows.

@atotic atotic changed the title [css-tables-3] Tall cell height redistribution algorithm [css-tables-3] Table height redistribution algorithm Oct 21, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
css-tables-3 Current Work
Projects
None yet
Development

No branches or pull requests

2 participants