| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "config.h" | 5 #include "config.h" |
| 6 | 6 |
| 7 #include "core/layout/MultiColumnFragmentainerGroup.h" | 7 #include "core/layout/MultiColumnFragmentainerGroup.h" |
| 8 | 8 |
| 9 #include "core/layout/ColumnBalancer.h" |
| 9 #include "core/layout/LayoutMultiColumnSet.h" | 10 #include "core/layout/LayoutMultiColumnSet.h" |
| 10 | 11 |
| 11 namespace blink { | 12 namespace blink { |
| 12 | 13 |
| 13 MultiColumnFragmentainerGroup::MultiColumnFragmentainerGroup(LayoutMultiColumnSe
t& columnSet) | 14 MultiColumnFragmentainerGroup::MultiColumnFragmentainerGroup(LayoutMultiColumnSe
t& columnSet) |
| 14 : m_columnSet(columnSet) | 15 : m_columnSet(columnSet) |
| 15 { | 16 { |
| 16 } | 17 } |
| 17 | 18 |
| 19 bool MultiColumnFragmentainerGroup::isFirstGroup() const |
| 20 { |
| 21 return &m_columnSet.firstFragmentainerGroup() == this; |
| 22 } |
| 23 |
| 18 bool MultiColumnFragmentainerGroup::isLastGroup() const | 24 bool MultiColumnFragmentainerGroup::isLastGroup() const |
| 19 { | 25 { |
| 20 return &m_columnSet.lastFragmentainerGroup() == this; | 26 return &m_columnSet.lastFragmentainerGroup() == this; |
| 21 } | 27 } |
| 22 | 28 |
| 23 LayoutSize MultiColumnFragmentainerGroup::offsetFromColumnSet() const | 29 LayoutSize MultiColumnFragmentainerGroup::offsetFromColumnSet() const |
| 24 { | 30 { |
| 25 LayoutSize offset(LayoutUnit(), logicalTop()); | 31 LayoutSize offset(LayoutUnit(), logicalTop()); |
| 26 if (!m_columnSet.flowThread()->isHorizontalWritingMode()) | 32 if (!m_columnSet.flowThread()->isHorizontalWritingMode()) |
| 27 return offset.transposedSize(); | 33 return offset.transposedSize(); |
| (...skipping 30 matching lines...) Expand all Loading... |
| 58 LayoutUnit columnHeight = heightIsAuto() ? m_maxColumnHeight : heightAdj
ustedForRowOffset(flowThread->columnHeightAvailable()); | 64 LayoutUnit columnHeight = heightIsAuto() ? m_maxColumnHeight : heightAdj
ustedForRowOffset(flowThread->columnHeightAvailable()); |
| 59 setAndConstrainColumnHeight(columnHeight); | 65 setAndConstrainColumnHeight(columnHeight); |
| 60 } else if (heightIsAuto()) { | 66 } else if (heightIsAuto()) { |
| 61 m_columnHeight = LayoutUnit(); | 67 m_columnHeight = LayoutUnit(); |
| 62 } else { | 68 } else { |
| 63 setAndConstrainColumnHeight(heightAdjustedForRowOffset(flowThread->colum
nHeightAvailable())); | 69 setAndConstrainColumnHeight(heightAdjustedForRowOffset(flowThread->colum
nHeightAvailable())); |
| 64 } | 70 } |
| 65 | 71 |
| 66 if (m_columnHeight != oldColumnHeight) | 72 if (m_columnHeight != oldColumnHeight) |
| 67 m_columnSet.setChildNeedsLayout(MarkOnlyThis); | 73 m_columnSet.setChildNeedsLayout(MarkOnlyThis); |
| 68 | |
| 69 // Content runs are only needed in the initial layout pass, in order to find
an initial column | |
| 70 // height, and should have been deleted afterwards. We're about to rebuild t
he content runs, so | |
| 71 // the list needs to be empty. | |
| 72 ASSERT(m_contentRuns.isEmpty()); | |
| 73 } | |
| 74 | |
| 75 void MultiColumnFragmentainerGroup::addContentRun(LayoutUnit endOffsetInFlowThre
ad) | |
| 76 { | |
| 77 if (!m_contentRuns.isEmpty() && endOffsetInFlowThread <= m_contentRuns.last(
).breakOffset()) | |
| 78 return; | |
| 79 // Append another item as long as we haven't exceeded used column count. Wha
t ends up in the | |
| 80 // overflow area shouldn't affect column balancing. | |
| 81 if (m_contentRuns.size() < m_columnSet.usedColumnCount()) | |
| 82 m_contentRuns.append(ContentRun(endOffsetInFlowThread)); | |
| 83 } | |
| 84 | |
| 85 void MultiColumnFragmentainerGroup::recordSpaceShortage(LayoutUnit spaceShortage
) | |
| 86 { | |
| 87 if (spaceShortage >= m_minSpaceShortage) | |
| 88 return; | |
| 89 | |
| 90 // The space shortage is what we use as our stretch amount. We need a positi
ve number here in | |
| 91 // order to get anywhere. | |
| 92 ASSERT(spaceShortage > 0); | |
| 93 | |
| 94 m_minSpaceShortage = spaceShortage; | |
| 95 } | 74 } |
| 96 | 75 |
| 97 bool MultiColumnFragmentainerGroup::recalculateColumnHeight(BalancedColumnHeight
Calculation calculationMode) | 76 bool MultiColumnFragmentainerGroup::recalculateColumnHeight(BalancedColumnHeight
Calculation calculationMode) |
| 98 { | 77 { |
| 99 LayoutUnit oldColumnHeight = m_columnHeight; | 78 LayoutUnit oldColumnHeight = m_columnHeight; |
| 100 | 79 |
| 101 m_maxColumnHeight = calculateMaxColumnHeight(); | 80 m_maxColumnHeight = calculateMaxColumnHeight(); |
| 102 | 81 |
| 103 if (heightIsAuto()) { | 82 if (heightIsAuto()) { |
| 104 if (calculationMode == GuessFromFlowThreadPortion) { | |
| 105 // Post-process the content runs and find out where the implicit bre
aks will occur. | |
| 106 distributeImplicitBreaks(); | |
| 107 } | |
| 108 LayoutUnit newColumnHeight = calculateColumnHeight(calculationMode); | 83 LayoutUnit newColumnHeight = calculateColumnHeight(calculationMode); |
| 109 setAndConstrainColumnHeight(newColumnHeight); | 84 setAndConstrainColumnHeight(newColumnHeight); |
| 110 // After having calculated an initial column height, the multicol contai
ner typically needs at | 85 // After having calculated an initial column height, the multicol contai
ner typically needs at |
| 111 // least one more layout pass with a new column height, but if a height
was specified, we only | 86 // least one more layout pass with a new column height, but if a height
was specified, we only |
| 112 // need to do this if we think that we need less space than specified. C
onversely, if we | 87 // need to do this if we think that we need less space than specified. C
onversely, if we |
| 113 // determined that the columns need to be as tall as the specified heigh
t of the container, we | 88 // determined that the columns need to be as tall as the specified heigh
t of the container, we |
| 114 // have already laid it out correctly, and there's no need for another p
ass. | 89 // have already laid it out correctly, and there's no need for another p
ass. |
| 115 } else { | 90 } else { |
| 116 // The position of the column set may have changed, in which case height
available for | 91 // The position of the column set may have changed, in which case height
available for |
| 117 // columns may have changed as well. | 92 // columns may have changed as well. |
| 118 setAndConstrainColumnHeight(m_columnHeight); | 93 setAndConstrainColumnHeight(m_columnHeight); |
| 119 } | 94 } |
| 120 | 95 |
| 121 // We can get rid of the content runs now, if we haven't already done so. Th
ey are only needed | |
| 122 // to calculate the initial balanced column height. In fact, we have to get
rid of them before | |
| 123 // the next layout pass, since each pass will rebuild this. | |
| 124 m_contentRuns.clear(); | |
| 125 | |
| 126 if (m_columnHeight == oldColumnHeight) | 96 if (m_columnHeight == oldColumnHeight) |
| 127 return false; // No change. We're done. | 97 return false; // No change. We're done. |
| 128 | 98 |
| 129 m_minSpaceShortage = LayoutUnit::max(); | |
| 130 return true; // Need another pass. | 99 return true; // Need another pass. |
| 131 } | 100 } |
| 132 | 101 |
| 133 LayoutSize MultiColumnFragmentainerGroup::flowThreadTranslationAtOffset(LayoutUn
it offsetInFlowThread) const | 102 LayoutSize MultiColumnFragmentainerGroup::flowThreadTranslationAtOffset(LayoutUn
it offsetInFlowThread) const |
| 134 { | 103 { |
| 135 LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread(
); | 104 LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread(
); |
| 136 unsigned columnIndex = columnIndexAtOffset(offsetInFlowThread); | 105 unsigned columnIndex = columnIndexAtOffset(offsetInFlowThread); |
| 137 LayoutRect portionRect(flowThreadPortionRectAt(columnIndex)); | 106 LayoutRect portionRect(flowThreadPortionRectAt(columnIndex)); |
| 138 flowThread->flipForWritingMode(portionRect); | 107 flowThread->flipForWritingMode(portionRect); |
| 139 LayoutRect columnRect(columnRectAt(columnIndex)); | 108 LayoutRect columnRect(columnRectAt(columnIndex)); |
| (...skipping 227 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 367 return maxHeight; | 336 return maxHeight; |
| 368 } | 337 } |
| 369 | 338 |
| 370 void MultiColumnFragmentainerGroup::setAndConstrainColumnHeight(LayoutUnit newHe
ight) | 339 void MultiColumnFragmentainerGroup::setAndConstrainColumnHeight(LayoutUnit newHe
ight) |
| 371 { | 340 { |
| 372 m_columnHeight = newHeight; | 341 m_columnHeight = newHeight; |
| 373 if (m_columnHeight > m_maxColumnHeight) | 342 if (m_columnHeight > m_maxColumnHeight) |
| 374 m_columnHeight = m_maxColumnHeight; | 343 m_columnHeight = m_maxColumnHeight; |
| 375 } | 344 } |
| 376 | 345 |
| 377 unsigned MultiColumnFragmentainerGroup::findRunWithTallestColumns() const | |
| 378 { | |
| 379 unsigned indexWithLargestHeight = 0; | |
| 380 LayoutUnit largestHeight; | |
| 381 LayoutUnit previousOffset = m_logicalTopInFlowThread; | |
| 382 size_t runCount = m_contentRuns.size(); | |
| 383 ASSERT(runCount); | |
| 384 for (size_t i = 0; i < runCount; i++) { | |
| 385 const ContentRun& run = m_contentRuns[i]; | |
| 386 LayoutUnit height = run.columnLogicalHeight(previousOffset); | |
| 387 if (largestHeight < height) { | |
| 388 largestHeight = height; | |
| 389 indexWithLargestHeight = i; | |
| 390 } | |
| 391 previousOffset = run.breakOffset(); | |
| 392 } | |
| 393 return indexWithLargestHeight; | |
| 394 } | |
| 395 | |
| 396 void MultiColumnFragmentainerGroup::distributeImplicitBreaks() | |
| 397 { | |
| 398 #if ENABLE(ASSERT) | |
| 399 // There should be no implicit breaks assumed at this point. | |
| 400 for (unsigned i = 0; i < m_contentRuns.size(); i++) | |
| 401 ASSERT(!m_contentRuns[i].assumedImplicitBreaks()); | |
| 402 #endif // ENABLE(ASSERT) | |
| 403 | |
| 404 // Insert a final content run to encompass all content. This will include ov
erflow if this is | |
| 405 // the last set. | |
| 406 addContentRun(m_logicalBottomInFlowThread); | |
| 407 unsigned columnCount = m_contentRuns.size(); | |
| 408 | |
| 409 // If there is room for more breaks (to reach the used value of column-count
), imagine that we | |
| 410 // insert implicit breaks at suitable locations. At any given time, the cont
ent run with the | |
| 411 // currently tallest columns will get another implicit break "inserted", whi
ch will increase its | |
| 412 // column count by one and shrink its columns' height. Repeat until we have
the desired total | |
| 413 // number of breaks. The largest column height among the runs will then be t
he initial column | |
| 414 // height for the balancer to use. | |
| 415 while (columnCount < m_columnSet.usedColumnCount()) { | |
| 416 unsigned index = findRunWithTallestColumns(); | |
| 417 m_contentRuns[index].assumeAnotherImplicitBreak(); | |
| 418 columnCount++; | |
| 419 } | |
| 420 } | |
| 421 | |
| 422 LayoutUnit MultiColumnFragmentainerGroup::calculateColumnHeight(BalancedColumnHe
ightCalculation calculationMode) const | 346 LayoutUnit MultiColumnFragmentainerGroup::calculateColumnHeight(BalancedColumnHe
ightCalculation calculationMode) const |
| 423 { | 347 { |
| 424 if (calculationMode == GuessFromFlowThreadPortion) { | 348 if (calculationMode == GuessFromFlowThreadPortion) { |
| 425 // Initial balancing. Start with the lowest imaginable column height. We
use the tallest | 349 // Initial balancing. Start with the lowest imaginable column height. We
use the tallest |
| 426 // content run (after having "inserted" implicit breaks), and find its s
tart offset (by | 350 // content run (after having "inserted" implicit breaks), and find its s
tart offset (by |
| 427 // looking at the previous run's end offset, or, if there's no previous
run, the set's start | 351 // looking at the previous run's end offset, or, if there's no previous
run, the set's start |
| 428 // offset in the flow thread). | 352 // offset in the flow thread). |
| 429 unsigned index = findRunWithTallestColumns(); | 353 return std::max(InitialColumnHeightFinder::initialMinimalBalancedHeight(
*this), m_minimumColumnHeight); |
| 430 LayoutUnit startOffset = index > 0 ? m_contentRuns[index - 1].breakOffse
t() : m_logicalTopInFlowThread; | |
| 431 return std::max<LayoutUnit>(m_contentRuns[index].columnLogicalHeight(sta
rtOffset), m_minimumColumnHeight); | |
| 432 } | 354 } |
| 433 | 355 |
| 434 if (actualColumnCount() <= m_columnSet.usedColumnCount()) { | 356 if (actualColumnCount() <= m_columnSet.usedColumnCount()) { |
| 435 // With the current column height, the content fits without creating ove
rflowing columns. We're done. | 357 // With the current column height, the content fits without creating ove
rflowing columns. We're done. |
| 436 return m_columnHeight; | 358 return m_columnHeight; |
| 437 } | 359 } |
| 438 | 360 |
| 439 if (m_contentRuns.size() >= m_columnSet.usedColumnCount()) { | |
| 440 // Too many forced breaks to allow any implicit breaks. Initial balancin
g should already | |
| 441 // have set a good height. There's nothing more we should do. | |
| 442 return m_columnHeight; | |
| 443 } | |
| 444 | |
| 445 if (m_columnHeight >= m_maxColumnHeight) { | 361 if (m_columnHeight >= m_maxColumnHeight) { |
| 446 // We cannot stretch any further. We'll just have to live with the overf
lowing columns. This | 362 // We cannot stretch any further. We'll just have to live with the overf
lowing columns. This |
| 447 // typically happens if the max column height is less than the height of
the tallest piece | 363 // typically happens if the max column height is less than the height of
the tallest piece |
| 448 // of unbreakable content (e.g. lines). | 364 // of unbreakable content (e.g. lines). |
| 449 return m_columnHeight; | 365 return m_columnHeight; |
| 450 } | 366 } |
| 451 | 367 |
| 368 MinimumSpaceShortageFinder shortageFinder(*this); |
| 369 |
| 370 if (shortageFinder.forcedBreaksCount() + 1 >= m_columnSet.usedColumnCount())
{ |
| 371 // Too many forced breaks to allow any implicit breaks. Initial balancin
g should already |
| 372 // have set a good height. There's nothing more we should do. |
| 373 return m_columnHeight; |
| 374 } |
| 375 |
| 452 // If the initial guessed column height wasn't enough, stretch it now. Stret
ch by the lowest | 376 // If the initial guessed column height wasn't enough, stretch it now. Stret
ch by the lowest |
| 453 // amount of space shortage found during layout. | 377 // amount of space. |
| 378 LayoutUnit minSpaceShortage = shortageFinder.minimumSpaceShortage(); |
| 454 | 379 |
| 455 ASSERT(m_minSpaceShortage > 0); // We should never _shrink_ the height! | 380 ASSERT(minSpaceShortage > 0); // We should never _shrink_ the height! |
| 456 ASSERT(m_minSpaceShortage != LayoutUnit::max()); // If this happens, we prob
ably have a bug. | 381 ASSERT(minSpaceShortage != LayoutUnit::max()); // If this happens, we probab
ly have a bug. |
| 457 if (m_minSpaceShortage == LayoutUnit::max()) | 382 if (minSpaceShortage == LayoutUnit::max()) |
| 458 return m_columnHeight; // So bail out rather than looping infinitely. | 383 return m_columnHeight; // So bail out rather than looping infinitely. |
| 459 | 384 |
| 460 return m_columnHeight + m_minSpaceShortage; | 385 return m_columnHeight + minSpaceShortage; |
| 461 } | 386 } |
| 462 | 387 |
| 463 LayoutRect MultiColumnFragmentainerGroup::columnRectAt(unsigned columnIndex) con
st | 388 LayoutRect MultiColumnFragmentainerGroup::columnRectAt(unsigned columnIndex) con
st |
| 464 { | 389 { |
| 465 LayoutUnit columnLogicalWidth = m_columnSet.pageLogicalWidth(); | 390 LayoutUnit columnLogicalWidth = m_columnSet.pageLogicalWidth(); |
| 466 LayoutUnit columnLogicalHeight = m_columnHeight; | 391 LayoutUnit columnLogicalHeight = m_columnHeight; |
| 467 LayoutUnit columnLogicalTop; | 392 LayoutUnit columnLogicalTop; |
| 468 LayoutUnit columnLogicalLeft; | 393 LayoutUnit columnLogicalLeft; |
| 469 LayoutUnit columnGap = m_columnSet.columnGap(); | 394 LayoutUnit columnGap = m_columnSet.columnGap(); |
| 470 LayoutUnit portionOutsideFlowThread = logicalTopInFlowThread() + (columnInde
x + 1) * columnLogicalHeight - logicalBottomInFlowThread(); | 395 LayoutUnit portionOutsideFlowThread = logicalTopInFlowThread() + (columnInde
x + 1) * columnLogicalHeight - logicalBottomInFlowThread(); |
| (...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 636 append(MultiColumnFragmentainerGroup(m_columnSet)); | 561 append(MultiColumnFragmentainerGroup(m_columnSet)); |
| 637 return last(); | 562 return last(); |
| 638 } | 563 } |
| 639 | 564 |
| 640 void MultiColumnFragmentainerGroupList::deleteExtraGroups() | 565 void MultiColumnFragmentainerGroupList::deleteExtraGroups() |
| 641 { | 566 { |
| 642 shrink(1); | 567 shrink(1); |
| 643 } | 568 } |
| 644 | 569 |
| 645 } // namespace blink | 570 } // namespace blink |
| OLD | NEW |