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();
}
예제 #6
0
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();
}
예제 #8
0
void RenderMultiColumnSet::addOverflowFromChildren()
{
    unsigned colCount = actualColumnCount();
    if (!colCount)
        return;

    LayoutRect lastRect = columnRectAt(colCount - 1);
    addLayoutOverflow(lastRect);
    if (!hasOverflowClip())
        addVisualOverflow(lastRect);
}
예제 #9
0
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);
}
예제 #10
0
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;
}