Index: Source/core/rendering/RenderMultiColumnSet.cpp |
diff --git a/Source/core/rendering/RenderMultiColumnSet.cpp b/Source/core/rendering/RenderMultiColumnSet.cpp |
index 59b89ad3a52594fcf1a1568e587e57d52e9a2611..78413e4da9db4a7e3c5662094161206bfb464999 100644 |
--- a/Source/core/rendering/RenderMultiColumnSet.cpp |
+++ b/Source/core/rendering/RenderMultiColumnSet.cpp |
@@ -40,7 +40,8 @@ RenderMultiColumnSet::RenderMultiColumnSet(RenderFlowThread* flowThread) |
, m_computedColumnCount(1) |
, m_computedColumnWidth(0) |
, m_computedColumnHeight(0) |
- , m_requiresBalancing(false) |
+ , m_maxColumnHeight(LayoutUnit::max()) |
+ , m_minSpaceShortage(LayoutUnit::max()) |
, m_minimumColumnHeight(0) |
, m_forcedBreaksCount(0) |
, m_maximumDistanceBetweenForcedBreaks(0) |
@@ -56,13 +57,84 @@ RenderMultiColumnSet* RenderMultiColumnSet::createAnonymous(RenderFlowThread* fl |
return renderer; |
} |
+LayoutUnit RenderMultiColumnSet::heightAdjustedForSetOffset(LayoutUnit height) const |
+{ |
+ RenderMultiColumnBlock* multicolBlock = toRenderMultiColumnBlock(parent()); |
+ LayoutUnit contentLogicalTop = logicalTop() - multicolBlock->borderBefore() - multicolBlock->paddingBefore(); |
+ |
+ height -= contentLogicalTop; |
+ return max(height, LayoutUnit(1)); // Let's avoid zero height, as that would probably cause an infinite amount of columns to be created. |
+} |
+ |
LayoutUnit RenderMultiColumnSet::pageLogicalTopForOffset(LayoutUnit offset) const |
{ |
LayoutUnit portionLogicalTop = (isHorizontalWritingMode() ? flowThreadPortionRect().y() : flowThreadPortionRect().x()); |
- unsigned columnIndex = (offset - portionLogicalTop) / computedColumnHeight(); |
+ unsigned columnIndex = columnIndexAtOffset(offset, AssumeNewColumns); |
return portionLogicalTop + columnIndex * computedColumnHeight(); |
} |
+void RenderMultiColumnSet::setAndConstrainColumnHeight(LayoutUnit newHeight) |
+{ |
+ m_computedColumnHeight = newHeight; |
+ if (m_computedColumnHeight > m_maxColumnHeight) |
+ m_computedColumnHeight = m_maxColumnHeight; |
+ // FIXME: the height may also be affected by the enclosing pagination context, if any. |
+} |
+ |
+bool RenderMultiColumnSet::calculateBalancedHeight(bool initial) |
+{ |
+ ASSERT(toRenderMultiColumnBlock(parent())->requiresBalancing()); |
+ LayoutUnit oldColumnHeight = m_computedColumnHeight; |
+ LayoutUnit currentMinSpaceShortage = m_minSpaceShortage; |
+ m_minSpaceShortage = LayoutUnit::max(); |
+ |
+ 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; |
+ } |
+ |
+ if (columnCount() <= computedColumnCount()) { |
+ // With the current column height, the content fits without creating overflowing columns. We're done. |
+ return false; |
+ } |
+ |
+ // 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. |
+ |
+ setAndConstrainColumnHeight(m_computedColumnHeight + currentMinSpaceShortage); |
+ |
+ // 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::recordSpaceShortage(LayoutUnit spaceShortage) |
+{ |
+ if (spaceShortage >= m_minSpaceShortage) |
+ return; |
+ |
+ // The space shortage is what we use as our stretch amount. We need a positive number here in |
+ // order to get anywhere. |
+ ASSERT(spaceShortage > 0); |
+ |
+ m_minSpaceShortage = spaceShortage; |
+} |
+ |
void RenderMultiColumnSet::updateLogicalWidth() |
{ |
RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent()); |
@@ -74,9 +146,6 @@ void RenderMultiColumnSet::updateLogicalWidth() |
// If we overflow, increase our logical width. |
unsigned colCount = columnCount(); |
- if (!colCount) |
- return; |
- |
LayoutUnit colGap = columnGap(); |
LayoutUnit minimumContentLogicalWidth = colCount * computedColumnWidth() + (colCount - 1) * colGap; |
LayoutUnit currentContentLogicalWidth = contentLogicalWidth(); |
@@ -88,23 +157,42 @@ void RenderMultiColumnSet::updateLogicalWidth() |
setLogicalWidth(logicalWidth() + delta); |
} |
-void RenderMultiColumnSet::updateLogicalHeight() |
+void RenderMultiColumnSet::prepareForLayout() |
{ |
- // FIXME: This is the only class that overrides updateLogicalHeight. If we didn't have to set computedColumnHeight, |
- // we could remove this and make updateLogicalHeight non-virtual. https://bugs.webkit.org/show_bug.cgi?id=96804 |
- // Make sure our column height is up to date. |
- LogicalExtentComputedValues computedValues; |
- computeLogicalHeight(0, 0, computedValues); |
- setComputedColumnHeight(computedValues.m_extent); // FIXME: Once we make more than one column set, this will become variable. |
- |
- // Our logical height is always just the height of our columns. |
- setLogicalHeight(computedColumnHeight()); |
+ RenderMultiColumnBlock* multicolBlock = toRenderMultiColumnBlock(parent()); |
+ RenderStyle* multicolStyle = multicolBlock->style(); |
+ |
+ // Set box logical top. |
+ ASSERT(!previousSiblingBox() || !previousSiblingBox()->isRenderMultiColumnSet()); // FIXME: multiple set not implemented; need to examine previous set to calculate the correct logical top. |
+ setLogicalTop(multicolBlock->borderBefore() + multicolBlock->paddingBefore()); |
+ |
+ // Set box width. |
+ updateLogicalWidth(); |
+ |
+ if (multicolBlock->requiresBalancing()) { |
+ // Set maximum column height. We will not stretch beyond this. |
+ m_maxColumnHeight = LayoutUnit::max(); |
+ if (!multicolStyle->logicalHeight().isAuto()) |
+ m_maxColumnHeight = multicolBlock->computeContentLogicalHeight(multicolStyle->logicalHeight(), -1); |
+ if (!multicolStyle->logicalMaxHeight().isUndefined()) { |
+ LayoutUnit logicalMaxHeight = multicolBlock->computeContentLogicalHeight(multicolStyle->logicalMaxHeight(), -1); |
+ if (m_maxColumnHeight > logicalMaxHeight) |
+ m_maxColumnHeight = logicalMaxHeight; |
+ } |
+ m_maxColumnHeight = heightAdjustedForSetOffset(m_maxColumnHeight); |
+ m_computedColumnHeight = 0; // Restart balancing. |
+ } else { |
+ setAndConstrainColumnHeight(heightAdjustedForSetOffset(multicolBlock->columnHeightAvailable())); |
+ } |
+ |
+ // Nuke previously stored minimum column height. Contents may have changed for all we know. |
+ m_minimumColumnHeight = 0; |
} |
-void RenderMultiColumnSet::computeLogicalHeight(LayoutUnit, LayoutUnit, LogicalExtentComputedValues& computedValues) const |
+void RenderMultiColumnSet::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const |
{ |
- RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent()); |
- computedValues.m_extent = parentBlock->columnHeight(); |
+ computedValues.m_extent = m_computedColumnHeight; |
+ computedValues.m_position = logicalTop; |
} |
LayoutUnit RenderMultiColumnSet::columnGap() const |
@@ -119,12 +207,16 @@ LayoutUnit RenderMultiColumnSet::columnGap() const |
unsigned RenderMultiColumnSet::columnCount() const |
{ |
+ // We must always return a value of 1 or greater. Column count = 0 is a meaningless situation, |
+ // and will confuse and cause problems in other parts of the code. |
if (!computedColumnHeight()) |
- return 0; |
- |
+ return 1; |
+ |
// Our portion rect determines our column count. We have as many columns as needed to fit all the content. |
LayoutUnit logicalHeightInColumns = flowThread()->isHorizontalWritingMode() ? flowThreadPortionRect().height() : flowThreadPortionRect().width(); |
- return ceil(static_cast<float>(logicalHeightInColumns) / computedColumnHeight()); |
+ unsigned count = ceil(static_cast<float>(logicalHeightInColumns) / computedColumnHeight()); |
+ ASSERT(count >= 1); |
+ return count; |
} |
LayoutRect RenderMultiColumnSet::columnRectAt(unsigned index) const |
@@ -144,18 +236,22 @@ LayoutRect RenderMultiColumnSet::columnRectAt(unsigned index) const |
return LayoutRect(colLogicalTop, colLogicalLeft, colLogicalHeight, colLogicalWidth); |
} |
-unsigned RenderMultiColumnSet::columnIndexAtOffset(LayoutUnit offset) const |
+unsigned RenderMultiColumnSet::columnIndexAtOffset(LayoutUnit offset, ColumnIndexCalculationMode mode) const |
{ |
LayoutRect portionRect(flowThreadPortionRect()); |
- LayoutUnit flowThreadLogicalTop = isHorizontalWritingMode() ? portionRect.y() : portionRect.x(); |
- LayoutUnit flowThreadLogicalBottom = isHorizontalWritingMode() ? portionRect.maxY() : portionRect.maxX(); |
- |
+ |
// Handle the offset being out of range. |
+ LayoutUnit flowThreadLogicalTop = isHorizontalWritingMode() ? portionRect.y() : portionRect.x(); |
if (offset < flowThreadLogicalTop) |
return 0; |
- if (offset >= flowThreadLogicalBottom) |
- return columnCount() - 1; |
- |
+ // If we're laying out right now, we cannot constrain against some logical bottom, since it |
+ // isn't known yet. Otherwise, just return the last column if we're past the logical bottom. |
+ if (mode == ClampToExistingColumns) { |
+ LayoutUnit flowThreadLogicalBottom = isHorizontalWritingMode() ? portionRect.maxY() : portionRect.maxX(); |
+ if (offset >= flowThreadLogicalBottom) |
+ return columnCount() - 1; |
+ } |
+ |
// Just divide by the column height to determine the correct column. |
return static_cast<float>(offset - flowThreadLogicalTop) / computedColumnHeight(); |
} |