LayoutRect MultiColumnFragmentainerGroup::calculateOverflow() const { // Note that we just return the bounding rectangle of the column boxes here. // We currently don't examine overflow caused by the actual content that ends // up in each column. LayoutRect overflowRect; if (unsigned columnCount = actualColumnCount()) { overflowRect = columnRectAt(0); if (columnCount > 1) overflowRect.uniteEvenIfEmpty(columnRectAt(columnCount - 1)); } return overflowRect; }
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 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()); } }
LayoutRect MultiColumnFragmentainerGroup::calculateOverflow() const { unsigned columnCount = actualColumnCount(); if (!columnCount) return LayoutRect(); return columnRectAt(columnCount - 1); }
LayoutPoint MultiColumnFragmentainerGroup::visualPointToFlowThreadPoint(const LayoutPoint& visualPoint) const { unsigned columnIndex = columnIndexAtVisualPoint(visualPoint); LayoutRect columnRect = columnRectAt(columnIndex); LayoutPoint localPoint(visualPoint); localPoint.moveBy(-columnRect.location()); // Before converting to a flow thread position, if the block direction coordinate is outside the // column, snap to the bounds of the column, and reset the inline direction coordinate to the // start position in the column. The effect of this is that if the block position is before the // column rectangle, we'll get to the beginning of this column, while if the block position is // after the column rectangle, we'll get to the beginning of the next column. if (!m_columnSet.isHorizontalWritingMode()) { LayoutUnit columnStart = m_columnSet.style()->isLeftToRightDirection() ? LayoutUnit() : columnRect.height(); if (localPoint.x() < 0) localPoint = LayoutPoint(LayoutUnit(), columnStart); else if (localPoint.x() > logicalHeight()) localPoint = LayoutPoint(logicalHeight(), columnStart); return LayoutPoint(localPoint.x() + logicalTopInFlowThreadAt(columnIndex), localPoint.y()); } LayoutUnit columnStart = m_columnSet.style()->isLeftToRightDirection() ? LayoutUnit() : columnRect.width(); if (localPoint.y() < 0) localPoint = LayoutPoint(columnStart, LayoutUnit()); else if (localPoint.y() > logicalHeight()) localPoint = LayoutPoint(columnStart, logicalHeight()); return LayoutPoint(localPoint.x(), localPoint.y() + logicalTopInFlowThreadAt(columnIndex)); }
LayoutSize MultiColumnFragmentainerGroup::flowThreadTranslationAtOffset(LayoutUnit offsetInFlowThread) const { LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread(); unsigned columnIndex = columnIndexAtOffset(offsetInFlowThread); LayoutRect portionRect(flowThreadPortionRectAt(columnIndex)); flowThread->flipForWritingMode(portionRect); LayoutRect columnRect(columnRectAt(columnIndex)); m_columnSet.flipForWritingMode(columnRect); LayoutSize translationRelativeToGroup = columnRect.location() - portionRect.location(); LayoutSize enclosingTranslation; if (LayoutMultiColumnFlowThread* enclosingFlowThread = flowThread->enclosingFlowThread()) { // Translation that would map points in the coordinate space of the outermost flow thread to // visual points in the first column in the first fragmentainer group (row) in our multicol // container. LayoutSize enclosingTranslationOrigin = enclosingFlowThread->flowThreadTranslationAtOffset(flowThread->blockOffsetInEnclosingFragmentationContext()); // Translation that would map points in the coordinate space of the outermost flow thread to // visual points in the first column in this fragmentainer group. enclosingTranslation = enclosingFlowThread->flowThreadTranslationAtOffset(blockOffsetInEnclosingFragmentationContext()); // What we ultimately return from this method is a translation that maps points in the // coordinate space of our flow thread to a visual point in a certain column in this // fragmentainer group. We had to go all the way up to the outermost flow thread, since this // fragmentainer group may be in a different outer column than the first outer column that // this multicol container lives in. It's the visual distance between the first // fragmentainer group and this fragmentainer group that we need to add to the translation. enclosingTranslation -= enclosingTranslationOrigin; } return enclosingTranslation + translationRelativeToGroup + offsetFromColumnSet() + m_columnSet.topLeftLocationOffset() - flowThread->topLeftLocationOffset(); }
LayoutSize RenderMultiColumnSet::flowThreadTranslationAtOffset(LayoutUnit blockOffset) const { unsigned columnIndex = columnIndexAtOffset(blockOffset); LayoutRect portionRect(flowThreadPortionRectAt(columnIndex)); flipForWritingMode(portionRect); LayoutRect columnRect(columnRectAt(columnIndex)); flipForWritingMode(columnRect); return contentBoxRect().location() + columnRect.location() - portionRect.location(); }
LayoutSize MultiColumnFragmentainerGroup::flowThreadTranslationAtOffset(LayoutUnit offsetInFlowThread) const { LayoutFlowThread* flowThread = m_columnSet.flowThread(); unsigned columnIndex = columnIndexAtOffset(offsetInFlowThread); LayoutRect portionRect(flowThreadPortionRectAt(columnIndex)); flowThread->flipForWritingMode(portionRect); LayoutRect columnRect(columnRectAt(columnIndex)); m_columnSet.flipForWritingMode(columnRect); LayoutSize translationRelativeToGroup = columnRect.location() - portionRect.location(); return translationRelativeToGroup + offsetFromColumnSet() + m_columnSet.topLeftLocationOffset() - flowThread->topLeftLocationOffset(); }
void RenderMultiColumnSet::addOverflowFromChildren() { unsigned colCount = actualColumnCount(); if (!colCount) return; LayoutRect lastRect = columnRectAt(colCount - 1); addLayoutOverflow(lastRect); if (!hasOverflowClip()) addVisualOverflow(lastRect); }
void RenderMultiColumnSet::addOverflowFromChildren() { // FIXME: Need to do much better here. unsigned colCount = columnCount(); if (!colCount) return; LayoutRect lastRect = columnRectAt(colCount - 1); addLayoutOverflow(lastRect); if (!hasOverflowClip()) addVisualOverflow(lastRect); }
LayoutUnit RenderMultiColumnSet::initialBlockOffsetForPainting() const { RenderBlockFlow* parentFlow = toRenderBlockFlow(parent()); bool progressionReversed = parentFlow->multiColumnFlowThread()->progressionIsReversed(); bool progressionIsInline = parentFlow->multiColumnFlowThread()->progressionIsInline(); LayoutUnit result = 0; if (!progressionIsInline && progressionReversed) { LayoutRect colRect = columnRectAt(0); result = isHorizontalWritingMode() ? colRect.y() : colRect.x(); if (style().isFlippedBlocksWritingMode()) result = -result; } return result; }
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); // 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()); } }
LayoutSize MultiColumnFragmentainerGroup::flowThreadTranslationAtOffset( LayoutUnit offsetInFlowThread, LayoutBox::PageBoundaryRule rule, CoordinateSpaceConversion mode) const { LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread(); // A column out of range doesn't have a flow thread portion, so we need to // clamp to make sure that we stay within the actual columns. This means that // content in the overflow area will be mapped to the last actual column, // instead of being mapped to an imaginary column further ahead. unsigned columnIndex = offsetInFlowThread >= logicalBottomInFlowThread() ? actualColumnCount() - 1 : columnIndexAtOffset(offsetInFlowThread, rule); LayoutRect portionRect(flowThreadPortionRectAt(columnIndex)); flowThread->flipForWritingMode(portionRect); portionRect.moveBy(flowThread->topLeftLocation()); LayoutRect columnRect(columnRectAt(columnIndex)); columnRect.move(offsetFromColumnSet()); m_columnSet.flipForWritingMode(columnRect); columnRect.moveBy(m_columnSet.topLeftLocation()); LayoutSize translationRelativeToFlowThread = columnRect.location() - portionRect.location(); if (mode == CoordinateSpaceConversion::Containing) return translationRelativeToFlowThread; LayoutSize enclosingTranslation; if (LayoutMultiColumnFlowThread* enclosingFlowThread = flowThread->enclosingFlowThread()) { const MultiColumnFragmentainerGroup& firstRow = flowThread->firstMultiColumnSet()->firstFragmentainerGroup(); // Translation that would map points in the coordinate space of the // outermost flow thread to visual points in the first column in the first // fragmentainer group (row) in our multicol container. LayoutSize enclosingTranslationOrigin = enclosingFlowThread->flowThreadTranslationAtOffset( firstRow.blockOffsetInEnclosingFragmentationContext(), LayoutBox::AssociateWithLatterPage, mode); // Translation that would map points in the coordinate space of the // outermost flow thread to visual points in the first column in this // fragmentainer group. enclosingTranslation = enclosingFlowThread->flowThreadTranslationAtOffset( blockOffsetInEnclosingFragmentationContext(), LayoutBox::AssociateWithLatterPage, mode); // What we ultimately return from this method is a translation that maps // points in the coordinate space of our flow thread to a visual point in a // certain column in this fragmentainer group. We had to go all the way up // to the outermost flow thread, since this fragmentainer group may be in a // different outer column than the first outer column that this multicol // container lives in. It's the visual distance between the first // fragmentainer group and this fragmentainer group that we need to add to // the translation. enclosingTranslation -= enclosingTranslationOrigin; } return enclosingTranslation + translationRelativeToFlowThread; }