diff options
author | Simon Hausmann <simon.hausmann@nokia.com> | 2012-09-10 19:10:20 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2012-09-10 19:10:20 +0200 |
commit | 284837daa07b29d6a63a748544a90b1f5842ac5c (patch) | |
tree | ecd258180bde91fe741e0cfd2638beb3c6da7e8e /Source/WebCore/rendering/RenderMultiColumnSet.cpp | |
parent | 2e2ba8ff45915f40ed3e014101269c175f2a89a0 (diff) | |
download | qtwebkit-284837daa07b29d6a63a748544a90b1f5842ac5c.tar.gz |
Imported WebKit commit 68645295d2e3e09af2c942f092556f06aa5f8b0d (http://svn.webkit.org/repository/webkit/trunk@128073)
New snapshot
Diffstat (limited to 'Source/WebCore/rendering/RenderMultiColumnSet.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderMultiColumnSet.cpp | 225 |
1 files changed, 211 insertions, 14 deletions
diff --git a/Source/WebCore/rendering/RenderMultiColumnSet.cpp b/Source/WebCore/rendering/RenderMultiColumnSet.cpp index 3ac55cc9e..369011980 100644 --- a/Source/WebCore/rendering/RenderMultiColumnSet.cpp +++ b/Source/WebCore/rendering/RenderMultiColumnSet.cpp @@ -24,21 +24,33 @@ */ #include "config.h" +#include "RenderMultiColumnSet.h" + +#include "HitTestResult.h" #include "PaintInfo.h" #include "RenderMultiColumnFlowThread.h" -#include "RenderMultiColumnSet.h" #include "RenderMultiColumnBlock.h" +using std::min; +using std::max; + namespace WebCore { RenderMultiColumnSet::RenderMultiColumnSet(Node* node, RenderFlowThread* flowThread) : RenderRegionSet(node, flowThread) - , m_columnCount(1) - , m_columnWidth(ZERO_LAYOUT_UNIT) - , m_columnHeight(ZERO_LAYOUT_UNIT) + , m_computedColumnCount(1) + , m_computedColumnWidth(ZERO_LAYOUT_UNIT) + , m_computedColumnHeight(ZERO_LAYOUT_UNIT) { } +LayoutUnit RenderMultiColumnSet::pageLogicalTopForOffset(LayoutUnit offset) const +{ + LayoutUnit portionLogicalTop = (isHorizontalWritingMode() ? flowThreadPortionRect().y() : flowThreadPortionRect().x()); + unsigned columnIndex = (offset - portionLogicalTop) / computedColumnHeight(); + return portionLogicalTop + columnIndex * computedColumnHeight(); +} + void RenderMultiColumnSet::computeLogicalWidth() { // Our logical width starts off matching the column block itself. @@ -49,17 +61,17 @@ void RenderMultiColumnSet::computeLogicalWidth() setLogicalWidth(parentBox()->contentLogicalWidth()); RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent()); - setColumnWidthAndCount(parentBlock->columnWidth(), parentBlock->columnCount()); // FIXME: This will eventually vary if we are contained inside regions. + setComputedColumnWidthAndCount(parentBlock->columnWidth(), parentBlock->columnCount()); // FIXME: This will eventually vary if we are contained inside regions. } void RenderMultiColumnSet::computeLogicalHeight() { // Make sure our column height is up to date. RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent()); - setColumnHeight(parentBlock->columnHeight()); // FIXME: Once we make more than one column set, this will become variable. + setComputedColumnHeight(parentBlock->columnHeight()); // 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(columnHeight()); + setLogicalHeight(computedColumnHeight()); } LayoutUnit RenderMultiColumnSet::columnGap() const @@ -69,10 +81,20 @@ LayoutUnit RenderMultiColumnSet::columnGap() const return static_cast<int>(style()->columnGap()); } +unsigned RenderMultiColumnSet::columnCount() const +{ + if (!computedColumnHeight()) + return 0; + + // Our region 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()); +} + LayoutRect RenderMultiColumnSet::columnRectAt(unsigned index) const { - LayoutUnit colLogicalWidth = columnWidth(); - LayoutUnit colLogicalHeight = columnHeight(); + LayoutUnit colLogicalWidth = computedColumnWidth(); + LayoutUnit colLogicalHeight = computedColumnHeight(); LayoutUnit colLogicalTop = borderBefore() + paddingBefore(); LayoutUnit colLogicalLeft = borderAndPaddingLogicalLeft(); int colGap = columnGap(); @@ -86,6 +108,81 @@ LayoutRect RenderMultiColumnSet::columnRectAt(unsigned index) const return LayoutRect(colLogicalTop, colLogicalLeft, colLogicalHeight, colLogicalWidth); } +unsigned RenderMultiColumnSet::columnIndexAtOffset(LayoutUnit offset) 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. + if (offset < flowThreadLogicalTop) + return 0; + if (offset >= flowThreadLogicalBottom) + return columnCount() - 1; + + // Just divide by the column height to determine the correct column. + return static_cast<float>(offset - flowThreadLogicalTop) / computedColumnHeight(); +} + +LayoutRect RenderMultiColumnSet::flowThreadPortionRectAt(unsigned index) const +{ + LayoutRect portionRect = flowThreadPortionRect(); + if (isHorizontalWritingMode()) + portionRect = LayoutRect(portionRect.x(), portionRect.y() + index * computedColumnHeight(), portionRect.width(), computedColumnHeight()); + else + portionRect = LayoutRect(portionRect.x() + index * computedColumnHeight(), portionRect.y(), computedColumnHeight(), portionRect.height()); + return portionRect; +} + +LayoutRect RenderMultiColumnSet::flowThreadPortionOverflowRect(const LayoutRect& portionRect, unsigned index, unsigned colCount, int 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; + LayoutRect overflowRect(portionRect); + if (isHorizontalWritingMode()) { + if (isFirstColumn) { + // Shift to the logical left overflow of the flow thread to make sure it's all covered. + overflowRect.shiftXEdgeTo(min(flowThread()->visualOverflowRect().x(), portionRect.x())); + } else { + // Expand into half of the logical left column gap. + overflowRect.shiftXEdgeTo(portionRect.x() - colGap / 2); + } + if (isLastColumn) { + // Shift to the logical right overflow of the flow thread to ensure content can spill out of the column. + overflowRect.shiftMaxXEdgeTo(max(flowThread()->visualOverflowRect().maxX(), portionRect.maxX())); + } else { + // Expand into half of the logical right column gap. + overflowRect.shiftMaxXEdgeTo(portionRect.maxX() + colGap / 2); + } + } else { + if (isFirstColumn) { + // Shift to the logical left overflow of the flow thread to make sure it's all covered. + overflowRect.shiftYEdgeTo(min(flowThread()->visualOverflowRect().y(), portionRect.y())); + } else { + // Expand into half of the logical left column gap. + overflowRect.shiftYEdgeTo(portionRect.y() - colGap / 2); + } + if (isLastColumn) { + // Shift to the logical right overflow of the flow thread to ensure content can spill out of the column. + overflowRect.shiftMaxYEdgeTo(max(flowThread()->visualOverflowRect().maxY(), portionRect.maxY())); + } else { + // Expand into half of the logical right column gap. + overflowRect.shiftMaxYEdgeTo(portionRect.maxY() + colGap / 2); + } + } + return overflowRectForFlowThreadPortion(overflowRect, isFirstRegion() && isFirstColumn, isLastRegion() && isLastColumn); +} + void RenderMultiColumnSet::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { // FIXME: RenderRegions are replaced elements right now and so they only paint in the foreground phase. @@ -116,6 +213,8 @@ void RenderMultiColumnSet::paintColumnRules(PaintInfo& paintInfo, const LayoutPo return; unsigned colCount = columnCount(); + if (colCount <= 1) + return; bool antialias = shouldAntialiasLines(paintInfo.context); @@ -123,7 +222,7 @@ void RenderMultiColumnSet::paintColumnRules(PaintInfo& paintInfo, const LayoutPo LayoutUnit currLogicalLeftOffset = leftToRight ? ZERO_LAYOUT_UNIT : contentLogicalWidth(); LayoutUnit ruleAdd = borderAndPaddingLogicalLeft(); LayoutUnit ruleLogicalLeft = leftToRight ? ZERO_LAYOUT_UNIT : contentLogicalWidth(); - LayoutUnit inlineDirectionSize = columnWidth(); + LayoutUnit inlineDirectionSize = computedColumnWidth(); BoxSide boxSide = isHorizontalWritingMode() ? leftToRight ? BSLeft : BSRight : leftToRight ? BSTop : BSBottom; @@ -152,14 +251,112 @@ void RenderMultiColumnSet::paintColumnRules(PaintInfo& paintInfo, const LayoutPo } } -void RenderMultiColumnSet::paintColumnContents(PaintInfo& /* paintInfo */, const LayoutPoint& /* paintOffset */) +void RenderMultiColumnSet::paintColumnContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { // For each rectangle, set it as the region rectangle and then let flow thread painting do the rest. - // We make multiple calls to paintIntoRegion, changing the rectangles each time. - if (!columnCount()) + // We make multiple calls to paintFlowThreadPortionInRegion, changing the rectangles each time. + unsigned colCount = columnCount(); + if (!colCount) return; + + LayoutUnit colGap = columnGap(); + for (unsigned i = 0; i < colCount; i++) { + // First we get the column rect, which is in our local coordinate space, and we make it physical and apply + // the paint offset to it. That gives us the physical location that we want to paint the column at. + LayoutRect colRect = columnRectAt(i); + flipForWritingMode(colRect); + colRect.moveBy(paintOffset); + + // Next we 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); + + // Do the paint with the computed rects. + flowThread()->paintFlowThreadPortionInRegion(paintInfo, this, flowThreadPortion, flowThreadOverflowPortion, colRect.location()); + } +} + +bool RenderMultiColumnSet::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action) +{ + LayoutPoint adjustedLocation = accumulatedOffset + location(); + + // Check our bounds next. For this purpose always assume that we can only be hit in the + // foreground phase (which is true for replaced elements like images). + // FIXME: Once we support overflow, we need to intersect with that and not with the bounds rect. + LayoutRect boundsRect = borderBoxRectInRegion(locationInContainer.region()); + boundsRect.moveBy(adjustedLocation); + if (!visibleToHitTesting() || action != HitTestForeground || !locationInContainer.intersects(boundsRect)) + return false; + + // The point is in one specific column. Since columns can't overlap, we don't ever have to test + // multiple columns. Put the + + // FIXME: It would be nice to jump right to the specific column by just doing math on the point. Since columns + // can't overlap, we shouldn't have to walk every column like this. The old column code walked all the columns, though, + // so this is no worse. We'd have to watch out for rect-based hit testing, though, which actually could overlap + // multiple columns. + LayoutUnit colGap = columnGap(); + unsigned colCount = columnCount(); + for (unsigned i = 0; i < colCount; i++) { + // First we get the column rect, which is in our local coordinate space, and we make it physical and apply + // the hit test offset to it. That gives us the physical location that we want to paint the column at. + LayoutRect colRect = columnRectAt(i); + flipForWritingMode(colRect); + colRect.moveBy(adjustedLocation); + + // Next we get the portion of the flow thread that corresponds to this column. + LayoutRect flowThreadPortion = flowThreadPortionRectAt(i); - // FIXME: Implement. + // Now get the overflow rect that corresponds to the column. + LayoutRect flowThreadOverflowPortion = flowThreadPortionOverflowRect(flowThreadPortion, i, colCount, colGap); + + // Do the hit test with the computed rects. + if (flowThread()->hitTestFlowThreadPortionInRegion(this, flowThreadPortion, flowThreadOverflowPortion, request, result, locationInContainer, colRect.location())) + return true; + } + + updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation)); + return !result.addNodeToRectBasedTestResult(node(), request, locationInContainer, boundsRect); +} + +void RenderMultiColumnSet::repaintFlowThreadContent(const LayoutRect& repaintRect, bool immediate) const +{ + // Figure out the start and end columns and only check within that range so that we don't walk the + // entire column set. Put the repaint rect into flow thread coordinates by flipping it first. + LayoutRect flowThreadRepaintRect(repaintRect); + flowThread()->flipForWritingMode(flowThreadRepaintRect); + + // Now we can compare this rect with the flow thread portions owned by each column. First let's + // just see if the repaint rect intersects our flow thread portion at all. + LayoutRect clippedRect(flowThreadRepaintRect); + 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 repainting. + LayoutUnit repaintLogicalTop = isHorizontalWritingMode() ? flowThreadRepaintRect.y() : flowThreadRepaintRect.x(); + LayoutUnit repaintLogicalBottom = (isHorizontalWritingMode() ? flowThreadRepaintRect.maxY() : flowThreadRepaintRect.maxX()) - 1; + + unsigned startColumn = columnIndexAtOffset(repaintLogicalTop); + unsigned endColumn = columnIndexAtOffset(repaintLogicalBottom); + + LayoutUnit colGap = columnGap(); + unsigned colCount = columnCount(); + for (unsigned i = startColumn; i <= endColumn; i++) { + LayoutRect colRect = columnRectAt(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); + + // Do a repaint for this specific column. + repaintFlowThreadContentRectangle(repaintRect, immediate, flowThreadPortion, flowThreadOverflowPortion, colRect.location()); + } } const char* RenderMultiColumnSet::renderName() const |