Index: Source/core/rendering/RenderMultiColumnSet.cpp |
diff --git a/Source/core/rendering/RenderMultiColumnSet.cpp b/Source/core/rendering/RenderMultiColumnSet.cpp |
index 7d53c06537eb2e609e40a57a550c6038e808daee..0ed1a9dba234316f28173a28844b46913c0cb36b 100644 |
--- a/Source/core/rendering/RenderMultiColumnSet.cpp |
+++ b/Source/core/rendering/RenderMultiColumnSet.cpp |
@@ -26,6 +26,7 @@ |
#include "config.h" |
#include "core/rendering/RenderMultiColumnSet.h" |
+#include "core/layout/MultiColumnFragmentainerGroup.h" |
#include "core/paint/MultiColumnSetPainter.h" |
#include "core/rendering/RenderMultiColumnFlowThread.h" |
@@ -33,10 +34,7 @@ namespace blink { |
RenderMultiColumnSet::RenderMultiColumnSet(RenderFlowThread* flowThread) |
: RenderRegion(0, flowThread) |
- , m_columnHeight(0) |
- , m_maxColumnHeight(RenderFlowThread::maxLogicalHeight()) |
- , m_minSpaceShortage(RenderFlowThread::maxLogicalHeight()) |
- , m_minimumColumnHeight(0) |
+ , m_fragmentainerGroups(*this) |
{ |
} |
@@ -49,6 +47,25 @@ RenderMultiColumnSet* RenderMultiColumnSet::createAnonymous(RenderFlowThread& fl |
return renderer; |
} |
+MultiColumnFragmentainerGroup& RenderMultiColumnSet::fragmentainerGroupAtFlowThreadOffset(LayoutUnit) |
+{ |
+ // FIXME: implement this, once we have support for multiple rows. |
+ return m_fragmentainerGroups.first(); |
+} |
+ |
+const MultiColumnFragmentainerGroup& RenderMultiColumnSet::fragmentainerGroupAtFlowThreadOffset(LayoutUnit) const |
+{ |
+ // FIXME: implement this, once we have support for multiple rows. |
+ return m_fragmentainerGroups.first(); |
+} |
+ |
+LayoutUnit RenderMultiColumnSet::pageLogicalHeight() const |
+{ |
+ // FIXME: pageLogicalHeight() needs to take a flow thread offset parameter, so that we can |
+ // locate the right row. |
+ return firstFragmentainerGroup().logicalHeight(); |
+} |
+ |
RenderMultiColumnSet* RenderMultiColumnSet::nextSiblingMultiColumnSet() const |
{ |
for (LayoutObject* sibling = nextSibling(); sibling; sibling = sibling->nextSibling()) { |
@@ -67,24 +84,14 @@ RenderMultiColumnSet* RenderMultiColumnSet::previousSiblingMultiColumnSet() cons |
return 0; |
} |
-void RenderMultiColumnSet::setLogicalTopInFlowThread(LayoutUnit logicalTop) |
+LayoutUnit RenderMultiColumnSet::logicalTopInFlowThread() const |
{ |
- LayoutRect rect = flowThreadPortionRect(); |
- if (isHorizontalWritingMode()) |
- rect.setY(logicalTop); |
- else |
- rect.setX(logicalTop); |
- setFlowThreadPortionRect(rect); |
+ return firstFragmentainerGroup().logicalTopInFlowThread(); |
} |
-void RenderMultiColumnSet::setLogicalBottomInFlowThread(LayoutUnit logicalBottom) |
+LayoutUnit RenderMultiColumnSet::logicalBottomInFlowThread() const |
{ |
- LayoutRect rect = flowThreadPortionRect(); |
- if (isHorizontalWritingMode()) |
- rect.shiftMaxYEdgeTo(logicalBottom); |
- else |
- rect.shiftMaxXEdgeTo(logicalBottom); |
- setFlowThreadPortionRect(rect); |
+ return lastFragmentainerGroup().logicalBottomInFlowThread(); |
} |
bool RenderMultiColumnSet::heightIsAuto() const |
@@ -105,245 +112,79 @@ bool RenderMultiColumnSet::heightIsAuto() const |
LayoutSize RenderMultiColumnSet::flowThreadTranslationAtOffset(LayoutUnit blockOffset) const |
{ |
- unsigned columnIndex = columnIndexAtOffset(blockOffset); |
- LayoutRect portionRect(flowThreadPortionRectAt(columnIndex)); |
- flipForWritingMode(portionRect); |
- LayoutRect columnRect(columnRectAt(columnIndex)); |
- flipForWritingMode(columnRect); |
- return toLayoutPoint(contentBoxOffset()) + columnRect.location() - portionRect.location(); |
-} |
- |
-LayoutUnit RenderMultiColumnSet::heightAdjustedForSetOffset(LayoutUnit height) const |
-{ |
- // Adjust for the top offset within the content box of the multicol container (containing |
- // block), unless this is the first set. We know that the top offset for the first set will be |
- // zero, but if the multicol container has non-zero top border or padding, the set's top offset |
- // (initially being 0 and relative to the border box) will be negative until it has been laid |
- // out. Had we used this bogus offset, we would calculate the wrong height, and risk performing |
- // a wasted layout iteration. Of course all other sets (if any) have this problem in the first |
- // layout pass too, but there's really nothing we can do there until the flow thread has been |
- // laid out anyway. |
- if (previousSiblingMultiColumnSet()) { |
- RenderBlockFlow* multicolBlock = multiColumnBlockFlow(); |
- LayoutUnit contentLogicalTop = logicalTop() - multicolBlock->borderAndPaddingBefore(); |
- 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. |
+ const MultiColumnFragmentainerGroup& row = fragmentainerGroupAtFlowThreadOffset(blockOffset); |
+ return row.offsetFromColumnSet() + row.flowThreadTranslationAtOffset(blockOffset); |
} |
-LayoutUnit RenderMultiColumnSet::pageLogicalTopForOffset(LayoutUnit offset) const |
+void RenderMultiColumnSet::updateMinimumColumnHeight(LayoutUnit offsetInFlowThread, LayoutUnit height) |
{ |
- unsigned columnIndex = columnIndexAtOffset(offset, AssumeNewColumns); |
- return logicalTopInFlowThread() + columnIndex * pageLogicalHeight(); |
+ fragmentainerGroupAtFlowThreadOffset(offsetInFlowThread).updateMinimumColumnHeight(height); |
} |
-void RenderMultiColumnSet::setAndConstrainColumnHeight(LayoutUnit newHeight) |
-{ |
- m_columnHeight = newHeight; |
- if (m_columnHeight > m_maxColumnHeight) |
- m_columnHeight = m_maxColumnHeight; |
- // FIXME: the height may also be affected by the enclosing pagination context, if any. |
-} |
- |
-unsigned RenderMultiColumnSet::findRunWithTallestColumns() const |
+LayoutUnit RenderMultiColumnSet::pageLogicalTopForOffset(LayoutUnit offset) const |
{ |
- unsigned indexWithLargestHeight = 0; |
- LayoutUnit largestHeight; |
- LayoutUnit previousOffset = logicalTopInFlowThread(); |
- 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() |
-{ |
-#if ENABLE(ASSERT) |
- // There should be no implicit breaks assumed at this point. |
- for (unsigned i = 0; i < m_contentRuns.size(); i++) |
- ASSERT(!m_contentRuns[i].assumedImplicitBreaks()); |
-#endif // ENABLE(ASSERT) |
- |
- // Insert a final content run to encompass all content. This will include overflow if this is |
- // the last set. |
- addContentRun(logicalBottomInFlowThread()); |
- unsigned columnCount = m_contentRuns.size(); |
- |
- // 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 (columnCount < usedColumnCount()) { |
- unsigned index = findRunWithTallestColumns(); |
- m_contentRuns[index].assumeAnotherImplicitBreak(); |
- columnCount++; |
- } |
-} |
- |
-LayoutUnit RenderMultiColumnSet::calculateColumnHeight(BalancedHeightCalculation calculationMode) const |
-{ |
- if (calculationMode == GuessFromFlowThreadPortion) { |
- // Initial balancing. Start with the lowest imaginable column height. We use the tallest |
- // content run (after having "inserted" implicit breaks), and find its start offset (by |
- // looking at the previous run's end offset, or, if there's no previous run, the set's start |
- // offset in the flow thread). |
- unsigned index = findRunWithTallestColumns(); |
- LayoutUnit startOffset = index > 0 ? m_contentRuns[index - 1].breakOffset() : logicalTopInFlowThread(); |
- return std::max<LayoutUnit>(m_contentRuns[index].columnLogicalHeight(startOffset), m_minimumColumnHeight); |
- } |
- |
- if (actualColumnCount() <= usedColumnCount()) { |
- // With the current column height, the content fits without creating overflowing columns. We're done. |
- return m_columnHeight; |
- } |
- |
- if (m_contentRuns.size() >= usedColumnCount()) { |
- // 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_columnHeight; |
- } |
- |
- // If the initial guessed column height wasn't enough, stretch it now. Stretch by the lowest |
- // amount of space shortage found during layout. |
- |
- ASSERT(m_minSpaceShortage > 0); // We should never _shrink_ the height! |
- ASSERT(m_minSpaceShortage != RenderFlowThread::maxLogicalHeight()); // If this happens, we probably have a bug. |
- if (m_minSpaceShortage == RenderFlowThread::maxLogicalHeight()) |
- return m_columnHeight; // So bail out rather than looping infinitely. |
- |
- return m_columnHeight + m_minSpaceShortage; |
+ return fragmentainerGroupAtFlowThreadOffset(offset).columnLogicalTopForOffset(offset); |
} |
void RenderMultiColumnSet::addContentRun(LayoutUnit endOffsetFromFirstPage) |
{ |
if (!heightIsAuto()) |
return; |
- if (!m_contentRuns.isEmpty() && endOffsetFromFirstPage <= 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() < usedColumnCount()) |
- m_contentRuns.append(ContentRun(endOffsetFromFirstPage)); |
+ fragmentainerGroupAtFlowThreadOffset(endOffsetFromFirstPage).addContentRun(endOffsetFromFirstPage); |
} |
-bool RenderMultiColumnSet::recalculateColumnHeight(BalancedHeightCalculation calculationMode) |
+bool RenderMultiColumnSet::recalculateColumnHeight(BalancedColumnHeightCalculation calculationMode) |
{ |
- LayoutUnit oldColumnHeight = m_columnHeight; |
- |
- m_maxColumnHeight = calculateMaxColumnHeight(); |
- |
- if (heightIsAuto()) { |
- if (calculationMode == GuessFromFlowThreadPortion) { |
- // Post-process the content runs and find out where the implicit breaks will occur. |
- distributeImplicitBreaks(); |
- } |
- LayoutUnit newColumnHeight = calculateColumnHeight(calculationMode); |
- 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. |
- } else { |
- // The position of the column set may have changed, in which case height available for |
- // columns may have changed as well. |
- setAndConstrainColumnHeight(m_columnHeight); |
- } |
- |
- // We can get rid of the content runs now, if we haven't already done so. They are only needed |
- // to calculate the initial balanced column height. In fact, we have to get rid of them before |
- // the next layout pass, since each pass will rebuild this. |
- m_contentRuns.clear(); |
- |
- if (m_columnHeight == oldColumnHeight) |
- return false; // No change. We're done. |
- |
- m_minSpaceShortage = RenderFlowThread::maxLogicalHeight(); |
- return true; // Need another pass. |
+ bool changed = false; |
+ for (auto& group : m_fragmentainerGroups) |
+ changed = group.recalculateColumnHeight(calculationMode) || changed; |
+ return changed; |
} |
-void RenderMultiColumnSet::recordSpaceShortage(LayoutUnit spaceShortage) |
+void RenderMultiColumnSet::recordSpaceShortage(LayoutUnit offsetInFlowThread, 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; |
+ fragmentainerGroupAtFlowThreadOffset(offsetInFlowThread).recordSpaceShortage(spaceShortage); |
} |
void RenderMultiColumnSet::resetColumnHeight() |
{ |
- // Nuke previously stored minimum column height. Contents may have changed for all we know. |
- m_minimumColumnHeight = 0; |
- |
- m_maxColumnHeight = calculateMaxColumnHeight(); |
- |
- LayoutUnit oldColumnHeight = pageLogicalHeight(); |
- |
- if (heightIsAuto()) |
- m_columnHeight = 0; |
- else |
- setAndConstrainColumnHeight(heightAdjustedForSetOffset(multiColumnFlowThread()->columnHeightAvailable())); |
+ m_fragmentainerGroups.deleteExtraGroups(); |
+ m_fragmentainerGroups.first().resetColumnHeight(); |
+} |
- if (pageLogicalHeight() != oldColumnHeight) |
- setChildNeedsLayout(MarkOnlyThis); |
+void RenderMultiColumnSet::beginFlow(LayoutUnit offsetInFlowThread) |
+{ |
+ // At this point layout is exactly at the beginning of this set. Store block offset from flow |
+ // thread start. |
+ m_fragmentainerGroups.first().setLogicalTopInFlowThread(offsetInFlowThread); |
+} |
- // Content runs are only needed in the initial layout pass, in order to find an initial column |
- // height, and should have been deleted afterwards. We're about to rebuild the content runs, so |
- // the list needs to be empty. |
- ASSERT(m_contentRuns.isEmpty()); |
+void RenderMultiColumnSet::endFlow(LayoutUnit offsetInFlowThread) |
+{ |
+ // At this point layout is exactly at the end of this set. Store block offset from flow thread |
+ // start. This set is now considered "flowed", although we may have to revisit it later (with |
+ // beginFlow()), e.g. if a subtree in the flow thread has to be laid out over again because the |
+ // initial margin collapsing estimates were wrong. |
+ m_fragmentainerGroups.last().setLogicalBottomInFlowThread(offsetInFlowThread); |
} |
void RenderMultiColumnSet::expandToEncompassFlowThreadContentsIfNeeded() |
{ |
ASSERT(multiColumnFlowThread()->lastMultiColumnSet() == this); |
- LayoutRect rect(flowThreadPortionRect()); |
- |
- // Get the offset within the flow thread in its block progression direction. Then get the |
- // flow thread's remaining logical height including its overflow and expand our rect |
- // to encompass that remaining height and overflow. The idea is that we will generate |
- // additional columns and pages to hold that overflow, since people do write bad |
- // content like <body style="height:0px"> in multi-column layouts. |
- bool isHorizontal = flowThread()->isHorizontalWritingMode(); |
- LayoutUnit logicalTopOffset = isHorizontal ? rect.y() : rect.x(); |
- LayoutRect layoutRect = flowThread()->layoutOverflowRect(); |
- LayoutUnit logicalHeightWithOverflow = (isHorizontal ? layoutRect.maxY() : layoutRect.maxX()) - logicalTopOffset; |
- setFlowThreadPortionRect(LayoutRect(rect.x(), rect.y(), isHorizontal ? rect.width() : logicalHeightWithOverflow, isHorizontal ? logicalHeightWithOverflow : rect.height())); |
+ // FIXME: this may result in the need for creating additional rows, since there may not be |
+ // enough space remaining in the currently last row. |
+ m_fragmentainerGroups.last().expandToEncompassFlowThreadOverflow(); |
} |
void RenderMultiColumnSet::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const |
{ |
- computedValues.m_extent = m_columnHeight; |
+ LayoutUnit logicalHeight; |
+ for (const auto& group : m_fragmentainerGroups) |
+ logicalHeight += group.logicalHeight(); |
+ computedValues.m_extent = logicalHeight; |
computedValues.m_position = logicalTop; |
} |
-LayoutUnit RenderMultiColumnSet::calculateMaxColumnHeight() const |
-{ |
- RenderBlockFlow* multicolBlock = multiColumnBlockFlow(); |
- const LayoutStyle& multicolStyle = multicolBlock->styleRef(); |
- LayoutUnit availableHeight = multiColumnFlowThread()->columnHeightAvailable(); |
- LayoutUnit maxColumnHeight = availableHeight ? availableHeight : RenderFlowThread::maxLogicalHeight(); |
- if (!multicolStyle.logicalMaxHeight().isMaxSizeNone()) { |
- LayoutUnit logicalMaxHeight = multicolBlock->computeContentLogicalHeight(multicolStyle.logicalMaxHeight(), -1); |
- if (logicalMaxHeight != -1 && maxColumnHeight > logicalMaxHeight) |
- maxColumnHeight = logicalMaxHeight; |
- } |
- return heightAdjustedForSetOffset(maxColumnHeight); |
-} |
- |
LayoutUnit RenderMultiColumnSet::columnGap() const |
{ |
RenderBlockFlow* parentBlock = multiColumnBlockFlow(); |
@@ -354,110 +195,9 @@ LayoutUnit RenderMultiColumnSet::columnGap() const |
unsigned RenderMultiColumnSet::actualColumnCount() 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 (!pageLogicalHeight()) |
- 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(); |
- if (!logicalHeightInColumns) |
- return 1; |
- |
- unsigned count = ceil(logicalHeightInColumns.toFloat() / pageLogicalHeight().toFloat()); |
- ASSERT(count >= 1); |
- return count; |
-} |
- |
-LayoutRect RenderMultiColumnSet::columnRectAt(unsigned index) const |
-{ |
- LayoutUnit colLogicalWidth = pageLogicalWidth(); |
- LayoutUnit colLogicalHeight = pageLogicalHeight(); |
- LayoutUnit colLogicalTop = borderBefore() + paddingBefore(); |
- LayoutUnit colLogicalLeft = borderAndPaddingLogicalLeft(); |
- LayoutUnit colGap = columnGap(); |
- |
- if (multiColumnFlowThread()->progressionIsInline()) { |
- if (style()->isLeftToRightDirection()) |
- colLogicalLeft += index * (colLogicalWidth + colGap); |
- else |
- colLogicalLeft += contentLogicalWidth() - colLogicalWidth - index * (colLogicalWidth + colGap); |
- } else { |
- colLogicalTop += index * (colLogicalHeight + colGap); |
- } |
- |
- if (isHorizontalWritingMode()) |
- return LayoutRect(colLogicalLeft, colLogicalTop, colLogicalWidth, colLogicalHeight); |
- return LayoutRect(colLogicalTop, colLogicalLeft, colLogicalHeight, colLogicalWidth); |
-} |
- |
-unsigned RenderMultiColumnSet::columnIndexAtOffset(LayoutUnit offset, ColumnIndexCalculationMode mode) const |
-{ |
- LayoutRect portionRect(flowThreadPortionRect()); |
- |
- // Handle the offset being out of range. |
- LayoutUnit flowThreadLogicalTop = isHorizontalWritingMode() ? portionRect.y() : portionRect.x(); |
- if (offset < flowThreadLogicalTop) |
- return 0; |
- // 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 actualColumnCount() - 1; |
- } |
- |
- if (LayoutUnit pageLogicalHeight = this->pageLogicalHeight()) |
- return (offset - flowThreadLogicalTop).toFloat() / pageLogicalHeight.toFloat(); |
- |
- return 0; |
-} |
- |
-LayoutRect RenderMultiColumnSet::flowThreadPortionRectAt(unsigned index) const |
-{ |
- LayoutRect portionRect = flowThreadPortionRect(); |
- if (isHorizontalWritingMode()) |
- portionRect = LayoutRect(portionRect.x(), portionRect.y() + index * pageLogicalHeight(), portionRect.width(), pageLogicalHeight()); |
- else |
- portionRect = LayoutRect(portionRect.x() + index * pageLogicalHeight(), portionRect.y(), pageLogicalHeight(), portionRect.height()); |
- return portionRect; |
-} |
- |
-LayoutRect RenderMultiColumnSet::flowThreadPortionOverflowRect(const LayoutRect& portionRect, unsigned index, unsigned colCount, LayoutUnit colGap) const |
-{ |
- // This function determines the portion of the flow thread that paints for the column. Along the inline axis, columns are |
- // unclipped at outside edges (i.e., the first and last column in the set), and they clip to half the column |
- // gap along interior edges. |
- // |
- // In the block direction, we will not clip overflow out of the top of the first column, or out of the bottom of |
- // the last column. This applies only to the true first column and last column across all column sets. |
- // |
- // FIXME: Eventually we will know overflow on a per-column basis, but we can't do this until we have a painting |
- // mode that understands not to paint contents from a previous column in the overflow area of a following column. |
- // This problem applies to regions and pages as well and is not unique to columns. |
- bool isFirstColumn = !index; |
- bool isLastColumn = index == colCount - 1; |
- bool isLeftmostColumn = style()->isLeftToRightDirection() ? isFirstColumn : isLastColumn; |
- bool isRightmostColumn = style()->isLeftToRightDirection() ? isLastColumn : isFirstColumn; |
- |
- // Calculate the overflow rectangle, based on the flow thread's, clipped at column logical |
- // top/bottom unless it's the first/last column. |
- LayoutRect overflowRect = overflowRectForFlowThreadPortion(portionRect, isFirstColumn && isFirstRegion(), isLastColumn && isLastRegion()); |
- |
- // Avoid overflowing into neighboring columns, by clipping in the middle of adjacent column |
- // gaps. Also make sure that we avoid rounding errors. |
- if (isHorizontalWritingMode()) { |
- if (!isLeftmostColumn) |
- overflowRect.shiftXEdgeTo(portionRect.x() - colGap / 2); |
- if (!isRightmostColumn) |
- overflowRect.shiftMaxXEdgeTo(portionRect.maxX() + colGap - colGap / 2); |
- } else { |
- if (!isLeftmostColumn) |
- overflowRect.shiftYEdgeTo(portionRect.y() - colGap / 2); |
- if (!isRightmostColumn) |
- overflowRect.shiftMaxYEdgeTo(portionRect.maxY() + colGap - colGap / 2); |
- } |
- return overflowRect; |
+ // FIXME: remove this method. It's a meaningless question to ask the set "how many columns do |
+ // you actually have?", since that may vary for each row. |
+ return firstFragmentainerGroup().actualColumnCount(); |
} |
void RenderMultiColumnSet::paintObject(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) |
@@ -467,125 +207,21 @@ void RenderMultiColumnSet::paintObject(const PaintInfo& paintInfo, const LayoutP |
void RenderMultiColumnSet::collectLayerFragments(LayerFragments& fragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect) |
{ |
- // |layerBoundingBox| is in the flow thread coordinate space, relative to the top/left edge of |
- // the flow thread, but note that it has been converted with respect to writing mode (so that |
- // it's visual/physical in that sense). |
- // |
- // |dirtyRect| is visual, relative to the multicol container. |
- // |
- // Then there's the output from this method - the stuff we put into the list of fragments. The |
- // fragment.paginationOffset point is the actual visual translation required to get from a |
- // location in the flow thread to a location in a given column. The fragment.paginationClip |
- // rectangle, on the other hand, is in flow thread coordinates. |
- // |
- // All other rectangles in this method are sized physically, and the inline direction coordinate |
- // is physical too, but the block direction coordinate is "logical top". This is the same as |
- // e.g. RenderBox::frameRect(). These rectangles also pretend that there's only one long column, |
- // i.e. they are for the flow thread. |
- |
- // Put the layer bounds into flow thread-local coordinates by flipping it first. Since we're in |
- // a renderer, most rectangles are represented this way. |
- LayoutRect layerBoundsInFlowThread(layerBoundingBox); |
- flowThread()->flipForWritingMode(layerBoundsInFlowThread); |
- |
- // Now we can compare with the flow thread portions owned by each column. First let's |
- // see if the rect intersects our flow thread portion at all. |
- LayoutRect clippedRect(layerBoundsInFlowThread); |
- clippedRect.intersect(RenderRegion::flowThreadPortionOverflowRect()); |
- if (clippedRect.isEmpty()) |
- return; |
- |
- // Now we know we intersect at least one column. Let's figure out the logical top and logical |
- // bottom of the area we're checking. |
- LayoutUnit layerLogicalTop = isHorizontalWritingMode() ? layerBoundsInFlowThread.y() : layerBoundsInFlowThread.x(); |
- LayoutUnit layerLogicalBottom = (isHorizontalWritingMode() ? layerBoundsInFlowThread.maxY() : layerBoundsInFlowThread.maxX()) - 1; |
- |
- // Figure out the start and end columns and only check within that range so that we don't walk the |
- // entire column set. |
- unsigned startColumn = columnIndexAtOffset(layerLogicalTop); |
- unsigned endColumn = columnIndexAtOffset(layerLogicalBottom); |
- |
- LayoutUnit colLogicalWidth = pageLogicalWidth(); |
- LayoutUnit colGap = columnGap(); |
- unsigned colCount = actualColumnCount(); |
- |
- RenderMultiColumnFlowThread* flowThread = multiColumnFlowThread(); |
- bool progressionIsInline = flowThread->progressionIsInline(); |
- bool leftToRight = style()->isLeftToRightDirection(); |
- |
- LayoutUnit initialBlockOffset = logicalTop() - flowThread->logicalTop(); |
- |
- for (unsigned i = startColumn; i <= endColumn; i++) { |
- // Get the portion of the flow thread that corresponds to this column. |
- LayoutRect flowThreadPortion = flowThreadPortionRectAt(i); |
- |
- // Now get the overflow rect that corresponds to the column. |
- LayoutRect flowThreadOverflowPortion = flowThreadPortionOverflowRect(flowThreadPortion, i, colCount, colGap); |
- |
- // In order to create a fragment we must intersect the portion painted by this column. |
- LayoutRect clippedRect(layerBoundsInFlowThread); |
- clippedRect.intersect(flowThreadOverflowPortion); |
- if (clippedRect.isEmpty()) |
- continue; |
- |
- // We also need to intersect the dirty rect. We have to apply a translation and shift based off |
- // our column index. |
- LayoutPoint translationOffset; |
- LayoutUnit inlineOffset = progressionIsInline ? i * (colLogicalWidth + colGap) : LayoutUnit(); |
- if (!leftToRight) |
- inlineOffset = -inlineOffset; |
- translationOffset.setX(inlineOffset); |
- LayoutUnit blockOffset; |
- if (progressionIsInline) { |
- blockOffset = initialBlockOffset + (isHorizontalWritingMode() ? -flowThreadPortion.y() : -flowThreadPortion.x()); |
- } else { |
- // Column gap can apply in the block direction for page fragmentainers. |
- // There is currently no spec which calls for column-gap to apply |
- // for page fragmentainers at all, but it's applied here for compatibility |
- // with the old multicolumn implementation. |
- blockOffset = i * colGap; |
- } |
- if (isFlippedBlocksWritingMode(style()->writingMode())) |
- blockOffset = -blockOffset; |
- translationOffset.setY(blockOffset); |
- if (!isHorizontalWritingMode()) |
- translationOffset = translationOffset.transposedPoint(); |
- // FIXME: The translation needs to include the multicolumn set's content offset within the |
- // multicolumn block as well. This won't be an issue until we start creating multiple multicolumn sets. |
- |
- // Shift the dirty rect to be in flow thread coordinates with this translation applied. |
- LayoutRect translatedDirtyRect(dirtyRect); |
- translatedDirtyRect.moveBy(-translationOffset); |
- |
- // See if we intersect the dirty rect. |
- clippedRect = layerBoundingBox; |
- clippedRect.intersect(translatedDirtyRect); |
- if (clippedRect.isEmpty()) |
- continue; |
- |
- // Something does need to paint in this column. Make a fragment now and supply the physical translation |
- // offset and the clip rect for the column with that offset applied. |
- LayerFragment fragment; |
- fragment.paginationOffset = translationOffset; |
- |
- LayoutRect flippedFlowThreadOverflowPortion(flowThreadOverflowPortion); |
- // Flip it into more a physical (Layer-style) rectangle. |
- flowThread->flipForWritingMode(flippedFlowThreadOverflowPortion); |
- fragment.paginationClip = flippedFlowThreadOverflowPortion; |
- fragments.append(fragment); |
- } |
+ for (const auto& group : m_fragmentainerGroups) |
+ group.collectLayerFragments(fragments, layerBoundingBox, dirtyRect); |
} |
void RenderMultiColumnSet::addOverflowFromChildren() |
{ |
- unsigned colCount = actualColumnCount(); |
- if (!colCount) |
- return; |
- |
- LayoutRect lastRect = columnRectAt(colCount - 1); |
- addLayoutOverflow(lastRect); |
+ LayoutRect overflowRect; |
+ for (const auto& group : m_fragmentainerGroups) { |
+ LayoutRect rect = group.calculateOverflow(); |
+ rect.move(group.offsetFromColumnSet()); |
+ overflowRect.unite(rect); |
+ } |
+ addLayoutOverflow(overflowRect); |
if (!hasOverflowClip()) |
- addVisualOverflow(lastRect); |
+ addVisualOverflow(overflowRect); |
} |
const char* RenderMultiColumnSet::renderName() const |
@@ -630,4 +266,12 @@ void RenderMultiColumnSet::detachRegion() |
} |
} |
+LayoutRect RenderMultiColumnSet::flowThreadPortionRect() const |
+{ |
+ LayoutRect portionRect(LayoutUnit(), logicalTopInFlowThread(), pageLogicalWidth(), logicalHeightInFlowThread()); |
+ if (!isHorizontalWritingMode()) |
+ return portionRect.transposedRect(); |
+ return portionRect; |
+} |
+ |
} |