Changeset 261064 in webkit
- Timestamp:
- May 3, 2020 11:23:39 AM (4 years ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r261063 r261064 1 2020-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 1 27 2020-05-03 Youenn Fablet <youenn@apple.com> 2 28 -
trunk/Source/WebCore/layout/tableformatting/TableFormattingContext.cpp
r260998 r261064 348 348 } 349 349 350 struct 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 364 using DistributedSpaces = Vector<float>; 365 template <typename SpanType> 366 static 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 350 482 void TableFormattingContext::computeAndDistributeExtraHorizontalSpace(LayoutUnit availableHorizontalSpace) 351 483 { 352 484 auto& grid = formattingState().tableGrid(); 353 485 auto& columns = grid.columns(); 354 auto& rows = grid.rows();355 486 auto tableWidthConstraints = *grid.widthConstraints(); 356 487 357 488 enum class ColumnWidthBalancingBase { MinimumWidth, MaximumWidth }; 358 489 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(); 385 498 for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) { 386 499 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); 484 504 columnLogicalLeft += columnWidth + grid.horizontalSpacing(); 485 505 } -
trunk/Source/WebCore/layout/tableformatting/TableGrid.h
r260708 r261064 215 215 216 216 Slot* slot(SlotPosition); 217 const Slot* slot(SlotPosition position) const { return m_slotMap.get(position); } 217 218 bool isSpanned(SlotPosition); 218 219
Note: See TracChangeset
for help on using the changeset viewer.