Index: Source/core/rendering/RenderMultiColumnSet.cpp |
diff --git a/Source/core/rendering/RenderMultiColumnSet.cpp b/Source/core/rendering/RenderMultiColumnSet.cpp |
index 449073752ae1e6e73f252dc473a2ef0e76f621bd..09e77b947686437415dc3e03f7bf429c257c6aa4 100644 |
--- a/Source/core/rendering/RenderMultiColumnSet.cpp |
+++ b/Source/core/rendering/RenderMultiColumnSet.cpp |
@@ -43,9 +43,6 @@ RenderMultiColumnSet::RenderMultiColumnSet(RenderFlowThread* flowThread) |
, m_maxColumnHeight(LayoutUnit::max()) |
, m_minSpaceShortage(LayoutUnit::max()) |
, m_minimumColumnHeight(0) |
- , m_forcedBreaksCount(0) |
- , m_maximumDistanceBetweenForcedBreaks(0) |
- , m_forcedBreakOffset(0) |
{ |
} |
@@ -81,46 +78,122 @@ void RenderMultiColumnSet::setAndConstrainColumnHeight(LayoutUnit newHeight) |
// FIXME: the height may also be affected by the enclosing pagination context, if any. |
} |
-bool RenderMultiColumnSet::calculateBalancedHeight(bool initial) |
+unsigned RenderMultiColumnSet::findRunWithTallestColumns() const |
{ |
- ASSERT(toRenderMultiColumnBlock(parent())->requiresBalancing()); |
- LayoutUnit oldColumnHeight = m_computedColumnHeight; |
- LayoutUnit currentMinSpaceShortage = m_minSpaceShortage; |
- m_minSpaceShortage = LayoutUnit::max(); |
+ unsigned indexWithLargestHeight = 0; |
+ LayoutUnit largestHeight; |
+ LayoutUnit previousOffset; |
+ size_t runCount = m_contentRuns.size(); |
+ ASSERT(runCount); |
+ for (size_t i = 0; i < runCount; i++) { |
+ const ContentRun& run = m_contentRuns[i]; |
+ LayoutUnit height = run.columnLogicalHeight(previousOffset); |
+ if (largestHeight < height) { |
+ largestHeight = height; |
+ indexWithLargestHeight = i; |
+ } |
+ previousOffset = run.breakOffset(); |
+ } |
+ return indexWithLargestHeight; |
+} |
+ |
+void RenderMultiColumnSet::distributeImplicitBreaks() |
+{ |
+ unsigned breakCount = forcedBreaksCount(); |
+ |
+#ifndef NDEBUG |
+ // There should be no implicit breaks assumed at this point. |
+ for (unsigned i = 0; i < breakCount; i++) |
+ ASSERT(!m_contentRuns[i].assumedImplicitBreaks()); |
+#endif // NDEBUG |
+ |
+ // There will always be at least one break, since the flow thread reports a "forced break" at |
+ // end of content. |
+ ASSERT(breakCount >= 1); |
+ |
+ // If there is room for more breaks (to reach the used value of column-count), imagine that we |
+ // insert implicit breaks at suitable locations. At any given time, the content run with the |
+ // currently tallest columns will get another implicit break "inserted", which will increase its |
+ // column count by one and shrink its columns' height. Repeat until we have the desired total |
+ // number of breaks. The largest column height among the runs will then be the initial column |
+ // height for the balancer to use. |
+ while (breakCount < m_computedColumnCount) { |
+ unsigned index = findRunWithTallestColumns(); |
+ m_contentRuns[index].assumeAnotherImplicitBreak(); |
+ breakCount++; |
+ } |
+} |
+LayoutUnit RenderMultiColumnSet::calculateBalancedHeight(bool initial) const |
+{ |
if (initial) { |
// Start with the lowest imaginable column height. |
- LayoutUnit logicalHeightGuess = ceilf(float(flowThread()->logicalHeight()) / float(m_computedColumnCount)); |
- logicalHeightGuess = max(logicalHeightGuess, m_minimumColumnHeight); |
- setAndConstrainColumnHeight(logicalHeightGuess); |
- |
- // The multicol container now typically needs at least one more layout pass with a new |
- // column height, but if height was specified, we only need to do this if we found that we |
- // might need less space than that. On the other hand, if we determined that the columns |
- // need to be as tall as the specified height of the container, we have already laid it out |
- // correctly, and there's no need for another pass. |
- return m_computedColumnHeight != oldColumnHeight; |
+ unsigned index = findRunWithTallestColumns(); |
+ LayoutUnit startOffset = index > 0 ? m_contentRuns[index - 1].breakOffset() : LayoutUnit(0); |
ojan
2014/02/08 02:26:12
Ditto
mstensho (USE GERRIT)
2014/02/10 13:50:25
Done.
|
+ return std::max<LayoutUnit>(m_contentRuns[index].columnLogicalHeight(startOffset), m_minimumColumnHeight); |
} |
if (columnCount() <= computedColumnCount()) { |
// With the current column height, the content fits without creating overflowing columns. We're done. |
- return false; |
+ return m_computedColumnHeight; |
+ } |
+ |
+ if (forcedBreaksCount() > 1 && forcedBreaksCount() >= computedColumnCount()) { |
+ // Too many forced breaks to allow any implicit breaks. Initial balancing should already |
+ // have set a good height. There's nothing more we should do. |
+ return m_computedColumnHeight; |
} |
// If the initial guessed column height wasn't enough, stretch it now. Stretch by the lowest |
// amount of space shortage found during layout. |
- ASSERT(currentMinSpaceShortage != LayoutUnit::max()); // If this can actually happen, we probably have a bug. |
- if (currentMinSpaceShortage == LayoutUnit::max()) |
- return false; // So bail out rather than looping infinitely. |
+ ASSERT(m_minSpaceShortage > 0); // We should never _shrink_ the height! |
+ ASSERT(m_minSpaceShortage != LayoutUnit::max()); // If this happens, we probably have a bug. |
+ if (m_minSpaceShortage == LayoutUnit::max()) |
+ return m_computedColumnHeight; // So bail out rather than looping infinitely. |
- setAndConstrainColumnHeight(m_computedColumnHeight + currentMinSpaceShortage); |
+ return m_computedColumnHeight + m_minSpaceShortage; |
+} |
- // If we reach the maximum column height (typically set by the height or max-height property), |
- // we may not be allowed to stretch further. Return true only if stretching |
- // succeeded. Otherwise, we're done. |
- ASSERT(m_computedColumnHeight >= oldColumnHeight); // We shouldn't be able to shrink the height! |
- return m_computedColumnHeight > oldColumnHeight; |
+void RenderMultiColumnSet::clearForcedBreaks() |
+{ |
+ m_contentRuns.clear(); |
+} |
+ |
+void RenderMultiColumnSet::addForcedBreak(LayoutUnit offsetFromFirstPage) |
+{ |
+ if (!toRenderMultiColumnBlock(parent())->requiresBalancing()) |
+ return; |
+ if (!m_contentRuns.isEmpty() && offsetFromFirstPage <= m_contentRuns.last().breakOffset()) |
+ return; |
+ // Append another item as long as we haven't exceeded used column count. What ends up in the |
+ // overflow area shouldn't affect column balancing. |
+ if (m_contentRuns.size() < m_computedColumnCount) |
+ m_contentRuns.append(ContentRun(offsetFromFirstPage)); |
+} |
+ |
+bool RenderMultiColumnSet::recalculateBalancedHeight(bool initial) |
+{ |
+ ASSERT(toRenderMultiColumnBlock(parent())->requiresBalancing()); |
+ |
+ LayoutUnit oldColumnHeight = m_computedColumnHeight; |
+ if (initial) |
+ distributeImplicitBreaks(); |
+ LayoutUnit newColumnHeight = calculateBalancedHeight(initial); |
+ setAndConstrainColumnHeight(newColumnHeight); |
+ |
+ // After having calculated an initial column height, the multicol container typically needs at |
+ // least one more layout pass with a new column height, but if a height was specified, we only |
+ // need to do this if we think that we need less space than specified. Conversely, if we |
+ // determined that the columns need to be as tall as the specified height of the container, we |
+ // have already laid it out correctly, and there's no need for another pass. |
+ |
+ if (m_computedColumnHeight == oldColumnHeight) |
+ return false; // No change. We're done. |
+ |
+ m_minSpaceShortage = LayoutUnit::max(); |
+ clearForcedBreaks(); |
+ return true; // Need another pass. |
} |
void RenderMultiColumnSet::recordSpaceShortage(LayoutUnit spaceShortage) |
@@ -185,6 +258,8 @@ void RenderMultiColumnSet::prepareForLayout() |
setAndConstrainColumnHeight(heightAdjustedForSetOffset(multicolBlock->columnHeightAvailable())); |
} |
+ clearForcedBreaks(); |
+ |
// Nuke previously stored minimum column height. Contents may have changed for all we know. |
m_minimumColumnHeight = 0; |
} |