LayoutRect LayoutMultiColumnSet::flowThreadPortionRect() const { LayoutRect portionRect(LayoutUnit(), logicalTopInFlowThread(), pageLogicalWidth(), logicalHeightInFlowThread()); if (!isHorizontalWritingMode()) return portionRect.transposedRect(); return portionRect; }
void RenderRegion::layoutBlock(bool relayoutChildren, LayoutUnit) { StackStats::LayoutCheckPoint layoutCheckPoint; RenderBlock::layoutBlock(relayoutChildren); if (isValid()) { LayoutRect oldRegionRect(flowThreadPortionRect()); if (!isHorizontalWritingMode()) oldRegionRect = oldRegionRect.transposedRect(); if (hasAutoLogicalHeight() && !m_flowThread->inConstrainedLayoutPhase()) { m_flowThread->invalidateRegions(); clearOverrideLogicalContentHeight(); return; } if (!isRenderRegionSet() && (oldRegionRect.width() != pageLogicalWidth() || oldRegionRect.height() != pageLogicalHeight())) // This can happen even if we are in the inConstrainedLayoutPhase and it will trigger a pathological layout of the flow thread. m_flowThread->invalidateRegions(); } // FIXME: We need to find a way to set up overflow properly. Our flow thread hasn't gotten a layout // yet, so we can't look to it for correct information. It's possible we could wait until after the RenderFlowThread // gets a layout, and then try to propagate overflow information back to the region, and then mark for a second layout. // That second layout would then be able to use the information from the RenderFlowThread to set up overflow. // // The big problem though is that overflow needs to be region-specific. We can't simply use the RenderFlowThread's global // overflow values, since then we'd always think any narrow region had huge overflow (all the way to the width of the // RenderFlowThread itself). // // We'll need to expand RenderBoxRegionInfo to also hold left and right overflow values. }
void RenderNamedFlowFragment::layoutBlock(bool relayoutChildren, LayoutUnit) { StackStats::LayoutCheckPoint layoutCheckPoint; RenderRegion::layoutBlock(relayoutChildren); if (isValid()) { LayoutRect oldRegionRect(flowThreadPortionRect()); if (!isHorizontalWritingMode()) oldRegionRect = oldRegionRect.transposedRect(); if (m_flowThread->inOverflowLayoutPhase() || m_flowThread->inFinalLayoutPhase()) { computeOverflowFromFlowThread(); updateOversetState(); } if (hasAutoLogicalHeight() && m_flowThread->inMeasureContentLayoutPhase()) { m_flowThread->invalidateRegions(); clearComputedAutoHeight(); return; } if ((oldRegionRect.width() != pageLogicalWidth() || oldRegionRect.height() != pageLogicalHeight()) && !m_flowThread->inFinalLayoutPhase()) // This can happen even if we are in the inConstrainedLayoutPhase and it will trigger a pathological layout of the flow thread. m_flowThread->invalidateRegions(); } }
void RenderMultiColumnSet::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (paintInfo.context->paintingDisabled()) return; RenderStyle* blockStyle = multiColumnBlockFlow()->style(); const Color& ruleColor = resolveColor(blockStyle, CSSPropertyWebkitColumnRuleColor); bool ruleTransparent = blockStyle->columnRuleIsTransparent(); EBorderStyle ruleStyle = blockStyle->columnRuleStyle(); LayoutUnit ruleThickness = blockStyle->columnRuleWidth(); LayoutUnit colGap = columnGap(); bool renderRule = ruleStyle > BHIDDEN && !ruleTransparent; if (!renderRule) return; unsigned colCount = actualColumnCount(); if (colCount <= 1) return; bool antialias = shouldAntialiasLines(paintInfo.context); bool leftToRight = style()->isLeftToRightDirection(); LayoutUnit currLogicalLeftOffset = leftToRight ? LayoutUnit() : contentLogicalWidth(); LayoutUnit ruleAdd = borderAndPaddingLogicalLeft(); LayoutUnit ruleLogicalLeft = leftToRight ? LayoutUnit() : contentLogicalWidth(); LayoutUnit inlineDirectionSize = pageLogicalWidth(); BoxSide boxSide = isHorizontalWritingMode() ? leftToRight ? BSLeft : BSRight : leftToRight ? BSTop : BSBottom; for (unsigned i = 0; i < colCount; i++) { // Move to the next position. if (leftToRight) { ruleLogicalLeft += inlineDirectionSize + colGap / 2; currLogicalLeftOffset += inlineDirectionSize + colGap; } else { ruleLogicalLeft -= (inlineDirectionSize + colGap / 2); currLogicalLeftOffset -= (inlineDirectionSize + colGap); } // Now paint the column rule. if (i < colCount - 1) { LayoutUnit ruleLeft = isHorizontalWritingMode() ? paintOffset.x() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd : paintOffset.x() + borderLeft() + paddingLeft(); LayoutUnit ruleRight = isHorizontalWritingMode() ? ruleLeft + ruleThickness : ruleLeft + contentWidth(); LayoutUnit ruleTop = isHorizontalWritingMode() ? paintOffset.y() + borderTop() + paddingTop() : paintOffset.y() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd; LayoutUnit ruleBottom = isHorizontalWritingMode() ? ruleTop + contentHeight() : ruleTop + ruleThickness; IntRect pixelSnappedRuleRect = pixelSnappedIntRectFromEdges(ruleLeft, ruleTop, ruleRight, ruleBottom); drawLineForBoxSide(paintInfo.context, pixelSnappedRuleRect.x(), pixelSnappedRuleRect.y(), pixelSnappedRuleRect.maxX(), pixelSnappedRuleRect.maxY(), boxSide, ruleColor, ruleStyle, 0, 0, antialias); } ruleLogicalLeft = currLogicalLeftOffset; } }
void RenderNamedFlowFragment::invalidateRegionIfNeeded() { if (!isValid()) return; LayoutRect oldRegionRect(flowThreadPortionRect()); if (!isHorizontalWritingMode()) oldRegionRect = oldRegionRect.transposedRect(); if ((oldRegionRect.width() != pageLogicalWidth() || oldRegionRect.height() != pageLogicalHeight()) && !m_flowThread->inFinalLayoutPhase()) { // This can happen even if we are in the inConstrainedLayoutPhase and it will trigger a pathological layout of the flow thread. m_flowThread->invalidateRegions(); } }
LayoutRect RenderMultiColumnSet::columnRectAt(unsigned index) const { LayoutUnit colLogicalWidth = pageLogicalWidth(); LayoutUnit colLogicalHeight = pageLogicalHeight(); LayoutUnit colLogicalTop = borderBefore() + paddingBefore(); LayoutUnit colLogicalLeft = borderAndPaddingLogicalLeft(); LayoutUnit colGap = columnGap(); if (style()->isLeftToRightDirection()) colLogicalLeft += index * (colLogicalWidth + colGap); else colLogicalLeft += contentLogicalWidth() - colLogicalWidth - index * (colLogicalWidth + colGap); if (isHorizontalWritingMode()) return LayoutRect(colLogicalLeft, colLogicalTop, colLogicalWidth, colLogicalHeight); return LayoutRect(colLogicalTop, colLogicalLeft, colLogicalHeight, colLogicalWidth); }
void RenderMultiColumnSet::collectLayerFragments(LayerFragments& fragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect) { // The two rectangles passed to this method are physical, except that we pretend that there's // only one long column (that's how a flow thread works). // // Then there's the output from this method - the stuff we put into the list of fragments. The // fragment.paginationOffset point is the actual physical 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 the same coordinate system as the two rectangles passed // to this method (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. // 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(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 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 set. unsigned startColumn = columnIndexAtOffset(layerLogicalTop); unsigned endColumn = columnIndexAtOffset(layerLogicalBottom); LayoutUnit colLogicalWidth = pageLogicalWidth(); LayoutUnit colGap = columnGap(); unsigned colCount = actualColumnCount(); 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 = i * (colLogicalWidth + colGap); if (!style()->isLeftToRightDirection()) inlineOffset = -inlineOffset; translationOffset.setX(inlineOffset); LayoutUnit blockOffset = isHorizontalWritingMode() ? -flowThreadPortion.y() : -flowThreadPortion.x(); if (isFlippedBlocksWritingMode(style()->writingMode())) blockOffset = -blockOffset; translationOffset.setY(blockOffset); if (!isHorizontalWritingMode()) translationOffset = translationOffset.transposedPoint(); // FIXME: The translation needs to include the multicolumn set's content offset within the // multicolumn block as well. This won't be an issue until we start creating multiple multicolumn sets. // 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); } }