Modified: trunk/Source/WebCore/ChangeLog (279548 => 279549)
--- trunk/Source/WebCore/ChangeLog 2021-07-04 14:10:24 UTC (rev 279548)
+++ trunk/Source/WebCore/ChangeLog 2021-07-04 15:47:12 UTC (rev 279549)
@@ -1,3 +1,17 @@
+2021-07-04 Zalan Bujtas <[email protected]>
+
+ [LFC][TFC] Cleanup computedPreferredWidthForColumns and distributeAvailableSpace functions
+ https://bugs.webkit.org/show_bug.cgi?id=227669
+
+ Reviewed by Antti Koivisto.
+
+ Let's create some sub-scopes within these long functions to make them read better.
+
+ * layout/formattingContexts/table/TableFormattingContext.cpp:
+ (WebCore::Layout::TableFormattingContext::computedPreferredWidthForColumns):
+ * layout/formattingContexts/table/TableLayout.cpp:
+ (WebCore::Layout::distributeAvailableSpace):
+
2021-07-04 Alan Bujtas <[email protected]>
[LFC][TFC] 100% percent width column(s) make the table stretch
Modified: trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingContext.cpp (279548 => 279549)
--- trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingContext.cpp 2021-07-04 14:10:24 UTC (rev 279548)
+++ trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingContext.cpp 2021-07-04 15:47:12 UTC (rev 279549)
@@ -324,114 +324,127 @@
// 2. Collect the fixed width <col>s.
auto& columnList = grid.columns().list();
auto& formattingGeometry = this->formattingGeometry();
- for (auto& column : columnList) {
- auto fixedWidth = [&] () -> std::optional<LayoutUnit> {
- auto* columnBox = column.box();
- if (!columnBox) {
- // Anonymous columns don't have associated layout boxes and can't have fixed col size.
- return { };
- }
- if (auto width = columnBox->columnWidth())
- return width;
- return formattingGeometry.computedColumnWidth(*columnBox);
- }();
+ auto collectColsFixedWidth = [&] {
+ for (auto& column : columnList) {
+ auto fixedWidth = [&] () -> std::optional<LayoutUnit> {
+ auto* columnBox = column.box();
+ if (!columnBox) {
+ // Anonymous columns don't have associated layout boxes and can't have fixed col size.
+ return { };
+ }
+ if (auto width = columnBox->columnWidth())
+ return width;
+ return formattingGeometry.computedColumnWidth(*columnBox);
+ }();
if (fixedWidth)
column.setComputedLogicalWidth({ *fixedWidth, LengthType::Fixed });
- }
+ }
+ };
+ collectColsFixedWidth();
auto hasColumnWithPercentWidth = false;
auto hasColumnWithFixedWidth = false;
Vector<std::optional<LayoutUnit>> maximumFixedColumnWidths(columnList.size());
Vector<std::optional<float>> maximumPercentColumnWidths(columnList.size());
- for (auto& cell : grid.cells()) {
- auto& cellBox = cell->box();
- ASSERT(cellBox.establishesBlockFormattingContext());
- auto intrinsicWidth = formattingState.intrinsicWidthConstraintsForBox(cellBox);
- if (!intrinsicWidth) {
- intrinsicWidth = formattingGeometry.intrinsicWidthConstraintsForCellContent(*cell);
- formattingState.setIntrinsicWidthConstraintsForBox(cellBox, *intrinsicWidth);
- }
- auto cellPosition = cell->position();
- auto& cellStyle = cellBox.style();
- // Expand it with border and padding.
- auto horizontalBorderAndPaddingWidth = formattingGeometry.computedCellBorder(*cell).width()
- + formattingGeometry.fixedValue(cellStyle.paddingLeft()).value_or(0)
- + formattingGeometry.fixedValue(cellStyle.paddingRight()).value_or(0);
- intrinsicWidth->expand(horizontalBorderAndPaddingWidth);
- // Spanner cells put their intrinsic widths on the initial slots.
- grid.slot(cellPosition)->setWidthConstraints(*intrinsicWidth);
+ auto collectCellsIntrinsicWidthConstraints = [&] {
+ for (auto& cell : grid.cells()) {
+ auto& cellBox = cell->box();
+ ASSERT(cellBox.establishesBlockFormattingContext());
- auto cellLogicalWidth = cellStyle.logicalWidth();
- auto columnIndex = cellPosition.column;
- switch (cellLogicalWidth.type()) {
- case LengthType::Fixed: {
- auto fixedWidth = LayoutUnit { cellLogicalWidth.value() } + horizontalBorderAndPaddingWidth;
- maximumFixedColumnWidths[columnIndex] = std::max(maximumFixedColumnWidths[columnIndex].value_or(0_lu), fixedWidth);
- hasColumnWithFixedWidth = true;
- break;
+ auto intrinsicWidth = formattingState.intrinsicWidthConstraintsForBox(cellBox);
+ if (!intrinsicWidth) {
+ intrinsicWidth = formattingGeometry.intrinsicWidthConstraintsForCellContent(*cell);
+ formattingState.setIntrinsicWidthConstraintsForBox(cellBox, *intrinsicWidth);
+ }
+ auto cellPosition = cell->position();
+ auto& cellStyle = cellBox.style();
+ // Expand it with border and padding.
+ auto horizontalBorderAndPaddingWidth = formattingGeometry.computedCellBorder(*cell).width()
+ + formattingGeometry.fixedValue(cellStyle.paddingLeft()).value_or(0)
+ + formattingGeometry.fixedValue(cellStyle.paddingRight()).value_or(0);
+ intrinsicWidth->expand(horizontalBorderAndPaddingWidth);
+ // Spanner cells put their intrinsic widths on the initial slots.
+ grid.slot(cellPosition)->setWidthConstraints(*intrinsicWidth);
+
+ auto cellLogicalWidth = cellStyle.logicalWidth();
+ auto columnIndex = cellPosition.column;
+ switch (cellLogicalWidth.type()) {
+ case LengthType::Fixed: {
+ auto fixedWidth = LayoutUnit { cellLogicalWidth.value() } + horizontalBorderAndPaddingWidth;
+ maximumFixedColumnWidths[columnIndex] = std::max(maximumFixedColumnWidths[columnIndex].value_or(0_lu), fixedWidth);
+ hasColumnWithFixedWidth = true;
+ break;
+ }
+ case LengthType::Percent: {
+ maximumPercentColumnWidths[columnIndex] = std::max(maximumPercentColumnWidths[columnIndex].value_or(0.f), cellLogicalWidth.percent());
+ hasColumnWithPercentWidth = true;
+ break;
+ }
+ case LengthType::Relative:
+ ASSERT_NOT_IMPLEMENTED_YET();
+ break;
+ default:
+ break;
+ }
}
- case LengthType::Percent: {
- maximumPercentColumnWidths[columnIndex] = std::max(maximumPercentColumnWidths[columnIndex].value_or(0.f), cellLogicalWidth.percent());
- hasColumnWithPercentWidth = true;
- break;
- }
- case LengthType::Relative:
- ASSERT_NOT_IMPLEMENTED_YET();
- break;
- default:
- break;
- }
- }
+ };
+ collectCellsIntrinsicWidthConstraints();
Vector<IntrinsicWidthConstraints> columnIntrinsicWidths(columnList.size());
- // 3. Collect he min/max width for each column but ignore column spans for now.
Vector<SlotPosition> spanningCellPositionList;
size_t numberOfActualColumns = 0;
- for (size_t columnIndex = 0; columnIndex < columnList.size(); ++columnIndex) {
- auto columnHasNonSpannedCell = false;
- for (size_t rowIndex = 0; rowIndex < grid.rows().size(); ++rowIndex) {
- auto& slot = *grid.slot({ columnIndex, rowIndex });
- if (slot.isColumnSpanned())
- continue;
- columnHasNonSpannedCell = true;
- if (slot.hasColumnSpan()) {
- spanningCellPositionList.append({ columnIndex, rowIndex });
- continue;
+ auto computeColumnsIntrinsicWidthConstraints = [&] {
+ // 3. Collect he min/max width for each column but ignore column spans for now.
+ for (size_t columnIndex = 0; columnIndex < columnList.size(); ++columnIndex) {
+ auto columnHasNonSpannedCell = false;
+ for (size_t rowIndex = 0; rowIndex < grid.rows().size(); ++rowIndex) {
+ auto& slot = *grid.slot({ columnIndex, rowIndex });
+ if (slot.isColumnSpanned())
+ continue;
+ columnHasNonSpannedCell = true;
+ if (slot.hasColumnSpan()) {
+ spanningCellPositionList.append({ columnIndex, rowIndex });
+ continue;
+ }
+ auto widthConstraints = slot.widthConstraints();
+ if (auto fixedColumnWidth = maximumFixedColumnWidths[columnIndex])
+ widthConstraints.maximum = std::max(*fixedColumnWidth, widthConstraints.minimum);
+
+ columnIntrinsicWidths[columnIndex].minimum = std::max(widthConstraints.minimum, columnIntrinsicWidths[columnIndex].minimum);
+ columnIntrinsicWidths[columnIndex].maximum = std::max(widthConstraints.maximum, columnIntrinsicWidths[columnIndex].maximum);
}
- auto widthConstraints = slot.widthConstraints();
- if (auto fixedColumnWidth = maximumFixedColumnWidths[columnIndex])
- widthConstraints.maximum = std::max(*fixedColumnWidth, widthConstraints.minimum);
-
- columnIntrinsicWidths[columnIndex].minimum = std::max(widthConstraints.minimum, columnIntrinsicWidths[columnIndex].minimum);
- columnIntrinsicWidths[columnIndex].maximum = std::max(widthConstraints.maximum, columnIntrinsicWidths[columnIndex].maximum);
+ if (columnHasNonSpannedCell)
+ ++numberOfActualColumns;
}
- if (columnHasNonSpannedCell)
- ++numberOfActualColumns;
- }
+ };
+ computeColumnsIntrinsicWidthConstraints();
- // 4. Distribute the spanning min/max widths.
- for (auto spanningCellPosition : spanningCellPositionList) {
- auto& slot = *grid.slot(spanningCellPosition);
- auto& cell = slot.cell();
- ASSERT(slot.hasColumnSpan());
- auto widthConstraintsToDistribute = slot.widthConstraints();
- for (size_t columnSpanIndex = cell.startColumn(); columnSpanIndex < cell.endColumn(); ++columnSpanIndex)
- widthConstraintsToDistribute -= columnIntrinsicWidths[columnSpanIndex];
- // <table style="border-spacing: 50px"><tr><td colspan=2>long long text</td></tr><tr><td>lo</td><td>xt</td><tr></table>
- // [long long text]
- // [lo] [xt]
- // While it looks like the spanning cell has to distribute all its spanning width, the border-spacing takes most of the space and
- // no distribution is needed at all.
- widthConstraintsToDistribute -= (cell.columnSpan() - 1) * grid.horizontalSpacing();
- // FIXME: Check if fixed width columns should be skipped here.
- widthConstraintsToDistribute.minimum = std::max(LayoutUnit { }, widthConstraintsToDistribute.minimum / cell.columnSpan());
- widthConstraintsToDistribute.maximum = std::max(LayoutUnit { }, widthConstraintsToDistribute.maximum / cell.columnSpan());
- if (widthConstraintsToDistribute.minimum || widthConstraintsToDistribute.maximum) {
+ auto resolveSpanningCells = [&] {
+ // 4. Distribute the spanning min/max widths.
+ for (auto spanningCellPosition : spanningCellPositionList) {
+ auto& slot = *grid.slot(spanningCellPosition);
+ auto& cell = slot.cell();
+ ASSERT(slot.hasColumnSpan());
+ auto widthConstraintsToDistribute = slot.widthConstraints();
for (size_t columnSpanIndex = cell.startColumn(); columnSpanIndex < cell.endColumn(); ++columnSpanIndex)
- columnIntrinsicWidths[columnSpanIndex] += widthConstraintsToDistribute;
+ widthConstraintsToDistribute -= columnIntrinsicWidths[columnSpanIndex];
+ // <table style="border-spacing: 50px"><tr><td colspan=2>long long text</td></tr><tr><td>lo</td><td>xt</td><tr></table>
+ // [long long text]
+ // [lo] [xt]
+ // While it looks like the spanning cell has to distribute all its spanning width, the border-spacing takes most of the space and
+ // no distribution is needed at all.
+ widthConstraintsToDistribute -= (cell.columnSpan() - 1) * grid.horizontalSpacing();
+ // FIXME: Check if fixed width columns should be skipped here.
+ widthConstraintsToDistribute.minimum = std::max(LayoutUnit { }, widthConstraintsToDistribute.minimum / cell.columnSpan());
+ widthConstraintsToDistribute.maximum = std::max(LayoutUnit { }, widthConstraintsToDistribute.maximum / cell.columnSpan());
+ if (widthConstraintsToDistribute.minimum || widthConstraintsToDistribute.maximum) {
+ for (size_t columnSpanIndex = cell.startColumn(); columnSpanIndex < cell.endColumn(); ++columnSpanIndex)
+ columnIntrinsicWidths[columnSpanIndex] += widthConstraintsToDistribute;
+ }
}
- }
+ };
+ resolveSpanningCells();
// 5. The table min/max widths is just the accumulated column constraints with the percent adjustment.
auto tableWidthConstraints = IntrinsicWidthConstraints { };
@@ -438,13 +451,19 @@
for (auto& columnIntrinsicWidth : columnIntrinsicWidths)
tableWidthConstraints += columnIntrinsicWidth;
- // 6. Adjust the table max width with the percent column values if applicable.
- if (hasColumnWithFixedWidth && !hasColumnWithPercentWidth) {
- for (size_t columnIndex = 0; columnIndex < columnList.size(); ++columnIndex) {
- if (auto fixedWidth = maximumFixedColumnWidths[columnIndex])
- columnList[columnIndex].setComputedLogicalWidth({ *fixedWidth, LengthType::Fixed });
- }
- } else if (hasColumnWithPercentWidth) {
+ auto adjustColumnsWithPercentAndFixedWidthValues = [&] {
+ // 6. Adjust the table max width with the percent column values if applicable.
+ if (!hasColumnWithFixedWidth && !hasColumnWithPercentWidth)
+ return;
+
+ if (hasColumnWithFixedWidth && !hasColumnWithPercentWidth) {
+ for (size_t columnIndex = 0; columnIndex < columnList.size(); ++columnIndex) {
+ if (auto fixedWidth = maximumFixedColumnWidths[columnIndex])
+ columnList[columnIndex].setComputedLogicalWidth({ *fixedWidth, LengthType::Fixed });
+ }
+ return;
+ }
+
auto remainingPercent = 100.0f;
auto percentMaximumWidth = LayoutUnit { };
auto nonPercentColumnsWidth = LayoutUnit { };
@@ -480,9 +499,10 @@
// It may very well be an ancient bug we need to support (it maps to the epsilon value in AutoTableLayout::computeIntrinsicLogicalWidths which is to avoid division by zero).
adjustedMaximumWidth = LayoutUnit::max();
}
+ tableWidthConstraints.maximum = std::max(tableWidthConstraints.maximum, adjustedMaximumWidth);
+ };
+ adjustColumnsWithPercentAndFixedWidthValues();
- tableWidthConstraints.maximum = std::max(tableWidthConstraints.maximum, adjustedMaximumWidth);
- }
// Expand the preferred width with leading and trailing cell spacing (note that column spanners count as one cell).
tableWidthConstraints += (numberOfActualColumns + 1) * grid.horizontalSpacing();
return tableWidthConstraints;
Modified: trunk/Source/WebCore/layout/formattingContexts/table/TableLayout.cpp (279548 => 279549)
--- trunk/Source/WebCore/layout/formattingContexts/table/TableLayout.cpp 2021-07-04 14:10:24 UTC (rev 279548)
+++ trunk/Source/WebCore/layout/formattingContexts/table/TableLayout.cpp 2021-07-04 15:47:12 UTC (rev 279549)
@@ -119,206 +119,219 @@
{
auto& columns = grid.columns();
auto& rows = grid.rows();
- // 1. Collect the non-spanning spaces first. They are used for the final distribution as well as for distributing the spanning space.
Vector<std::optional<GridSpace>> resolvedItems(SpanType::size(grid));
- for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
- for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex) {
- auto& slot = *grid.slot({ columnIndex, rowIndex });
- if (SpanType::hasSpan(slot) || SpanType::isSpanned(slot))
- continue;
- auto index = SpanType::index(columnIndex, rowIndex);
- auto currentSpace = slotSpace(slot, index);
- if (!resolvedItems[index]) {
- resolvedItems[index] = currentSpace;
- continue;
+ auto collectNonSpanningCells = [&] {
+ // 1. Collect the non-spanning spaces first. They are used for the final distribution as well as for distributing the spanning space.
+ for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
+ for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex) {
+ auto& slot = *grid.slot({ columnIndex, rowIndex });
+ if (SpanType::hasSpan(slot) || SpanType::isSpanned(slot))
+ continue;
+ auto index = SpanType::index(columnIndex, rowIndex);
+ auto currentSpace = slotSpace(slot, index);
+ if (!resolvedItems[index]) {
+ resolvedItems[index] = currentSpace;
+ continue;
+ }
+ auto& resolvedItem = resolvedItems[index];
+ // FIXME: Add support for mixed column/row types e.g. first row first column is fixed, while second row first column is percent.
+ ASSERT(resolvedItem->type == currentSpace.type);
+ resolvedItem->preferredSize = std::max(resolvedItem->preferredSize, currentSpace.preferredSize);
+ resolvedItem->flexBase = std::max(resolvedItem->flexBase, currentSpace.flexBase);
}
- auto& resolvedItem = resolvedItems[index];
- // FIXME: Add support for mixed column/row types e.g. first row first column is fixed, while second row first column is percent.
- ASSERT(resolvedItem->type == currentSpace.type);
- resolvedItem->preferredSize = std::max(resolvedItem->preferredSize, currentSpace.preferredSize);
- resolvedItem->flexBase = std::max(resolvedItem->flexBase, currentSpace.flexBase);
}
- }
+ };
+ collectNonSpanningCells();
- // 2. Collect the spanning cells.
- struct SpanningCell {
- SlotPosition position;
- GridSpace unresolvedSpace;
- };
- Vector<SpanningCell> spanningCells;
- for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex) {
- for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
- auto& slot = *grid.slot({ columnIndex, rowIndex });
- if (SpanType::hasSpan(slot))
- spanningCells.append({ { columnIndex, rowIndex }, slotSpace(slot, SpanType::index(columnIndex, rowIndex)) });
+ auto collectAndDistributeSpanningCells = [&] {
+ // 2. Collect the spanning cells.
+ struct SpanningCell {
+ SlotPosition position;
+ GridSpace unresolvedSpace;
+ };
+ Vector<SpanningCell> spanningCells;
+ for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex) {
+ for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
+ auto& slot = *grid.slot({ columnIndex, rowIndex });
+ if (SpanType::hasSpan(slot))
+ spanningCells.append({ { columnIndex, rowIndex }, slotSpace(slot, SpanType::index(columnIndex, rowIndex)) });
+ }
}
- }
- // We need these spanning cells in the order of the number of columns/rows they span so that
- // we can resolve overlapping spans starting with the shorter ones e.g.
- // <td colspan=4>#a</td><td>#b</td>
- // <td colspan=2>#c</td><td colspan=3>#d</td>
- std::sort(spanningCells.begin(), spanningCells.end(), [&] (auto& a, auto& b) {
- return SpanType::spanCount(grid.slot(a.position)->cell()) < SpanType::spanCount(grid.slot(b.position)->cell());
- });
+ // We need these spanning cells in the order of the number of columns/rows they span so that
+ // we can resolve overlapping spans starting with the shorter ones e.g.
+ // <td colspan=4>#a</td><td>#b</td>
+ // <td colspan=2>#c</td><td colspan=3>#d</td>
+ std::sort(spanningCells.begin(), spanningCells.end(), [&] (auto& a, auto& b) {
+ return SpanType::spanCount(grid.slot(a.position)->cell()) < SpanType::spanCount(grid.slot(b.position)->cell());
+ });
- // 3. Distribute the spanning cells' mimimum space across the columns/rows using the non-spanning spaces.
- // e.g. [ 1 ][ 5 ][ 1 ]
- // [ 9 ][ 1 ]
- // The initial widths are: [ 2 ][ 7 ][ 1 ]
- for (auto spanningCell : spanningCells) {
- auto& cell = grid.slot(spanningCell.position)->cell();
- auto unresolvedSpanningSpace = spanningCell.unresolvedSpace;
- if (!resolvedItems[SpanType::startSpan(cell)] || !resolvedItems[SpanType::endSpan(cell) - 1]) {
- // <td colspan=4>#a</td><td>#b</td>
- // <td colspan=2>#c</td><td colspan=3>#d</td>
- // Unresolved columns are: 1 2 3 4
- // 1. Take colspan=2 (shortest span) and resolve column 1 and 2
- // 2. Take colspan=3 and resolve column 3 and 4 (5 is resolved because it already has a non-spanning cell).
- // 3. colspan=4 needs no resolving because all the spanned columns (1 2 3 4) have already been resolved.
- auto unresolvedColumnCount = cell.columnSpan();
- for (auto spanIndex = SpanType::startSpan(cell); spanIndex < SpanType::endSpan(cell); ++spanIndex) {
- if (!resolvedItems[spanIndex])
- continue;
+ // 3. Distribute the spanning cells' mimimum space across the columns/rows using the non-spanning spaces.
+ // e.g. [ 1 ][ 5 ][ 1 ]
+ // [ 9 ][ 1 ]
+ // The initial widths are: [ 2 ][ 7 ][ 1 ]
+ for (auto spanningCell : spanningCells) {
+ auto& cell = grid.slot(spanningCell.position)->cell();
+ auto unresolvedSpanningSpace = spanningCell.unresolvedSpace;
+ if (!resolvedItems[SpanType::startSpan(cell)] || !resolvedItems[SpanType::endSpan(cell) - 1]) {
+ // <td colspan=4>#a</td><td>#b</td>
+ // <td colspan=2>#c</td><td colspan=3>#d</td>
+ // Unresolved columns are: 1 2 3 4
+ // 1. Take colspan=2 (shortest span) and resolve column 1 and 2
+ // 2. Take colspan=3 and resolve column 3 and 4 (5 is resolved because it already has a non-spanning cell).
+ // 3. colspan=4 needs no resolving because all the spanned columns (1 2 3 4) have already been resolved.
+ auto unresolvedColumnCount = cell.columnSpan();
+ for (auto spanIndex = SpanType::startSpan(cell); spanIndex < SpanType::endSpan(cell); ++spanIndex) {
+ if (!resolvedItems[spanIndex])
+ continue;
+ ASSERT(unresolvedColumnCount);
+ --unresolvedColumnCount;
+ unresolvedSpanningSpace -= *resolvedItems[spanIndex];
+ }
ASSERT(unresolvedColumnCount);
- --unresolvedColumnCount;
- unresolvedSpanningSpace -= *resolvedItems[spanIndex];
- }
- ASSERT(unresolvedColumnCount);
- auto equalSpaceForSpannedColumns = unresolvedSpanningSpace / unresolvedColumnCount;
- for (auto spanIndex = SpanType::startSpan(cell); spanIndex < SpanType::endSpan(cell); ++spanIndex) {
- if (resolvedItems[spanIndex])
+ auto equalSpaceForSpannedColumns = unresolvedSpanningSpace / unresolvedColumnCount;
+ for (auto spanIndex = SpanType::startSpan(cell); spanIndex < SpanType::endSpan(cell); ++spanIndex) {
+ if (resolvedItems[spanIndex])
+ continue;
+ resolvedItems[spanIndex] = equalSpaceForSpannedColumns;
+ }
+ } else {
+ // 1. Collect the non-spanning resolved spaces.
+ // 2. Distribute the extra space among the spanned columns/rows based on the resolved space values.
+ // e.g. spanning width: [ 9 ]. Resolved widths for the spanned columns: [ 1 ] [ 2 ]
+ // New resolved widths: [ 3 ] [ 6 ].
+ auto resolvedSpanningSpace = GridSpace { };
+ for (auto spanIndex = SpanType::startSpan(cell); spanIndex < SpanType::endSpan(cell); ++spanIndex)
+ resolvedSpanningSpace += *resolvedItems[spanIndex];
+ if (resolvedSpanningSpace.preferredSize >= unresolvedSpanningSpace.preferredSize) {
+ // The spanning cell fits the spanned columns/rows just fine. Nothing to distribute.
continue;
- resolvedItems[spanIndex] = equalSpaceForSpannedColumns;
+ }
+ auto spacing = SpanType::spacing(grid) * (SpanType::spanCount(cell) - 1);
+ auto spaceToDistribute = unresolvedSpanningSpace - GridSpace { { }, spacing, spacing } - resolvedSpanningSpace;
+ if (!spaceToDistribute.isEmpty()) {
+ auto columnsFlexBase = spaceToDistribute.flexBase / resolvedSpanningSpace.flexBase;
+ for (auto spanIndex = SpanType::startSpan(cell); spanIndex < SpanType::endSpan(cell); ++spanIndex)
+ *resolvedItems[spanIndex] += GridSpace { resolvedItems[spanIndex]->type, resolvedItems[spanIndex]->preferredSize * columnsFlexBase, resolvedItems[spanIndex]->flexBase * columnsFlexBase };
+ }
}
- } else {
- // 1. Collect the non-spaning resolved spaces.
- // 2. Distribute the extra space among the spanned columns/rows based on the resolved space values.
- // e.g. spanning width: [ 9 ]. Resolved widths for the spanned columns: [ 1 ] [ 2 ]
- // New resolved widths: [ 3 ] [ 6 ].
- auto resolvedSpanningSpace = GridSpace { };
- for (auto spanIndex = SpanType::startSpan(cell); spanIndex < SpanType::endSpan(cell); ++spanIndex)
- resolvedSpanningSpace += *resolvedItems[spanIndex];
- if (resolvedSpanningSpace.preferredSize >= unresolvedSpanningSpace.preferredSize) {
- // The spanning cell fits the spanned columns/rows just fine. Nothing to distribute.
- continue;
- }
- auto spacing = SpanType::spacing(grid) * (SpanType::spanCount(cell) - 1);
- auto spaceToDistribute = unresolvedSpanningSpace - GridSpace { { }, spacing, spacing } - resolvedSpanningSpace;
- if (!spaceToDistribute.isEmpty()) {
- auto columnsFlexBase = spaceToDistribute.flexBase / resolvedSpanningSpace.flexBase;
- for (auto spanIndex = SpanType::startSpan(cell); spanIndex < SpanType::endSpan(cell); ++spanIndex)
- *resolvedItems[spanIndex] += GridSpace { resolvedItems[spanIndex]->type, resolvedItems[spanIndex]->preferredSize * columnsFlexBase, resolvedItems[spanIndex]->flexBase * columnsFlexBase };
- }
}
- }
- // 4. Distribute the extra space using the final resolved sizes.
+ };
+ collectAndDistributeSpanningCells();
+
Vector<LayoutUnit> distributedSpaces(resolvedItems.size());
- float spaceToDistribute = availableSpace - (resolvedItems.size() + 1) * SpanType::spacing(grid);
- auto adjustabledSpace = GridSpace { };
- for (auto& resolvedItem : resolvedItems) {
- ASSERT(resolvedItem);
- adjustabledSpace += *resolvedItem;
- }
- spaceToDistribute -= adjustabledSpace.preferredSize;
+ auto spaceToDistribute = 0.f;
+ auto computeSpaceToDistribute = [&] {
+ // 4. Distribute the extra space using the final resolved sizes.
+ spaceToDistribute = availableSpace - (resolvedItems.size() + 1) * SpanType::spacing(grid);
+ auto adjustabledSpace = GridSpace { };
+ for (auto& resolvedItem : resolvedItems) {
+ ASSERT(resolvedItem);
+ adjustabledSpace += *resolvedItem;
+ }
+ spaceToDistribute -= adjustabledSpace.preferredSize;
+ };
+ computeSpaceToDistribute();
+
// Let's start with the preferred size for each column.
for (size_t columnIndex = 0; columnIndex < resolvedItems.size(); ++columnIndex)
distributedSpaces[columnIndex] = LayoutUnit { resolvedItems[columnIndex]->preferredSize };
- if (!spaceToDistribute) {
- // Preferred size covers the spaces. There's nothing extra spaace to distribute.
- return distributedSpaces;
- }
+ auto distributeSpace = [&] {
+ if (!spaceToDistribute)
+ return;
+ // Setup the priority lists. We use these when expanding/shrinking slots.
+ Vector<size_t> autoColumnIndexes;
+ Vector<size_t> relativeColumnIndexes;
+ Vector<size_t> fixedColumnIndexes;
+ Vector<size_t> percentColumnIndexes;
- // Setup the priority lists. We use these when expanding/shrinking slots.
- Vector<size_t> autoColumnIndexes;
- Vector<size_t> relativeColumnIndexes;
- Vector<size_t> fixedColumnIndexes;
- Vector<size_t> percentColumnIndexes;
+ for (size_t columnIndex = 0; columnIndex < resolvedItems.size(); ++columnIndex) {
+ switch (resolvedItems[columnIndex]->type) {
+ case GridSpace::Type::Percent:
+ percentColumnIndexes.append(columnIndex);
+ break;
+ case GridSpace::Type::Fixed:
+ fixedColumnIndexes.append(columnIndex);
+ break;
+ case GridSpace::Type::Relative:
+ relativeColumnIndexes.append(columnIndex);
+ break;
+ case GridSpace::Type::Auto:
+ autoColumnIndexes.append(columnIndex);
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+ }
- for (size_t columnIndex = 0; columnIndex < resolvedItems.size(); ++columnIndex) {
- switch (resolvedItems[columnIndex]->type) {
- case GridSpace::Type::Percent:
- percentColumnIndexes.append(columnIndex);
- break;
- case GridSpace::Type::Fixed:
- fixedColumnIndexes.append(columnIndex);
- break;
- case GridSpace::Type::Relative:
- relativeColumnIndexes.append(columnIndex);
- break;
- case GridSpace::Type::Auto:
- autoColumnIndexes.append(columnIndex);
- break;
- default:
- ASSERT_NOT_REACHED();
- break;
+ if (spaceToDistribute > 0) {
+ // Each column can get some extra space.
+ auto hasSpaceToDistribute = [&] {
+ ASSERT(spaceToDistribute > -LayoutUnit::epsilon());
+ return spaceToDistribute > LayoutUnit::epsilon();
+ };
+
+ auto expandSpace = [&](const auto& columnIndexes) {
+ auto adjustabledSpace = GridSpace { };
+ for (auto& columnIndex : columnIndexes)
+ adjustabledSpace += *resolvedItems[columnIndex];
+
+ auto columnsFlexBase = adjustabledSpace.flexBase ? spaceToDistribute / adjustabledSpace.flexBase : 0.f;
+ for (auto& columnIndex : columnIndexes) {
+ auto extraSpace = columnsFlexBase * resolvedItems[columnIndex]->flexBase;
+ distributedSpaces[columnIndex] += LayoutUnit { extraSpace };
+ spaceToDistribute -= extraSpace;
+ if (!hasSpaceToDistribute())
+ return;
+ }
+ };
+ // We distribute the extra space among columns in the priority order as follows:
+ expandSpace(fixedColumnIndexes);
+ if (hasSpaceToDistribute())
+ expandSpace(percentColumnIndexes);
+ if (hasSpaceToDistribute())
+ expandSpace(relativeColumnIndexes);
+ if (hasSpaceToDistribute())
+ expandSpace(autoColumnIndexes);
+ ASSERT(!hasSpaceToDistribute());
+ return;
}
- }
+ // Can't accommodate the preferred width. Let's use the priority list to shrink columns.
+ auto spaceNeeded = -spaceToDistribute;
- if (spaceToDistribute > 0) {
- // Each column can get some extra space.
- auto hasSpaceToDistribute = [&] {
- ASSERT(spaceToDistribute > -LayoutUnit::epsilon());
- return spaceToDistribute > LayoutUnit::epsilon();
+ auto needsMoreSpace = [&] {
+ ASSERT(spaceNeeded > -LayoutUnit::epsilon());
+ return spaceNeeded > LayoutUnit::epsilon();
};
- auto expandSpace = [&](const auto& columnIndexes) {
+ auto shrinkSpace = [&](const auto& columnIndexes) {
auto adjustabledSpace = GridSpace { };
for (auto& columnIndex : columnIndexes)
adjustabledSpace += *resolvedItems[columnIndex];
- auto columnsFlexBase = adjustabledSpace.flexBase ? spaceToDistribute / adjustabledSpace.flexBase : 0.f;
+ auto columnsFlexBase = adjustabledSpace.flexBase ? spaceNeeded / adjustabledSpace.flexBase : 0.f;
for (auto& columnIndex : columnIndexes) {
- auto extraSpace = columnsFlexBase * resolvedItems[columnIndex]->flexBase;
- distributedSpaces[columnIndex] += LayoutUnit { extraSpace };
- spaceToDistribute -= extraSpace;
- if (!hasSpaceToDistribute())
+ auto& resolvedItem = *resolvedItems[columnIndex];
+ auto spaceToRemove = std::min(resolvedItem.preferredSize - resolvedItem.minimumSize, columnsFlexBase * resolvedItem.flexBase);
+ spaceToRemove = std::min(spaceToRemove, spaceNeeded);
+ distributedSpaces[columnIndex] -= spaceToRemove;
+ spaceNeeded -= spaceToRemove;
+ if (!needsMoreSpace())
return;
}
};
- // We distribute the extra space among columns in the priority order as follows:
- expandSpace(fixedColumnIndexes);
- if (hasSpaceToDistribute())
- expandSpace(percentColumnIndexes);
- if (hasSpaceToDistribute())
- expandSpace(relativeColumnIndexes);
- if (hasSpaceToDistribute())
- expandSpace(autoColumnIndexes);
- ASSERT(!hasSpaceToDistribute());
- return distributedSpaces;
- }
- // Can't accomodate the preferred width. Let's use the priority list to shrink columns.
- auto spaceNeeded = -spaceToDistribute;
-
- auto needsMoreSpace = [&] {
- ASSERT(spaceNeeded > -LayoutUnit::epsilon());
- return spaceNeeded > LayoutUnit::epsilon();
+ shrinkSpace(autoColumnIndexes);
+ if (needsMoreSpace())
+ shrinkSpace(relativeColumnIndexes);
+ if (needsMoreSpace())
+ shrinkSpace(fixedColumnIndexes);
+ if (needsMoreSpace())
+ shrinkSpace(percentColumnIndexes);
+ ASSERT(!needsMoreSpace());
};
+ distributeSpace();
- auto shrinkSpace = [&](const auto& columnIndexes) {
- auto adjustabledSpace = GridSpace { };
- for (auto& columnIndex : columnIndexes)
- adjustabledSpace += *resolvedItems[columnIndex];
-
- auto columnsFlexBase = adjustabledSpace.flexBase ? spaceNeeded / adjustabledSpace.flexBase : 0.f;
- for (auto& columnIndex : columnIndexes) {
- auto& resolvedItem = *resolvedItems[columnIndex];
- auto spaceToRemove = std::min(resolvedItem.preferredSize - resolvedItem.minimumSize, columnsFlexBase * resolvedItem.flexBase);
- spaceToRemove = std::min(spaceToRemove, spaceNeeded);
- distributedSpaces[columnIndex] -= spaceToRemove;
- spaceNeeded -= spaceToRemove;
- if (!needsMoreSpace())
- return;
- }
- };
- shrinkSpace(autoColumnIndexes);
- if (needsMoreSpace())
- shrinkSpace(relativeColumnIndexes);
- if (needsMoreSpace())
- shrinkSpace(fixedColumnIndexes);
- if (needsMoreSpace())
- shrinkSpace(percentColumnIndexes);
- ASSERT(!needsMoreSpace());
return distributedSpaces;
}