Changeset 260342 in webkit


Ignore:
Timestamp:
Apr 19, 2020 11:34:07 AM (4 years ago)
Author:
Alan Bujtas
Message:

[LFC][TFC] Add column spanning support for flexible table width
https://bugs.webkit.org/show_bug.cgi?id=210713

Reviewed by Antti Koivisto.

Source/WebCore:

Test: fast/layoutformattingcontext/table-flex-width-colspans.html

This patch slightly changes the extra space distribution logic by using either the minimum or
the maximum width as the base initial width for the columns.

  • layout/tableformatting/TableFormattingContext.cpp:

(WebCore::Layout::TableFormattingContext::layoutInFlowContent):
(WebCore::Layout::TableFormattingContext::computeColumnWidths):
(WebCore::Layout::TableFormattingContext::computeAndDistributeExtraHorizontalSpace): Deleted.

  • layout/tableformatting/TableFormattingContext.h:

LayoutTests:

  • fast/layoutformattingcontext/table-flex-width-colspans-expected.txt: Added.
  • fast/layoutformattingcontext/table-flex-width-colspans.html: Added.
Location:
trunk
Files:
2 added
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r260340 r260342  
     12020-04-19  Zalan Bujtas  <zalan@apple.com>
     2
     3        [LFC][TFC] Add column spanning support for flexible table width
     4        https://bugs.webkit.org/show_bug.cgi?id=210713
     5
     6        Reviewed by Antti Koivisto.
     7
     8        * fast/layoutformattingcontext/table-flex-width-colspans-expected.txt: Added.
     9        * fast/layoutformattingcontext/table-flex-width-colspans.html: Added.
     10
    1112020-04-19  Emilio Cobos Álvarez  <emilio@crisal.io>
    212
  • trunk/Source/WebCore/ChangeLog

    r260340 r260342  
     12020-04-19  Zalan Bujtas  <zalan@apple.com>
     2
     3        [LFC][TFC] Add column spanning support for flexible table width
     4        https://bugs.webkit.org/show_bug.cgi?id=210713
     5
     6        Reviewed by Antti Koivisto.
     7
     8        Test: fast/layoutformattingcontext/table-flex-width-colspans.html
     9
     10        This patch slightly changes the extra space distribution logic by using either the minimum or
     11        the maximum width as the base initial width for the columns.
     12
     13        * layout/tableformatting/TableFormattingContext.cpp:
     14        (WebCore::Layout::TableFormattingContext::layoutInFlowContent):
     15        (WebCore::Layout::TableFormattingContext::computeColumnWidths):
     16        (WebCore::Layout::TableFormattingContext::computeAndDistributeExtraHorizontalSpace): Deleted.
     17        * layout/tableformatting/TableFormattingContext.h:
     18
    1192020-04-19  Emilio Cobos Álvarez  <emilio@crisal.io>
    220
  • trunk/Source/WebCore/layout/tableformatting/TableFormattingContext.cpp

    r260337 r260342  
    340340    auto tableWidthConstraints = *grid.widthConstraints();
    341341
    342     auto distributeExtraHorizontalSpace = [&] (auto horizontalSpaceToDistribute) {
    343         auto& columnList = grid.columns().list();
    344         ASSERT(!columnList.isEmpty());
    345 
    346         // 1. Collect minimum widths driven by <td> across columns but ignore spanning cells first.
    347         struct ColumnMinimumWidth {
     342    enum class ColumnWidthBalancingBase { MinimumWidth, MaximumWidth };
     343    auto computeColumnWidths = [&] (auto columnWidthBalancingBase, auto extraHorizontalSpace) {
     344        auto slotInitialWidth = [&] (auto& slot) {
     345            return columnWidthBalancingBase == ColumnWidthBalancingBase::MinimumWidth ? slot.widthConstraints().minimum : slot.widthConstraints().maximum;
     346        };
     347        // 1. Collect initial widths driven by <td> across columns but ignore spanning cells first.
     348        struct ColumnInitialWidth {
    348349            float value { 0 };
    349350            bool isFixed { false };
    350351        };
    351         Vector<Optional<ColumnMinimumWidth>> columnMinimumWidths(columnList.size());
     352        Vector<Optional<ColumnInitialWidth>> columnInitialWidths(columns.size());
    352353        Vector<SlotPosition> spanningCellPositionList;
    353354        for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex) {
    354355            for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
    355356                auto& slot = *grid.slot({ columnIndex, rowIndex });
     357                if (slot.isColumnSpanned())
     358                    continue;
    356359                if (slot.hasColumnSpan()) {
    357360                    spanningCellPositionList.append({ columnIndex, rowIndex });
    358361                    continue;
    359362                }
    360                 if (slot.isColumnSpanned())
    361                     continue;
    362                 if (!columnMinimumWidths[columnIndex])
    363                     columnMinimumWidths[columnIndex] = ColumnMinimumWidth { };
    364                 columnMinimumWidths[columnIndex]->value = std::max<float>(columnMinimumWidths[columnIndex]->value, slot.widthConstraints().minimum);
    365             }
    366         }
    367         // 2. Adjust the <td> minimum widths with fixed column widths (<col> vs. <td>) and also manage all-fixed-width-column content.
     363                if (!columnInitialWidths[columnIndex])
     364                    columnInitialWidths[columnIndex] = ColumnInitialWidth { };
     365                columnInitialWidths[columnIndex]->value = std::max<float>(columnInitialWidths[columnIndex]->value, slotInitialWidth(slot));
     366            }
     367        }
     368        // 2. Adjust the <td> initial widths with fixed column widths (<col> vs. <td>) and also manage all-fixed-width-column content.
    368369        auto hasFixedColumnsOnly = columns.hasFixedColumnsOnly();
    369         for (size_t columnIndex = 0; columnIndex < columnList.size(); ++columnIndex) {
    370             auto& column = columnList[columnIndex];
     370        for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
     371            auto& column = columns.list()[columnIndex];
    371372            if (!column.isFixedWidth())
    372373                continue;
     
    375376            if (!columnFixedWidth)
    376377                continue;
    377             if (!columnMinimumWidths[columnIndex])
    378                 columnMinimumWidths[columnIndex] = ColumnMinimumWidth { };
    379             columnMinimumWidths[columnIndex]->value = std::max(columnMinimumWidths[columnIndex]->value, columnFixedWidth.valueOr(0).toFloat());
     378            if (!columnInitialWidths[columnIndex])
     379                columnInitialWidths[columnIndex] = ColumnInitialWidth { };
     380            columnInitialWidths[columnIndex]->value = std::max(columnInitialWidths[columnIndex]->value, columnFixedWidth.valueOr(0).toFloat());
    380381            // Fixed columns flex when there are no other flexing columns.
    381             columnMinimumWidths[columnIndex]->isFixed = !hasFixedColumnsOnly;
     382            columnInitialWidths[columnIndex]->isFixed = !hasFixedColumnsOnly;
    382383        }
    383384
     
    389390            return grid.slot(a)->cell().columnSpan() < grid.slot(b)->cell().columnSpan();
    390391        });
    391         // 3. Distribute the spanning cells' mimimum widths across the columns using the non-spanning minimum widths.
     392        // 3. Distribute the spanning cells' mimimum widths across the columns using the non-spanning initial widths.
    392393        // e.g. [ 1 ][ 5 ][ 1 ]
    393394        //      [    9   ][ 1 ]
    394         // The minimum widths are: [ 2 ][ 7 ][ 1 ]
     395        // The initial widths are: [ 2 ][ 7 ][ 1 ]
    395396        for (auto spanningCellPosition : spanningCellPositionList) {
    396397            auto& slot = *grid.slot(spanningCellPosition);
    397398            ASSERT(slot.hasColumnSpan());
    398399            auto& cell = slot.cell();
    399             float spanningMinimumWidth = slot.widthConstraints().minimum;
    400             if (!columnMinimumWidths[cell.startColumn()] || !columnMinimumWidths[cell.endColumn() - 1]) {
     400            float spanningInitialWidth = slotInitialWidth(slot);
     401            if (!columnInitialWidths[cell.startColumn()] || !columnInitialWidths[cell.endColumn() - 1]) {
    401402                // <td colspan=4>#a</td><td>#b</td>
    402403                // <td colspan=2>#c</td><td colspan=3>#d</td>
     
    407408                auto unresolvedColumnNumber = cell.columnSpan();
    408409                for (auto columnIndex = cell.startColumn(); columnIndex < cell.endColumn(); ++columnIndex) {
    409                     if (!columnMinimumWidths[columnIndex])
     410                    if (!columnInitialWidths[columnIndex])
    410411                        continue;
    411412                    ASSERT(unresolvedColumnNumber);
    412413                    --unresolvedColumnNumber;
    413                     spanningMinimumWidth = std::max(0.0f, spanningMinimumWidth - columnMinimumWidths[columnIndex]->value);
     414                    spanningInitialWidth = std::max(0.0f, spanningInitialWidth - columnInitialWidths[columnIndex]->value);
    414415                }
    415416                ASSERT(unresolvedColumnNumber);
    416417                for (auto columnIndex = cell.startColumn(); columnIndex < cell.endColumn(); ++columnIndex) {
    417                     if (columnMinimumWidths[columnIndex])
     418                    if (columnInitialWidths[columnIndex])
    418419                        continue;
    419                     columnMinimumWidths[columnIndex] = ColumnMinimumWidth { spanningMinimumWidth / unresolvedColumnNumber, false };
     420                    columnInitialWidths[columnIndex] = ColumnInitialWidth { spanningInitialWidth / unresolvedColumnNumber, false };
    420421                }
    421422            } else {
    422                 // 1. Collect the non-spaning minimum widths.
    423                 float currentSpanningMinimumWidth = 0;
     423                // 1. Collect the non-spaning initial widths.
     424                float currentSpanningInitialWidth = 0;
    424425                for (auto columnIndex = cell.startColumn(); columnIndex < cell.endColumn(); ++columnIndex)
    425                     currentSpanningMinimumWidth += columnMinimumWidths[columnIndex]->value;
    426                 if (currentSpanningMinimumWidth >= spanningMinimumWidth) {
     426                    currentSpanningInitialWidth += columnInitialWidths[columnIndex]->value;
     427                if (currentSpanningInitialWidth >= spanningInitialWidth) {
    427428                    // The spanning cell fits the spanned columns just fine. Nothing to distribute.
    428429                    continue;
    429430                }
    430                 // 2. Distribute the extra minimum width among the spanned columns based on the minimum colmn width.
    431                 // e.g. spanning mimimum width: [   9   ]. Current minimum widths for the spanned columns: [ 1 ] [ 2 ]
    432                 // New minimum widths: [ 3 ] [ 6 ].
    433                 auto spaceToDistribute = std::max(0.0f, spanningMinimumWidth - (cell.columnSpan() - 1) * grid.horizontalSpacing() - currentSpanningMinimumWidth);
     431                // 2. Distribute the extra width among the spanned columns based on the initial column width.
     432                // e.g. spanning initial width: [   9   ]. Current initial widths for the spanned columns: [ 1 ] [ 2 ]
     433                // New initial widths: [ 3 ] [ 6 ].
     434                auto spaceToDistribute = std::max(0.0f, spanningInitialWidth - (cell.columnSpan() - 1) * grid.horizontalSpacing() - currentSpanningInitialWidth);
    434435                if (spaceToDistribute) {
    435436                    for (auto columnIndex = cell.startColumn(); columnIndex < cell.endColumn(); ++columnIndex)
    436                         columnMinimumWidths[columnIndex]->value += spaceToDistribute / currentSpanningMinimumWidth * columnMinimumWidths[columnIndex]->value;
     437                        columnInitialWidths[columnIndex]->value += spaceToDistribute / currentSpanningInitialWidth * columnInitialWidths[columnIndex]->value;
    437438                }
    438439            }
    439440        }
    440         // 3. Distribute the extra space using the final minimum widths.
     441        // 4. Distribute the extra space using the final initial widths.
    441442#if ASSERT_ENABLED
    442         // We have to have all the columns resolved at this point with valid minimum widths.
    443         for (auto& columnMinimumWidth : columnMinimumWidths)
    444             ASSERT(columnMinimumWidth);
     443        // We have to have all the columns resolved at this point with valid initial widths.
     444        for (auto& columnInitialWidth : columnInitialWidths)
     445            ASSERT(columnInitialWidth);
    445446#endif
    446447        // Fixed width columns don't participate in available space distribution.
    447448        // Unless there are no flexing column at all, then they start flexing as if they were not fixed at all.
    448449        float adjustabledHorizontalSpace = 0;
    449         for (auto& columnMinimumWidth : columnMinimumWidths) {
    450             if (columnMinimumWidth->isFixed)
    451                 continue;
    452             adjustabledHorizontalSpace += columnMinimumWidth->value;
    453         }
    454         if (!adjustabledHorizontalSpace)
    455             return;
    456         // FIXME: Implement overconstrained columns when fixed width content is wider than the table.
     450        for (auto& columnInitialWidth : columnInitialWidths) {
     451            if (columnInitialWidth->isFixed)
     452                continue;
     453            adjustabledHorizontalSpace += columnInitialWidth->value;
     454        }
     455
    457456        for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
    458457            auto& column = columns.list()[columnIndex];
    459             auto minimumWidth = columnMinimumWidths[columnIndex]->value;
    460             if (columnMinimumWidths[columnIndex]->isFixed) {
    461                 column.setLogicalWidth(LayoutUnit { minimumWidth });
    462                 continue;
    463             }
    464             auto columnExtraSpace = horizontalSpaceToDistribute / adjustabledHorizontalSpace * minimumWidth;
    465             column.setLogicalWidth(LayoutUnit { minimumWidth + columnExtraSpace });
     458            auto initialWidth = columnInitialWidths[columnIndex]->value;
     459
     460            if (!extraHorizontalSpace || columnInitialWidths[columnIndex]->isFixed) {
     461                column.setLogicalWidth(LayoutUnit { initialWidth });
     462                continue;
     463            }
     464            auto columnExtraSpace = extraHorizontalSpace / adjustabledHorizontalSpace * initialWidth;
     465            column.setLogicalWidth(LayoutUnit { initialWidth + columnExtraSpace });
    466466        }
    467467    };
    468468
    469     enum class WidthConstraintsType { Minimum, Maximum };
    470     auto distributeMinOrMax = [&] (WidthConstraintsType type) {
    471         for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
    472             auto logicalWidth = LayoutUnit { };
    473             for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex) {
    474                 auto widthConstraints = grid.slot({ columnIndex, rowIndex })->widthConstraints();
    475                 logicalWidth = std::max(logicalWidth, type == WidthConstraintsType::Minimum ? widthConstraints.minimum : widthConstraints.maximum);
    476             }
    477             columns.list()[columnIndex].setLogicalWidth(logicalWidth);
    478         }
    479     };
    480 
    481     ASSERT(availableHorizontalSpace >= tableWidthConstraints.minimum);
    482     if (availableHorizontalSpace == tableWidthConstraints.minimum)
    483         distributeMinOrMax(WidthConstraintsType::Minimum);
    484     else if (availableHorizontalSpace == tableWidthConstraints.maximum)
    485         distributeMinOrMax(WidthConstraintsType::Maximum);
    486     else
    487         distributeExtraHorizontalSpace(availableHorizontalSpace - tableWidthConstraints.minimum);
     469    auto needsExtraSpaceDistribution = availableHorizontalSpace != tableWidthConstraints.minimum && availableHorizontalSpace != tableWidthConstraints.maximum;
     470    if (!needsExtraSpaceDistribution) {
     471        auto columnWidthBalancingBase = availableHorizontalSpace == tableWidthConstraints.maximum ? ColumnWidthBalancingBase::MaximumWidth : ColumnWidthBalancingBase::MinimumWidth;
     472        computeColumnWidths(columnWidthBalancingBase, LayoutUnit { });
     473        return;
     474    }
     475    auto horizontalSpaceToDistribute = availableHorizontalSpace - tableWidthConstraints.minimum;
     476    ASSERT(horizontalSpaceToDistribute > 0);
     477    computeColumnWidths(ColumnWidthBalancingBase::MinimumWidth, horizontalSpaceToDistribute);
    488478}
    489479
  • trunk/Source/WebCore/layout/tableformatting/TableFormattingContext.h

    r259982 r260342  
    6868    void ensureTableGrid();
    6969    IntrinsicWidthConstraints computedPreferredWidthForColumns();
    70     void computeAndDistributeExtraHorizontalSpace(LayoutUnit containingBlockWidth);
     70    void computeAndDistributeExtraHorizontalSpace(LayoutUnit availableHorizontalSpace);
    7171
    7272    void initializeDisplayBoxToBlank(Display::Box&) const;
Note: See TracChangeset for help on using the changeset viewer.