Changeset 261064 in webkit


Ignore:
Timestamp:
May 3, 2020 11:23:39 AM (4 years ago)
Author:
Alan Bujtas
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):

Location:
trunk/Source/WebCore
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r261063 r261064  
     12020-05-03  Zalan Bujtas  <zalan@apple.com>
     2
     3        [LFC][TFC] Turns horizontal space distribution into a generic space distribution
     4        https://bugs.webkit.org/show_bug.cgi?id=211352
     5
     6        Reviewed by Antti Koivisto.
     7
     8        Horizontal(column) and vertical(row) space distributions use essentially the same logic to distribute
     9        the extra space among the columns/rows.
     10        This patch turns the horizontal space distribution function into a generic space distribution code so
     11        that we can use it for row sizing as well. 
     12
     13        * layout/tableformatting/TableFormattingContext.cpp:
     14        (WebCore::Layout::ColumnSpan::hasSpan):
     15        (WebCore::Layout::ColumnSpan::isSpanned):
     16        (WebCore::Layout::ColumnSpan::spanCount):
     17        (WebCore::Layout::ColumnSpan::startSpan):
     18        (WebCore::Layout::ColumnSpan::endSpan):
     19        (WebCore::Layout::ColumnSpan::index):
     20        (WebCore::Layout::ColumnSpan::size):
     21        (WebCore::Layout::ColumnSpan::spacing):
     22        (WebCore::Layout::distributeAvailableSpace):
     23        (WebCore::Layout::TableFormattingContext::computeAndDistributeExtraHorizontalSpace):
     24        * layout/tableformatting/TableGrid.h:
     25        (WebCore::Layout::TableGrid::slot const):
     26
    1272020-05-03  Youenn Fablet  <youenn@apple.com>
    228
  • trunk/Source/WebCore/layout/tableformatting/TableFormattingContext.cpp

    r260998 r261064  
    348348}
    349349
     350struct ColumnSpan {
     351    static size_t hasSpan(const TableGrid::Slot& slot) { return slot.hasColumnSpan(); }
     352    static size_t isSpanned(const TableGrid::Slot& slot) { return slot.isColumnSpanned(); }
     353
     354    static size_t spanCount(const TableGrid::Cell& cell) { return cell.columnSpan(); }
     355    static size_t startSpan(const TableGrid::Cell& cell) { return cell.startColumn(); }
     356    static size_t endSpan(const TableGrid::Cell& cell) { return cell.endColumn(); }
     357
     358    static size_t index(size_t columnIndex, size_t /*rowIndex*/) { return columnIndex; }
     359    static size_t size(const TableGrid& grid) { return grid.columns().size(); }
     360
     361    static LayoutUnit spacing(const TableGrid& grid, const TableGrid::Cell& cell) { return (cell.columnSpan() - 1) * grid.horizontalSpacing(); }
     362};
     363
     364using DistributedSpaces = Vector<float>;
     365template <typename SpanType>
     366static DistributedSpaces distributeAvailableSpace(const TableGrid& grid, float spaceToDistribute, const WTF::Function<LayoutUnit(const TableGrid::Slot&, size_t)>& slotSpace)
     367{
     368    struct ResolvedSpace {
     369        float value { 0 };
     370        bool isFixed { false };
     371    };
     372
     373    auto& columns = grid.columns();
     374    auto& rows = grid.rows();
     375    // 1. Collect the non-spanning spaces first. They are used for the final distribution as well as for distributing the spanning space.
     376    Vector<Optional<ResolvedSpace>> resolvedSpaces(SpanType::size(grid));
     377    for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
     378        for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex) {
     379            auto& slot = *grid.slot({ columnIndex, rowIndex });
     380            if (SpanType::hasSpan(slot) || SpanType::isSpanned(slot))
     381                continue;
     382            auto index = SpanType::index(columnIndex, rowIndex);
     383            if (!resolvedSpaces[index])
     384                resolvedSpaces[index] = ResolvedSpace { };
     385            resolvedSpaces[index]->value = std::max<float>(resolvedSpaces[index]->value, slotSpace(slot, index));
     386        }
     387    }
     388
     389    // 2. Collect the spanning cells.
     390    struct SpanningCell {
     391        SlotPosition position;
     392        LayoutUnit unresolvedSpace;
     393    };
     394    Vector<SpanningCell> spanningCells;
     395    for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex) {
     396        for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
     397            auto& slot = *grid.slot({ columnIndex, rowIndex });
     398            if (SpanType::hasSpan(slot))
     399                spanningCells.append({ { columnIndex, rowIndex }, slotSpace(slot, SpanType::index(columnIndex, rowIndex)) });
     400        }
     401    }
     402    // We need these spanning cells in the order of the number of columns/rows they span so that
     403    // we can resolve overlapping spans starting with the shorter ones e.g.
     404    // <td colspan=4>#a</td><td>#b</td>
     405    // <td colspan=2>#c</td><td colspan=3>#d</td>
     406    std::sort(spanningCells.begin(), spanningCells.end(), [&] (auto& a, auto& b) {
     407        return SpanType::spanCount(grid.slot(a.position)->cell()) < SpanType::spanCount(grid.slot(b.position)->cell());
     408    });
     409
     410    // 3. Distribute the spanning cells' mimimum space across the columns/rows using the non-spanning spaces.
     411    // e.g. [ 1 ][ 5 ][ 1 ]
     412    //      [    9   ][ 1 ]
     413    // The initial widths are: [ 2 ][ 7 ][ 1 ]
     414    for (auto spanningCell : spanningCells) {
     415        auto& cell = grid.slot(spanningCell.position)->cell();
     416        float unresolvedSpanningSpace = spanningCell.unresolvedSpace;
     417        if (!resolvedSpaces[SpanType::startSpan(cell)] || !resolvedSpaces[SpanType::endSpan(cell) - 1]) {
     418            // <td colspan=4>#a</td><td>#b</td>
     419            // <td colspan=2>#c</td><td colspan=3>#d</td>
     420            // Unresolved columns are: 1 2 3 4
     421            // 1. Take colspan=2 (shortest span) and resolve column 1 and 2
     422            // 2. Take colspan=3 and resolve column 3 and 4 (5 is resolved because it already has a non-spanning cell).
     423            // 3. colspan=4 needs no resolving because all the spanned columns (1 2 3 4) have already been resolved.
     424            auto unresolvedColumnCount = cell.columnSpan();
     425            for (auto spanIndex = SpanType::startSpan(cell); spanIndex < SpanType::endSpan(cell); ++spanIndex) {
     426                if (!resolvedSpaces[spanIndex])
     427                    continue;
     428                ASSERT(unresolvedColumnCount);
     429                --unresolvedColumnCount;
     430                unresolvedSpanningSpace = std::max(0.0f, unresolvedSpanningSpace - resolvedSpaces[spanIndex]->value);
     431            }
     432            ASSERT(unresolvedColumnCount);
     433            for (auto spanIndex = SpanType::startSpan(cell); spanIndex < SpanType::endSpan(cell); ++spanIndex) {
     434                if (resolvedSpaces[spanIndex])
     435                    continue;
     436                resolvedSpaces[spanIndex] = ResolvedSpace { unresolvedSpanningSpace / unresolvedColumnCount, false };
     437            }
     438        } else {
     439            // 1. Collect the non-spaning resolved spaces.
     440            // 2. Distribute the extra space among the spanned columns/rows based on the resolved space values.
     441            // e.g. spanning width: [   9   ]. Resolved widths for the spanned columns: [ 1 ] [ 2 ]
     442            // New resolved widths: [ 3 ] [ 6 ].
     443            float resolvedSpace = 0;
     444            for (auto columnIndex = cell.startColumn(); columnIndex < cell.endColumn(); ++columnIndex)
     445                resolvedSpace += resolvedSpaces[columnIndex]->value;
     446            if (resolvedSpace >= unresolvedSpanningSpace) {
     447                // The spanning cell fits the spanned columns/rows just fine. Nothing to distribute.
     448                continue;
     449            }
     450            auto spanningSpaceToDistribute = std::max(0.0f, unresolvedSpanningSpace - SpanType::spacing(grid, cell) - resolvedSpace);
     451            if (spanningSpaceToDistribute) {
     452                for (auto spanIndex = SpanType::startSpan(cell); spanIndex < SpanType::endSpan(cell); ++spanIndex)
     453                    resolvedSpaces[spanIndex]->value += spanningSpaceToDistribute / resolvedSpace * resolvedSpaces[spanIndex]->value;
     454            }
     455        }
     456    }
     457    // 4. Distribute the extra space using the final resolved widths.
     458#if ASSERT_ENABLED
     459    // We have to have all the spaces resolved at this point.
     460    for (auto& resolvedSpace : resolvedSpaces)
     461        ASSERT(resolvedSpace);
     462#endif
     463    // Fixed size cells don't participate in available space distribution.
     464    float adjustabledSpace = 0;
     465    for (auto& resolvedSpace : resolvedSpaces) {
     466        if (resolvedSpace->isFixed)
     467            continue;
     468        adjustabledSpace += resolvedSpace->value;
     469    }
     470
     471    DistributedSpaces distributedSpaces(resolvedSpaces.size());
     472    // Distribute the extra space based on the resolved spaces.
     473    for (size_t index = 0; index < resolvedSpaces.size(); ++index) {
     474        auto& resolvedSpace = resolvedSpaces[index];
     475        auto hasExtraSpaceToDistribute = spaceToDistribute && !resolvedSpace->isFixed;
     476        auto resolvedValue = resolvedSpace->value;
     477        distributedSpaces[index] = hasExtraSpaceToDistribute ? resolvedValue + (spaceToDistribute / adjustabledSpace * resolvedValue) : resolvedValue;
     478    }
     479    return distributedSpaces;
     480}
     481
    350482void TableFormattingContext::computeAndDistributeExtraHorizontalSpace(LayoutUnit availableHorizontalSpace)
    351483{
    352484    auto& grid = formattingState().tableGrid();
    353485    auto& columns = grid.columns();
    354     auto& rows = grid.rows();
    355486    auto tableWidthConstraints = *grid.widthConstraints();
    356487
    357488    enum class ColumnWidthBalancingBase { MinimumWidth, MaximumWidth };
    358489    auto computeColumnWidths = [&] (auto columnWidthBalancingBase, auto extraHorizontalSpace) {
    359         auto slotInitialWidth = [&] (auto& slot) {
    360             return columnWidthBalancingBase == ColumnWidthBalancingBase::MinimumWidth ? slot.widthConstraints().minimum : slot.widthConstraints().maximum;
    361         };
    362         // 1. Collect initial widths driven by <td> across columns but ignore spanning cells first.
    363         struct ColumnInitialWidth {
    364             float value { 0 };
    365             bool isFixed { false };
    366         };
    367         Vector<Optional<ColumnInitialWidth>> columnInitialWidths(columns.size());
    368         Vector<SlotPosition> spanningCellPositionList;
    369         for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex) {
    370             for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
    371                 auto& slot = *grid.slot({ columnIndex, rowIndex });
    372                 if (slot.isColumnSpanned())
    373                     continue;
    374                 if (slot.hasColumnSpan()) {
    375                     spanningCellPositionList.append({ columnIndex, rowIndex });
    376                     continue;
    377                 }
    378                 if (!columnInitialWidths[columnIndex])
    379                     columnInitialWidths[columnIndex] = ColumnInitialWidth { };
    380                 columnInitialWidths[columnIndex]->value = std::max<float>(columnInitialWidths[columnIndex]->value, slotInitialWidth(slot));
    381             }
    382         }
    383         // 2. Adjust the <td> initial widths with fixed column widths (<col> vs. <td>) and also manage all-fixed-width-column content.
    384         auto hasFixedColumnsOnly = columns.hasFixedColumnsOnly();
     490        auto distributedSpaces = distributeAvailableSpace<ColumnSpan>(grid, extraHorizontalSpace, [&] (const TableGrid::Slot& slot, size_t columnIndex) {
     491            auto& column = columns.list()[columnIndex];
     492            auto columnFixedWidth = column.box() ? column.box()->columnWidth() : WTF::nullopt;
     493            auto slotWidth = columnWidthBalancingBase == ColumnWidthBalancingBase::MinimumWidth ? slot.widthConstraints().minimum : slot.widthConstraints().maximum;
     494            return std::max(slotWidth, columnFixedWidth.valueOr(0_lu));
     495        });
     496        // Set finial horizontal position and width.
     497        auto columnLogicalLeft = grid.horizontalSpacing();
    385498        for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
    386499            auto& column = columns.list()[columnIndex];
    387             if (!column.isFixedWidth())
    388                 continue;
    389             // This is the column width based on <col width=""> and not <td style="width: ">.
    390             auto columnFixedWidth = column.box() ? column.box()->columnWidth() : WTF::nullopt;
    391             if (!columnFixedWidth)
    392                 continue;
    393             if (!columnInitialWidths[columnIndex])
    394                 columnInitialWidths[columnIndex] = ColumnInitialWidth { };
    395             columnInitialWidths[columnIndex]->value = std::max(columnInitialWidths[columnIndex]->value, columnFixedWidth.valueOr(0).toFloat());
    396             // Fixed columns flex when there are no other flexing columns.
    397             columnInitialWidths[columnIndex]->isFixed = !hasFixedColumnsOnly;
    398         }
    399 
    400         // We need these spanning cells in the order of the number of columns they span so that
    401         // we can resolve overlapping spans starting with the shorter ones e.g.
    402         // <td colspan=4>#a</td><td>#b</td>
    403         // <td colspan=2>#c</td><td colspan=3>#d</td>
    404         std::sort(spanningCellPositionList.begin(), spanningCellPositionList.end(), [&] (auto& a, auto& b) {
    405             return grid.slot(a)->cell().columnSpan() < grid.slot(b)->cell().columnSpan();
    406         });
    407         // 3. Distribute the spanning cells' mimimum widths across the columns using the non-spanning initial widths.
    408         // e.g. [ 1 ][ 5 ][ 1 ]
    409         //      [    9   ][ 1 ]
    410         // The initial widths are: [ 2 ][ 7 ][ 1 ]
    411         for (auto spanningCellPosition : spanningCellPositionList) {
    412             auto& slot = *grid.slot(spanningCellPosition);
    413             ASSERT(slot.hasColumnSpan());
    414             auto& cell = slot.cell();
    415             float spanningInitialWidth = slotInitialWidth(slot);
    416             if (!columnInitialWidths[cell.startColumn()] || !columnInitialWidths[cell.endColumn() - 1]) {
    417                 // <td colspan=4>#a</td><td>#b</td>
    418                 // <td colspan=2>#c</td><td colspan=3>#d</td>
    419                 // Unresolved columns are: 1 2 3 4
    420                 // 1. Take colspan=2 (shortest span) and resolve column 1 and 2
    421                 // 2. Take colspan=3 and resolve column 3 and 4 (5 is resolved because it already has a non-spanning cell).
    422                 // 3. colspan=4 needs no resolving because all the spanned columns (1 2 3 4) have already been resolved.
    423                 auto unresolvedColumnNumber = cell.columnSpan();
    424                 for (auto columnIndex = cell.startColumn(); columnIndex < cell.endColumn(); ++columnIndex) {
    425                     if (!columnInitialWidths[columnIndex])
    426                         continue;
    427                     ASSERT(unresolvedColumnNumber);
    428                     --unresolvedColumnNumber;
    429                     spanningInitialWidth = std::max(0.0f, spanningInitialWidth - columnInitialWidths[columnIndex]->value);
    430                 }
    431                 ASSERT(unresolvedColumnNumber);
    432                 for (auto columnIndex = cell.startColumn(); columnIndex < cell.endColumn(); ++columnIndex) {
    433                     if (columnInitialWidths[columnIndex])
    434                         continue;
    435                     columnInitialWidths[columnIndex] = ColumnInitialWidth { spanningInitialWidth / unresolvedColumnNumber, false };
    436                 }
    437             } else {
    438                 // 1. Collect the non-spaning initial widths.
    439                 float currentSpanningInitialWidth = 0;
    440                 for (auto columnIndex = cell.startColumn(); columnIndex < cell.endColumn(); ++columnIndex)
    441                     currentSpanningInitialWidth += columnInitialWidths[columnIndex]->value;
    442                 if (currentSpanningInitialWidth >= spanningInitialWidth) {
    443                     // The spanning cell fits the spanned columns just fine. Nothing to distribute.
    444                     continue;
    445                 }
    446                 // 2. Distribute the extra width among the spanned columns based on the initial column width.
    447                 // e.g. spanning initial width: [   9   ]. Current initial widths for the spanned columns: [ 1 ] [ 2 ]
    448                 // New initial widths: [ 3 ] [ 6 ].
    449                 auto spaceToDistribute = std::max(0.0f, spanningInitialWidth - (cell.columnSpan() - 1) * grid.horizontalSpacing() - currentSpanningInitialWidth);
    450                 if (spaceToDistribute) {
    451                     for (auto columnIndex = cell.startColumn(); columnIndex < cell.endColumn(); ++columnIndex)
    452                         columnInitialWidths[columnIndex]->value += spaceToDistribute / currentSpanningInitialWidth * columnInitialWidths[columnIndex]->value;
    453                 }
    454             }
    455         }
    456         // 4. Distribute the extra space using the final initial widths.
    457 #if ASSERT_ENABLED
    458         // We have to have all the columns resolved at this point with valid initial widths.
    459         for (auto& columnInitialWidth : columnInitialWidths)
    460             ASSERT(columnInitialWidth);
    461 #endif
    462         // Fixed width columns don't participate in available space distribution.
    463         // Unless there are no flexing column at all, then they start flexing as if they were not fixed at all.
    464         float adjustabledHorizontalSpace = 0;
    465         for (auto& columnInitialWidth : columnInitialWidths) {
    466             if (columnInitialWidth->isFixed)
    467                 continue;
    468             adjustabledHorizontalSpace += columnInitialWidth->value;
    469         }
    470 
    471         // Set finial horizontal position and width.
    472         float columnLogicalLeft = grid.horizontalSpacing();
    473         for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
    474             auto& column = columns.list()[columnIndex];
    475             auto initialWidth = columnInitialWidths[columnIndex]->value;
    476             auto columnWidth = initialWidth;
    477 
    478             if (extraHorizontalSpace && !columnInitialWidths[columnIndex]->isFixed) {
    479                 auto columnExtraSpace = extraHorizontalSpace / adjustabledHorizontalSpace * initialWidth;
    480                 columnWidth = initialWidth + columnExtraSpace;
    481             }
    482             column.setLogicalLeft(LayoutUnit {columnLogicalLeft });
    483             column.setLogicalWidth(LayoutUnit { columnWidth });
     500            auto columnWidth = LayoutUnit { distributedSpaces[columnIndex] };
     501
     502            column.setLogicalLeft(columnLogicalLeft);
     503            column.setLogicalWidth(columnWidth);
    484504            columnLogicalLeft += columnWidth + grid.horizontalSpacing();
    485505        }
  • trunk/Source/WebCore/layout/tableformatting/TableGrid.h

    r260708 r261064  
    215215
    216216    Slot* slot(SlotPosition);
     217    const Slot* slot(SlotPosition position) const { return m_slotMap.get(position); }
    217218    bool isSpanned(SlotPosition);
    218219
Note: See TracChangeset for help on using the changeset viewer.