Title: [261064] trunk/Source/WebCore
Revision
261064
Author
[email protected]
Date
2020-05-03 11:23:39 -0700 (Sun, 03 May 2020)

Log Message

[LFC][TFC] Turns horizontal space distribution into a generic space distribution
https://bugs.webkit.org/show_bug.cgi?id=211352

Reviewed by Antti Koivisto.

Horizontal(column) and vertical(row) space distributions use essentially the same logic to distribute
the extra space among the columns/rows.
This patch turns the horizontal space distribution function into a generic space distribution code so
that we can use it for row sizing as well.

* layout/tableformatting/TableFormattingContext.cpp:
(WebCore::Layout::ColumnSpan::hasSpan):
(WebCore::Layout::ColumnSpan::isSpanned):
(WebCore::Layout::ColumnSpan::spanCount):
(WebCore::Layout::ColumnSpan::startSpan):
(WebCore::Layout::ColumnSpan::endSpan):
(WebCore::Layout::ColumnSpan::index):
(WebCore::Layout::ColumnSpan::size):
(WebCore::Layout::ColumnSpan::spacing):
(WebCore::Layout::distributeAvailableSpace):
(WebCore::Layout::TableFormattingContext::computeAndDistributeExtraHorizontalSpace):
* layout/tableformatting/TableGrid.h:
(WebCore::Layout::TableGrid::slot const):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (261063 => 261064)


--- trunk/Source/WebCore/ChangeLog	2020-05-03 18:16:31 UTC (rev 261063)
+++ trunk/Source/WebCore/ChangeLog	2020-05-03 18:23:39 UTC (rev 261064)
@@ -1,3 +1,29 @@
+2020-05-03  Zalan Bujtas  <[email protected]>
+
+        [LFC][TFC] Turns horizontal space distribution into a generic space distribution
+        https://bugs.webkit.org/show_bug.cgi?id=211352
+
+        Reviewed by Antti Koivisto.
+
+        Horizontal(column) and vertical(row) space distributions use essentially the same logic to distribute
+        the extra space among the columns/rows.
+        This patch turns the horizontal space distribution function into a generic space distribution code so
+        that we can use it for row sizing as well.  
+
+        * layout/tableformatting/TableFormattingContext.cpp:
+        (WebCore::Layout::ColumnSpan::hasSpan):
+        (WebCore::Layout::ColumnSpan::isSpanned):
+        (WebCore::Layout::ColumnSpan::spanCount):
+        (WebCore::Layout::ColumnSpan::startSpan):
+        (WebCore::Layout::ColumnSpan::endSpan):
+        (WebCore::Layout::ColumnSpan::index):
+        (WebCore::Layout::ColumnSpan::size):
+        (WebCore::Layout::ColumnSpan::spacing):
+        (WebCore::Layout::distributeAvailableSpace):
+        (WebCore::Layout::TableFormattingContext::computeAndDistributeExtraHorizontalSpace):
+        * layout/tableformatting/TableGrid.h:
+        (WebCore::Layout::TableGrid::slot const):
+
 2020-05-03  Youenn Fablet  <[email protected]>
 
         AudioMediaStreamTrackRendererCocoa should create/start/stop its remote unit on the main thread

Modified: trunk/Source/WebCore/layout/tableformatting/TableFormattingContext.cpp (261063 => 261064)


--- trunk/Source/WebCore/layout/tableformatting/TableFormattingContext.cpp	2020-05-03 18:16:31 UTC (rev 261063)
+++ trunk/Source/WebCore/layout/tableformatting/TableFormattingContext.cpp	2020-05-03 18:23:39 UTC (rev 261064)
@@ -347,140 +347,160 @@
     return tableWidthConstraints;
 }
 
-void TableFormattingContext::computeAndDistributeExtraHorizontalSpace(LayoutUnit availableHorizontalSpace)
+struct ColumnSpan {
+    static size_t hasSpan(const TableGrid::Slot& slot) { return slot.hasColumnSpan(); }
+    static size_t isSpanned(const TableGrid::Slot& slot) { return slot.isColumnSpanned(); }
+
+    static size_t spanCount(const TableGrid::Cell& cell) { return cell.columnSpan(); }
+    static size_t startSpan(const TableGrid::Cell& cell) { return cell.startColumn(); }
+    static size_t endSpan(const TableGrid::Cell& cell) { return cell.endColumn(); }
+
+    static size_t index(size_t columnIndex, size_t /*rowIndex*/) { return columnIndex; }
+    static size_t size(const TableGrid& grid) { return grid.columns().size(); }
+
+    static LayoutUnit spacing(const TableGrid& grid, const TableGrid::Cell& cell) { return (cell.columnSpan() - 1) * grid.horizontalSpacing(); }
+};
+
+using DistributedSpaces = Vector<float>;
+template <typename SpanType>
+static DistributedSpaces distributeAvailableSpace(const TableGrid& grid, float spaceToDistribute, const WTF::Function<LayoutUnit(const TableGrid::Slot&, size_t)>& slotSpace)
 {
-    auto& grid = formattingState().tableGrid();
+    struct ResolvedSpace {
+        float value { 0 };
+        bool isFixed { false };
+    };
+
     auto& columns = grid.columns();
     auto& rows = grid.rows();
-    auto tableWidthConstraints = *grid.widthConstraints();
-
-    enum class ColumnWidthBalancingBase { MinimumWidth, MaximumWidth };
-    auto computeColumnWidths = [&] (auto columnWidthBalancingBase, auto extraHorizontalSpace) {
-        auto slotInitialWidth = [&] (auto& slot) {
-            return columnWidthBalancingBase == ColumnWidthBalancingBase::MinimumWidth ? slot.widthConstraints().minimum : slot.widthConstraints().maximum;
-        };
-        // 1. Collect initial widths driven by <td> across columns but ignore spanning cells first.
-        struct ColumnInitialWidth {
-            float value { 0 };
-            bool isFixed { false };
-        };
-        Vector<Optional<ColumnInitialWidth>> columnInitialWidths(columns.size());
-        Vector<SlotPosition> spanningCellPositionList;
+    // 1. Collect the non-spanning spaces first. They are used for the final distribution as well as for distributing the spanning space.
+    Vector<Optional<ResolvedSpace>> resolvedSpaces(SpanType::size(grid));
+    for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
         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 (slot.isColumnSpanned())
-                    continue;
-                if (slot.hasColumnSpan()) {
-                    spanningCellPositionList.append({ columnIndex, rowIndex });
-                    continue;
-                }
-                if (!columnInitialWidths[columnIndex])
-                    columnInitialWidths[columnIndex] = ColumnInitialWidth { };
-                columnInitialWidths[columnIndex]->value = std::max<float>(columnInitialWidths[columnIndex]->value, slotInitialWidth(slot));
-            }
+            auto& slot = *grid.slot({ columnIndex, rowIndex });
+            if (SpanType::hasSpan(slot) || SpanType::isSpanned(slot))
+                continue;
+            auto index = SpanType::index(columnIndex, rowIndex);
+            if (!resolvedSpaces[index])
+                resolvedSpaces[index] = ResolvedSpace { };
+            resolvedSpaces[index]->value = std::max<float>(resolvedSpaces[index]->value, slotSpace(slot, index));
         }
-        // 2. Adjust the <td> initial widths with fixed column widths (<col> vs. <td>) and also manage all-fixed-width-column content.
-        auto hasFixedColumnsOnly = columns.hasFixedColumnsOnly();
+    }
+
+    // 2. Collect the spanning cells.
+    struct SpanningCell {
+        SlotPosition position;
+        LayoutUnit unresolvedSpace;
+    };
+    Vector<SpanningCell> spanningCells;
+    for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex) {
         for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
-            auto& column = columns.list()[columnIndex];
-            if (!column.isFixedWidth())
-                continue;
-            // This is the column width based on <col width=""> and not <td style="width: ">.
-            auto columnFixedWidth = column.box() ? column.box()->columnWidth() : WTF::nullopt;
-            if (!columnFixedWidth)
-                continue;
-            if (!columnInitialWidths[columnIndex])
-                columnInitialWidths[columnIndex] = ColumnInitialWidth { };
-            columnInitialWidths[columnIndex]->value = std::max(columnInitialWidths[columnIndex]->value, columnFixedWidth.valueOr(0).toFloat());
-            // Fixed columns flex when there are no other flexing columns.
-            columnInitialWidths[columnIndex]->isFixed = !hasFixedColumnsOnly;
+            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 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(spanningCellPositionList.begin(), spanningCellPositionList.end(), [&] (auto& a, auto& b) {
-            return grid.slot(a)->cell().columnSpan() < grid.slot(b)->cell().columnSpan();
-        });
-        // 3. Distribute the spanning cells' mimimum widths across the columns using the non-spanning initial widths.
-        // e.g. [ 1 ][ 5 ][ 1 ]
-        //      [    9   ][ 1 ]
-        // The initial widths are: [ 2 ][ 7 ][ 1 ]
-        for (auto spanningCellPosition : spanningCellPositionList) {
-            auto& slot = *grid.slot(spanningCellPosition);
-            ASSERT(slot.hasColumnSpan());
-            auto& cell = slot.cell();
-            float spanningInitialWidth = slotInitialWidth(slot);
-            if (!columnInitialWidths[cell.startColumn()] || !columnInitialWidths[cell.endColumn() - 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 unresolvedColumnNumber = cell.columnSpan();
-                for (auto columnIndex = cell.startColumn(); columnIndex < cell.endColumn(); ++columnIndex) {
-                    if (!columnInitialWidths[columnIndex])
-                        continue;
-                    ASSERT(unresolvedColumnNumber);
-                    --unresolvedColumnNumber;
-                    spanningInitialWidth = std::max(0.0f, spanningInitialWidth - columnInitialWidths[columnIndex]->value);
-                }
-                ASSERT(unresolvedColumnNumber);
-                for (auto columnIndex = cell.startColumn(); columnIndex < cell.endColumn(); ++columnIndex) {
-                    if (columnInitialWidths[columnIndex])
-                        continue;
-                    columnInitialWidths[columnIndex] = ColumnInitialWidth { spanningInitialWidth / unresolvedColumnNumber, false };
-                }
-            } else {
-                // 1. Collect the non-spaning initial widths.
-                float currentSpanningInitialWidth = 0;
-                for (auto columnIndex = cell.startColumn(); columnIndex < cell.endColumn(); ++columnIndex)
-                    currentSpanningInitialWidth += columnInitialWidths[columnIndex]->value;
-                if (currentSpanningInitialWidth >= spanningInitialWidth) {
-                    // The spanning cell fits the spanned columns just fine. Nothing to distribute.
+    // 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();
+        float unresolvedSpanningSpace = spanningCell.unresolvedSpace;
+        if (!resolvedSpaces[SpanType::startSpan(cell)] || !resolvedSpaces[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 (!resolvedSpaces[spanIndex])
                     continue;
-                }
-                // 2. Distribute the extra width among the spanned columns based on the initial column width.
-                // e.g. spanning initial width: [   9   ]. Current initial widths for the spanned columns: [ 1 ] [ 2 ]
-                // New initial widths: [ 3 ] [ 6 ].
-                auto spaceToDistribute = std::max(0.0f, spanningInitialWidth - (cell.columnSpan() - 1) * grid.horizontalSpacing() - currentSpanningInitialWidth);
-                if (spaceToDistribute) {
-                    for (auto columnIndex = cell.startColumn(); columnIndex < cell.endColumn(); ++columnIndex)
-                        columnInitialWidths[columnIndex]->value += spaceToDistribute / currentSpanningInitialWidth * columnInitialWidths[columnIndex]->value;
-                }
+                ASSERT(unresolvedColumnCount);
+                --unresolvedColumnCount;
+                unresolvedSpanningSpace = std::max(0.0f, unresolvedSpanningSpace - resolvedSpaces[spanIndex]->value);
             }
+            ASSERT(unresolvedColumnCount);
+            for (auto spanIndex = SpanType::startSpan(cell); spanIndex < SpanType::endSpan(cell); ++spanIndex) {
+                if (resolvedSpaces[spanIndex])
+                    continue;
+                resolvedSpaces[spanIndex] = ResolvedSpace { unresolvedSpanningSpace / unresolvedColumnCount, false };
+            }
+        } 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 ].
+            float resolvedSpace = 0;
+            for (auto columnIndex = cell.startColumn(); columnIndex < cell.endColumn(); ++columnIndex)
+                resolvedSpace += resolvedSpaces[columnIndex]->value;
+            if (resolvedSpace >= unresolvedSpanningSpace) {
+                // The spanning cell fits the spanned columns/rows just fine. Nothing to distribute.
+                continue;
+            }
+            auto spanningSpaceToDistribute = std::max(0.0f, unresolvedSpanningSpace - SpanType::spacing(grid, cell) - resolvedSpace);
+            if (spanningSpaceToDistribute) {
+                for (auto spanIndex = SpanType::startSpan(cell); spanIndex < SpanType::endSpan(cell); ++spanIndex)
+                    resolvedSpaces[spanIndex]->value += spanningSpaceToDistribute / resolvedSpace * resolvedSpaces[spanIndex]->value;
+            }
         }
-        // 4. Distribute the extra space using the final initial widths.
+    }
+    // 4. Distribute the extra space using the final resolved widths.
 #if ASSERT_ENABLED
-        // We have to have all the columns resolved at this point with valid initial widths.
-        for (auto& columnInitialWidth : columnInitialWidths)
-            ASSERT(columnInitialWidth);
+    // We have to have all the spaces resolved at this point.
+    for (auto& resolvedSpace : resolvedSpaces)
+        ASSERT(resolvedSpace);
 #endif
-        // Fixed width columns don't participate in available space distribution.
-        // Unless there are no flexing column at all, then they start flexing as if they were not fixed at all.
-        float adjustabledHorizontalSpace = 0;
-        for (auto& columnInitialWidth : columnInitialWidths) {
-            if (columnInitialWidth->isFixed)
-                continue;
-            adjustabledHorizontalSpace += columnInitialWidth->value;
-        }
+    // Fixed size cells don't participate in available space distribution.
+    float adjustabledSpace = 0;
+    for (auto& resolvedSpace : resolvedSpaces) {
+        if (resolvedSpace->isFixed)
+            continue;
+        adjustabledSpace += resolvedSpace->value;
+    }
 
+    DistributedSpaces distributedSpaces(resolvedSpaces.size());
+    // Distribute the extra space based on the resolved spaces.
+    for (size_t index = 0; index < resolvedSpaces.size(); ++index) {
+        auto& resolvedSpace = resolvedSpaces[index];
+        auto hasExtraSpaceToDistribute = spaceToDistribute && !resolvedSpace->isFixed;
+        auto resolvedValue = resolvedSpace->value;
+        distributedSpaces[index] = hasExtraSpaceToDistribute ? resolvedValue + (spaceToDistribute / adjustabledSpace * resolvedValue) : resolvedValue;
+    }
+    return distributedSpaces;
+}
+
+void TableFormattingContext::computeAndDistributeExtraHorizontalSpace(LayoutUnit availableHorizontalSpace)
+{
+    auto& grid = formattingState().tableGrid();
+    auto& columns = grid.columns();
+    auto tableWidthConstraints = *grid.widthConstraints();
+
+    enum class ColumnWidthBalancingBase { MinimumWidth, MaximumWidth };
+    auto computeColumnWidths = [&] (auto columnWidthBalancingBase, auto extraHorizontalSpace) {
+        auto distributedSpaces = distributeAvailableSpace<ColumnSpan>(grid, extraHorizontalSpace, [&] (const TableGrid::Slot& slot, size_t columnIndex) {
+            auto& column = columns.list()[columnIndex];
+            auto columnFixedWidth = column.box() ? column.box()->columnWidth() : WTF::nullopt;
+            auto slotWidth = columnWidthBalancingBase == ColumnWidthBalancingBase::MinimumWidth ? slot.widthConstraints().minimum : slot.widthConstraints().maximum;
+            return std::max(slotWidth, columnFixedWidth.valueOr(0_lu));
+        });
         // Set finial horizontal position and width.
-        float columnLogicalLeft = grid.horizontalSpacing();
+        auto columnLogicalLeft = grid.horizontalSpacing();
         for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
             auto& column = columns.list()[columnIndex];
-            auto initialWidth = columnInitialWidths[columnIndex]->value;
-            auto columnWidth = initialWidth;
+            auto columnWidth = LayoutUnit { distributedSpaces[columnIndex] };
 
-            if (extraHorizontalSpace && !columnInitialWidths[columnIndex]->isFixed) {
-                auto columnExtraSpace = extraHorizontalSpace / adjustabledHorizontalSpace * initialWidth;
-                columnWidth = initialWidth + columnExtraSpace;
-            }
-            column.setLogicalLeft(LayoutUnit {columnLogicalLeft });
-            column.setLogicalWidth(LayoutUnit { columnWidth });
+            column.setLogicalLeft(columnLogicalLeft);
+            column.setLogicalWidth(columnWidth);
             columnLogicalLeft += columnWidth + grid.horizontalSpacing();
         }
     };

Modified: trunk/Source/WebCore/layout/tableformatting/TableGrid.h (261063 => 261064)


--- trunk/Source/WebCore/layout/tableformatting/TableGrid.h	2020-05-03 18:16:31 UTC (rev 261063)
+++ trunk/Source/WebCore/layout/tableformatting/TableGrid.h	2020-05-03 18:23:39 UTC (rev 261064)
@@ -214,6 +214,7 @@
     Cells& cells() { return m_cells; }
 
     Slot* slot(SlotPosition);
+    const Slot* slot(SlotPosition position) const { return m_slotMap.get(position); }
     bool isSpanned(SlotPosition);
 
 private:
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to