summaryrefslogtreecommitdiff
path: root/Source/WebCore/rendering/RenderMultiColumnSet.cpp
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@nokia.com>2012-09-10 19:10:20 +0200
committerSimon Hausmann <simon.hausmann@nokia.com>2012-09-10 19:10:20 +0200
commit284837daa07b29d6a63a748544a90b1f5842ac5c (patch)
treeecd258180bde91fe741e0cfd2638beb3c6da7e8e /Source/WebCore/rendering/RenderMultiColumnSet.cpp
parent2e2ba8ff45915f40ed3e014101269c175f2a89a0 (diff)
downloadqtwebkit-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.cpp225
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