Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1174)

Unified Diff: Source/core/layout/MultiColumnFragmentainerGroup.cpp

Issue 883293004: [New Multicolumn] Preparatory work for nested multicol support. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Place the new files in ../layout/ , since that's where they'll end up soon anyway. Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: Source/core/layout/MultiColumnFragmentainerGroup.cpp
diff --git a/Source/core/layout/MultiColumnFragmentainerGroup.cpp b/Source/core/layout/MultiColumnFragmentainerGroup.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e0acffd15b4621588225404b6a965b598f2ff456
--- /dev/null
+++ b/Source/core/layout/MultiColumnFragmentainerGroup.cpp
@@ -0,0 +1,509 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "core/layout/MultiColumnFragmentainerGroup.h"
+
+#include "core/rendering/RenderMultiColumnSet.h"
+
+namespace blink {
+
+MultiColumnFragmentainerGroup::MultiColumnFragmentainerGroup(RenderMultiColumnSet& columnSet)
+ : m_columnSet(columnSet)
+{
+}
+
+bool MultiColumnFragmentainerGroup::isLastGroup() const
+{
+ return &m_columnSet.lastFragmentainerGroup() == this;
+}
+
+LayoutSize MultiColumnFragmentainerGroup::offsetFromColumnSet() const
+{
+ LayoutSize offset(LayoutUnit(), logicalTop());
+ if (!m_columnSet.flowThread()->isHorizontalWritingMode())
+ return offset.transposedSize();
+ return offset;
+}
+
+bool MultiColumnFragmentainerGroup::heightIsAuto() const
+{
+ // Only the last row may have auto height, and thus be balanced. There are no good reasons to
+ // balance the preceding rows, and that could potentially lead to an insane number of layout
+ // passes as well.
Julien - ping for review 2015/02/11 07:25:29 Maybe there is some ASSERT we could land to avoid
mstensho (USE GERRIT) 2015/02/11 09:49:54 This very code already makes sure that we only bal
+ return isLastGroup() && m_columnSet.heightIsAuto();
+}
+
+void MultiColumnFragmentainerGroup::resetColumnHeight()
+{
+ // Nuke previously stored minimum column height. Contents may have changed for all we know.
+ m_minimumColumnHeight = 0;
+
+ m_maxColumnHeight = calculateMaxColumnHeight();
+
+ LayoutUnit oldColumnHeight = m_columnHeight;
+
+ if (heightIsAuto())
+ m_columnHeight = LayoutUnit();
+ else
+ setAndConstrainColumnHeight(heightAdjustedForRowOffset(m_columnSet.multiColumnFlowThread()->columnHeightAvailable()));
+
+ if (m_columnHeight != oldColumnHeight)
+ m_columnSet.setChildNeedsLayout(MarkOnlyThis);
+
+ // 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 MultiColumnFragmentainerGroup::addContentRun(LayoutUnit endOffsetInFlowThread)
+{
+ if (!m_contentRuns.isEmpty() && endOffsetInFlowThread <= 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_columnSet.usedColumnCount())
+ m_contentRuns.append(ContentRun(endOffsetInFlowThread));
+}
+
+void MultiColumnFragmentainerGroup::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;
+}
+
+bool MultiColumnFragmentainerGroup::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.
+}
+
+void MultiColumnFragmentainerGroup::expandToEncompassFlowThreadOverflow()
+{
+ ASSERT(isLastGroup());
+ // 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.
+ RenderMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread();
+ LayoutRect layoutRect = flowThread->layoutOverflowRect();
+ m_logicalBottomInFlowThread = flowThread->isHorizontalWritingMode() ? layoutRect.maxY() : layoutRect.maxX();
+}
+
+LayoutSize MultiColumnFragmentainerGroup::flowThreadTranslationAtOffset(LayoutUnit offsetInFlowThread) const
+{
+ unsigned columnIndex = columnIndexAtOffset(offsetInFlowThread);
+ LayoutRect portionRect(flowThreadPortionRectAt(columnIndex));
+ m_columnSet.flipForWritingMode(portionRect);
+ LayoutRect columnRect(columnRectAt(columnIndex));
+ m_columnSet.flipForWritingMode(columnRect);
+ return columnRect.location() - portionRect.location();
+}
+
+LayoutUnit MultiColumnFragmentainerGroup::columnLogicalTopForOffset(LayoutUnit offsetInFlowThread) const
+{
+ unsigned columnIndex = columnIndexAtOffset(offsetInFlowThread, AssumeNewColumns);
+ return m_logicalTopInFlowThread + columnIndex * m_columnHeight;
+}
+
+void MultiColumnFragmentainerGroup::collectLayerFragments(LayerFragments& fragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect) const
+{
+ // |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.
+
+ RenderMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread();
+ bool isHorizontalWritingMode = m_columnSet.isHorizontalWritingMode();
+
+ // 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(m_columnSet.RenderRegion::flowThreadPortionOverflowRect()); // FIXME: clean up this mess.
+ 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 row.
+ unsigned startColumn = columnIndexAtOffset(layerLogicalTop);
+ unsigned endColumn = columnIndexAtOffset(layerLogicalBottom);
+
+ LayoutUnit colLogicalWidth = m_columnSet.pageLogicalWidth();
+ LayoutUnit colGap = m_columnSet.columnGap();
+ unsigned colCount = actualColumnCount();
+
+ bool progressionIsInline = flowThread->progressionIsInline();
+ bool leftToRight = m_columnSet.style()->isLeftToRightDirection();
+
+ LayoutUnit initialBlockOffset = m_columnSet.logicalTop() + 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(m_columnSet.style()->writingMode()))
+ blockOffset = -blockOffset;
+ translationOffset.setY(blockOffset);
+ if (!isHorizontalWritingMode)
+ translationOffset = translationOffset.transposedPoint();
+
+ // 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);
+ }
+}
+
+LayoutRect MultiColumnFragmentainerGroup::calculateOverflow() const
+{
+ unsigned columnCount = actualColumnCount();
+ if (!columnCount)
+ return LayoutRect();
+ return columnRectAt(columnCount - 1);
+}
+
+unsigned MultiColumnFragmentainerGroup::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 (!m_columnHeight)
+ return 1;
+
+ // Our flow thread portion determines our column count. We have as many columns as needed to fit
+ // all the content.
+ LayoutUnit flowThreadPortionHeight = logicalHeightInFlowThread();
+ if (!flowThreadPortionHeight)
+ return 1;
+
+ unsigned count = ceil(flowThreadPortionHeight.toFloat() / m_columnHeight.toFloat());
+ ASSERT(count >= 1);
+ return count;
+}
+
+LayoutUnit MultiColumnFragmentainerGroup::heightAdjustedForRowOffset(LayoutUnit height) const
+{
+ // Adjust for the top offset within the content box of the multicol container (containing
+ // block), unless we're in 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 (m_columnSet.previousSiblingMultiColumnSet()) {
+ RenderBlockFlow* multicolBlock = m_columnSet.multiColumnBlockFlow();
+ LayoutUnit contentLogicalTop = m_columnSet.logicalTop() - multicolBlock->borderAndPaddingBefore();
+ height -= contentLogicalTop;
+ }
+ height -= logicalTop();
+ return max(height, LayoutUnit(1)); // Let's avoid zero height, as that would probably cause an infinite amount of columns to be created.
+}
+
+LayoutUnit MultiColumnFragmentainerGroup::calculateMaxColumnHeight() const
+{
+ RenderBlockFlow* multicolBlock = m_columnSet.multiColumnBlockFlow();
+ LayoutStyle* multicolStyle = multicolBlock->style();
+ LayoutUnit availableHeight = m_columnSet.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 heightAdjustedForRowOffset(maxColumnHeight);
+}
+
+void MultiColumnFragmentainerGroup::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 MultiColumnFragmentainerGroup::findRunWithTallestColumns() const
+{
+ unsigned indexWithLargestHeight = 0;
+ LayoutUnit largestHeight;
+ LayoutUnit previousOffset = m_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 MultiColumnFragmentainerGroup::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(m_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 < m_columnSet.usedColumnCount()) {
+ unsigned index = findRunWithTallestColumns();
+ m_contentRuns[index].assumeAnotherImplicitBreak();
+ columnCount++;
+ }
+}
+
+LayoutUnit MultiColumnFragmentainerGroup::calculateColumnHeight(BalancedColumnHeightCalculation 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() : m_logicalTopInFlowThread;
+ return std::max<LayoutUnit>(m_contentRuns[index].columnLogicalHeight(startOffset), m_minimumColumnHeight);
+ }
+
+ if (actualColumnCount() <= m_columnSet.usedColumnCount()) {
+ // With the current column height, the content fits without creating overflowing columns. We're done.
+ return m_columnHeight;
+ }
+
+ if (m_contentRuns.size() >= m_columnSet.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;
+}
+
+LayoutRect MultiColumnFragmentainerGroup::columnRectAt(unsigned columnIndex) const
+{
+ LayoutUnit columnLogicalWidth = m_columnSet.pageLogicalWidth();
+ LayoutUnit columnLogicalHeight = m_columnHeight;
+ LayoutUnit columnLogicalTop;
+ LayoutUnit columnLogicalLeft;
+ LayoutUnit columnGap = m_columnSet.columnGap();
+
+ if (m_columnSet.multiColumnFlowThread()->progressionIsInline()) {
+ if (m_columnSet.style()->isLeftToRightDirection())
+ columnLogicalLeft += columnIndex * (columnLogicalWidth + columnGap);
+ else
+ columnLogicalLeft += m_columnSet.contentLogicalWidth() - columnLogicalWidth - columnIndex * (columnLogicalWidth + columnGap);
+ } else {
+ columnLogicalTop += columnIndex * (columnLogicalHeight + columnGap);
+ }
+
+ LayoutRect columnRect(columnLogicalLeft, columnLogicalTop, columnLogicalWidth, columnLogicalHeight);
+ if (!m_columnSet.isHorizontalWritingMode())
+ return columnRect.transposedRect();
+ return columnRect;
+}
+
+LayoutRect MultiColumnFragmentainerGroup::flowThreadPortionRectAt(unsigned columnIndex) const
+{
+ LayoutUnit logicalTop = m_logicalTopInFlowThread + columnIndex * m_columnHeight;
+ if (m_columnSet.isHorizontalWritingMode())
+ return LayoutRect(LayoutUnit(), logicalTop, m_columnSet.pageLogicalWidth(), m_columnHeight);
+ return LayoutRect(logicalTop, LayoutUnit(), m_columnHeight, m_columnSet.pageLogicalWidth());
+}
+
+LayoutRect MultiColumnFragmentainerGroup::flowThreadPortionOverflowRect(const LayoutRect& portionRect, unsigned columnIndex, unsigned columnCount, LayoutUnit columnGap) 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 = !columnIndex;
+ bool isLastColumn = columnIndex == columnCount - 1;
+ bool isLTR = m_columnSet.style()->isLeftToRightDirection();
+ bool isLeftmostColumn = isLTR ? isFirstColumn : isLastColumn;
+ bool isRightmostColumn = isLTR ? 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 = m_columnSet.overflowRectForFlowThreadPortion(portionRect, isFirstColumn && m_columnSet.isFirstRegion(), isLastColumn && m_columnSet.isLastRegion());
+
+ // Avoid overflowing into neighboring columns, by clipping in the middle of adjacent column
+ // gaps. Also make sure that we avoid rounding errors.
+ if (m_columnSet.isHorizontalWritingMode()) {
+ if (!isLeftmostColumn)
+ overflowRect.shiftXEdgeTo(portionRect.x() - columnGap / 2);
+ if (!isRightmostColumn)
+ overflowRect.shiftMaxXEdgeTo(portionRect.maxX() + columnGap - columnGap / 2);
+ } else {
+ if (!isLeftmostColumn)
+ overflowRect.shiftYEdgeTo(portionRect.y() - columnGap / 2);
+ if (!isRightmostColumn)
+ overflowRect.shiftMaxYEdgeTo(portionRect.maxY() + columnGap - columnGap / 2);
+ }
+ return overflowRect;
+}
+
+unsigned MultiColumnFragmentainerGroup::columnIndexAtOffset(LayoutUnit offsetInFlowThread, ColumnIndexCalculationMode mode) const
+{
+ // Handle the offset being out of range.
+ if (offsetInFlowThread < m_logicalTopInFlowThread)
+ 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) {
+ if (offsetInFlowThread >= m_logicalBottomInFlowThread)
+ return actualColumnCount() - 1;
+ }
+
+ if (m_columnHeight)
+ return (offsetInFlowThread - m_logicalTopInFlowThread).toFloat() / m_columnHeight.toFloat();
+ return 0;
+}
+
+MultiColumnFragmentainerGroupList::MultiColumnFragmentainerGroupList(RenderMultiColumnSet& columnSet)
+ : m_columnSet(columnSet)
+{
+ append(MultiColumnFragmentainerGroup(m_columnSet));
+}
+
+MultiColumnFragmentainerGroup& MultiColumnFragmentainerGroupList::addExtraGroup()
+{
+ append(MultiColumnFragmentainerGroup(m_columnSet));
+ return last();
+}
+
+void MultiColumnFragmentainerGroupList::deleteExtraGroups()
+{
+ shrink(1);
+}
+
+} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698