Chromium Code Reviews| Index: Source/core/rendering/RenderMultiColumnSet.cpp |
| diff --git a/Source/core/rendering/RenderMultiColumnSet.cpp b/Source/core/rendering/RenderMultiColumnSet.cpp |
| index 4ad1c9d855d1defeb150ba44acaf9b931143bee7..521e1e32fbd9c69c28a0955bcad859a5f43c4d23 100644 |
| --- a/Source/core/rendering/RenderMultiColumnSet.cpp |
| +++ b/Source/core/rendering/RenderMultiColumnSet.cpp |
| @@ -27,16 +27,14 @@ |
| #include "core/rendering/RenderMultiColumnSet.h" |
| #include "core/paint/MultiColumnSetPainter.h" |
| +#include "core/rendering/MultiColumnRow.h" |
| #include "core/rendering/RenderMultiColumnFlowThread.h" |
| namespace blink { |
| RenderMultiColumnSet::RenderMultiColumnSet(RenderFlowThread* flowThread) |
| : RenderRegion(0, flowThread) |
| - , m_columnHeight(0) |
| - , m_maxColumnHeight(RenderFlowThread::maxLogicalHeight()) |
| - , m_minSpaceShortage(RenderFlowThread::maxLogicalHeight()) |
| - , m_minimumColumnHeight(0) |
| + , m_columnRow(*this) |
| { |
| } |
| @@ -49,6 +47,13 @@ RenderMultiColumnSet* RenderMultiColumnSet::createAnonymous(RenderFlowThread* fl |
| return renderer; |
| } |
| +LayoutUnit RenderMultiColumnSet::pageLogicalHeight() const |
| +{ |
| + // FIXME: pageLogicalHeight() needs to take a flow thread offset parameter, so that we can |
| + // locate the right row. |
| + return firstColumnRow()->logicalHeight(); |
| +} |
| + |
| RenderMultiColumnSet* RenderMultiColumnSet::nextSiblingMultiColumnSet() const |
| { |
| for (RenderObject* sibling = nextSibling(); sibling; sibling = sibling->nextSibling()) { |
| @@ -67,24 +72,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 firstColumnRow()->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 lastColumnRow()->logicalBottomInFlowThread(); |
| } |
| bool RenderMultiColumnSet::heightIsAuto() const |
| @@ -105,245 +100,80 @@ 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 MultiColumnRow* row = columnRowAtFlowThreadOffset(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(); |
| + columnRowAtFlowThreadOffset(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 columnRowAtFlowThreadOffset(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)); |
| + columnRowAtFlowThreadOffset(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 (MultiColumnRow* row = firstColumnRow(); row; row = row->nextRow()) |
| + changed = row->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; |
| + columnRowAtFlowThreadOffset(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())); |
| + // FIXME: don't we really have to just delete all rows but the first one here? |
| + for (MultiColumnRow* row = firstColumnRow(); row; row = row->nextRow()) |
| + row->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. |
| + firstColumnRow()->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. |
| + lastColumnRow()->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. |
| + lastColumnRow()->expandToEncompassFlowThreadOverflow(); |
| } |
| void RenderMultiColumnSet::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const |
| { |
| - computedValues.m_extent = m_columnHeight; |
| + LayoutUnit logicalHeight; |
| + for (const MultiColumnRow* row = firstColumnRow(); row; row = row->nextRow()) |
| + logicalHeight += row->logicalHeight(); |
| + computedValues.m_extent = logicalHeight; |
| computedValues.m_position = logicalTop; |
| } |
| -LayoutUnit RenderMultiColumnSet::calculateMaxColumnHeight() const |
| -{ |
| - RenderBlockFlow* multicolBlock = multiColumnBlockFlow(); |
| - RenderStyle* multicolStyle = multicolBlock->style(); |
| - 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,238 +184,45 @@ 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; |
| + // 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 firstColumnRow()->actualColumnCount(); |
| } |
| -LayoutRect RenderMultiColumnSet::columnRectAt(unsigned index) const |
| +void RenderMultiColumnSet::paintObject(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) |
| { |
| - 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); |
| + MultiColumnSetPainter(*this).paintObject(paintInfo, paintOffset); |
| } |
| -unsigned RenderMultiColumnSet::columnIndexAtOffset(LayoutUnit offset, ColumnIndexCalculationMode mode) const |
| +void RenderMultiColumnSet::collectLayerFragments(LayerFragments& fragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect) |
| { |
| - 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; |
| + for (MultiColumnRow* row = firstColumnRow(); row; row = row->nextRow()) |
|
Julien - ping for review
2015/01/29 17:37:47
Could we use C++11 for-loop here? (and probably fo
mstensho (USE GERRIT)
2015/02/02 17:57:24
Done.
I was planning to use a home-made linked li
|
| + row->collectLayerFragments(fragments, layerBoundingBox, dirtyRect); |
| } |
| -LayoutRect RenderMultiColumnSet::flowThreadPortionRectAt(unsigned index) const |
| +void RenderMultiColumnSet::addOverflowFromChildren() |
| { |
| - 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); |
| + LayoutRect overflowRect; |
| + for (MultiColumnRow* row = firstColumnRow(); row; row = row->nextRow()) { |
| + LayoutRect rect = row->calculateOverflow(); |
| + rect.move(row->offsetFromColumnSet()); |
| + overflowRect.unite(rect); |
| } |
| - return overflowRect; |
| -} |
| - |
| -void RenderMultiColumnSet::paintObject(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) |
| -{ |
| - MultiColumnSetPainter(*this).paintObject(paintInfo, paintOffset); |
| + addLayoutOverflow(overflowRect); |
| + if (!hasOverflowClip()) |
| + addVisualOverflow(overflowRect); |
| } |
| -void RenderMultiColumnSet::collectLayerFragments(LayerFragments& fragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect) |
| +MultiColumnRow* RenderMultiColumnSet::columnRowAtFlowThreadOffset(LayoutUnit) |
| { |
| - // |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 (RenderLayer-style) rectangle. |
| - flowThread->flipForWritingMode(flippedFlowThreadOverflowPortion); |
| - fragment.paginationClip = flippedFlowThreadOverflowPortion; |
| - fragments.append(fragment); |
| - } |
| + // FIXME: implement this, once we have support for multiple rows. |
| + return &m_columnRow; |
| } |
| -void RenderMultiColumnSet::addOverflowFromChildren() |
| +const MultiColumnRow* RenderMultiColumnSet::columnRowAtFlowThreadOffset(LayoutUnit) const |
| { |
| - unsigned colCount = actualColumnCount(); |
| - if (!colCount) |
| - return; |
| - |
| - LayoutRect lastRect = columnRectAt(colCount - 1); |
| - addLayoutOverflow(lastRect); |
| - if (!hasOverflowClip()) |
| - addVisualOverflow(lastRect); |
| + // FIXME: implement this, once we have support for multiple rows. |
| + return &m_columnRow; |
| } |
| const char* RenderMultiColumnSet::renderName() const |
| @@ -630,4 +267,12 @@ void RenderMultiColumnSet::detachRegion() |
| } |
| } |
| +LayoutRect RenderMultiColumnSet::flowThreadPortionRect() const |
| +{ |
| + LayoutRect portionRect(LayoutUnit(), logicalTopInFlowThread(), pageLogicalWidth(), logicalHeightInFlowThread()); |
| + if (!isHorizontalWritingMode()) |
| + return portionRect.transposedRect(); |
| + return portionRect; |
| +} |
| + |
| } |