mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Now documents can have many element children, all created equal. R=esprehn@chromium.org, ojan@chromium.org Review URL: https://codereview.chromium.org/928393003
365 lines
14 KiB
C++
365 lines
14 KiB
C++
/*
|
|
* Copyright (C) 2013 Google Inc. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following disclaimer
|
|
* in the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* * Neither the name of Google Inc. nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "sky/engine/config.h"
|
|
#include "sky/engine/core/rendering/RenderBlockFlow.h"
|
|
|
|
#include "sky/engine/core/frame/FrameView.h"
|
|
#include "sky/engine/core/frame/LocalFrame.h"
|
|
#include "sky/engine/core/frame/Settings.h"
|
|
#include "sky/engine/core/rendering/BidiRun.h"
|
|
#include "sky/engine/core/rendering/HitTestLocation.h"
|
|
#include "sky/engine/core/rendering/RenderLayer.h"
|
|
#include "sky/engine/core/rendering/RenderText.h"
|
|
#include "sky/engine/core/rendering/RenderView.h"
|
|
#include "sky/engine/core/rendering/line/LineWidth.h"
|
|
#include "sky/engine/platform/text/BidiTextRun.h"
|
|
|
|
namespace blink {
|
|
|
|
RenderBlockFlow::RenderBlockFlow(ContainerNode* node)
|
|
: RenderBlock(node)
|
|
{
|
|
}
|
|
|
|
RenderBlockFlow::~RenderBlockFlow()
|
|
{
|
|
}
|
|
|
|
RenderBlockFlow* RenderBlockFlow::createAnonymous(Document* document)
|
|
{
|
|
RenderBlockFlow* renderer = new RenderBlockFlow(0);
|
|
renderer->setDocumentForAnonymous(document);
|
|
return renderer;
|
|
}
|
|
|
|
bool RenderBlockFlow::updateLogicalWidthAndColumnWidth()
|
|
{
|
|
return RenderBlock::updateLogicalWidthAndColumnWidth();
|
|
}
|
|
|
|
void RenderBlockFlow::layout()
|
|
{
|
|
ASSERT(needsLayout());
|
|
ASSERT(isInlineBlock() || !isInline());
|
|
|
|
if (simplifiedLayout())
|
|
return;
|
|
|
|
SubtreeLayoutScope layoutScope(*this);
|
|
|
|
layoutBlockFlow(layoutScope);
|
|
|
|
updateLayerTransformAfterLayout();
|
|
|
|
clearNeedsLayout();
|
|
}
|
|
|
|
inline void RenderBlockFlow::layoutBlockFlow(SubtreeLayoutScope& layoutScope)
|
|
{
|
|
LayoutUnit oldLeft = logicalLeft();
|
|
bool logicalWidthChanged = updateLogicalWidthAndColumnWidth();
|
|
bool relayoutChildren = logicalWidthChanged;
|
|
|
|
LayoutState state(*this, locationOffset(), logicalWidthChanged);
|
|
|
|
LayoutUnit beforeEdge = borderBefore() + paddingBefore();
|
|
LayoutUnit afterEdge = borderAfter() + paddingAfter();
|
|
LayoutUnit previousHeight = logicalHeight();
|
|
setLogicalHeight(beforeEdge);
|
|
|
|
layoutChildren(relayoutChildren, layoutScope, beforeEdge, afterEdge);
|
|
|
|
LayoutUnit oldClientAfterEdge = clientLogicalBottom();
|
|
|
|
updateLogicalHeight();
|
|
|
|
if (previousHeight != logicalHeight())
|
|
relayoutChildren = true;
|
|
|
|
layoutPositionedObjects(relayoutChildren, oldLeft != logicalLeft() ? ForcedLayoutAfterContainingBlockMoved : DefaultLayout);
|
|
|
|
// Add overflow from children (unless we're multi-column, since in that case all our child overflow is clipped anyway).
|
|
computeOverflow(oldClientAfterEdge);
|
|
}
|
|
|
|
void RenderBlockFlow::determineLogicalLeftPositionForChild(RenderBox* child)
|
|
{
|
|
LayoutUnit startPosition = borderStart() + paddingStart();
|
|
LayoutUnit totalAvailableLogicalWidth = borderAndPaddingLogicalWidth() + availableLogicalWidth();
|
|
|
|
LayoutUnit childMarginStart = marginStartForChild(child);
|
|
LayoutUnit newPosition = startPosition + childMarginStart;
|
|
|
|
// If the child has an offset from the content edge to avoid floats then use that, otherwise let any negative
|
|
// margin pull it back over the content edge or any positive margin push it out.
|
|
if (child->style()->marginStartUsing(style()).isAuto())
|
|
newPosition = std::max(newPosition, childMarginStart);
|
|
|
|
child->setX(style()->isLeftToRightDirection() ? newPosition : totalAvailableLogicalWidth - newPosition - logicalWidthForChild(child));
|
|
}
|
|
|
|
void RenderBlockFlow::layoutBlockChild(RenderBox* child)
|
|
{
|
|
child->computeAndSetBlockDirectionMargins(this);
|
|
LayoutUnit marginBefore = marginBeforeForChild(child);
|
|
child->setY(logicalHeight() + marginBefore);
|
|
child->layoutIfNeeded();
|
|
determineLogicalLeftPositionForChild(child);
|
|
setLogicalHeight(logicalHeight() + marginBefore + logicalHeightForChild(child) + marginAfterForChild(child));
|
|
}
|
|
|
|
void RenderBlockFlow::layoutChildren(bool relayoutChildren, SubtreeLayoutScope& layoutScope, LayoutUnit beforeEdge, LayoutUnit afterEdge)
|
|
{
|
|
dirtyForLayoutFromPercentageHeightDescendants(layoutScope);
|
|
|
|
RenderBox* next = firstChildBox();
|
|
RenderBox* lastNormalFlowChild = 0;
|
|
|
|
while (next) {
|
|
RenderBox* child = next;
|
|
next = child->nextSiblingBox();
|
|
|
|
updateBlockChildDirtyBitsBeforeLayout(relayoutChildren, child);
|
|
|
|
if (child->isOutOfFlowPositioned()) {
|
|
child->containingBlock()->insertPositionedObject(child);
|
|
adjustPositionedBlock(child);
|
|
continue;
|
|
}
|
|
|
|
// Lay out the child.
|
|
layoutBlockChild(child);
|
|
lastNormalFlowChild = child;
|
|
}
|
|
|
|
// Negative margins can cause our height to shrink below our minimal height (border/padding).
|
|
// If this happens, ensure that the computed height is increased to the minimal height.
|
|
setLogicalHeight(std::max(logicalHeight() + afterEdge, beforeEdge + afterEdge));
|
|
}
|
|
|
|
void RenderBlockFlow::adjustPositionedBlock(RenderBox* child)
|
|
{
|
|
bool hasStaticBlockPosition = child->style()->hasStaticBlockPosition();
|
|
|
|
LayoutUnit logicalTop = logicalHeight();
|
|
updateStaticInlinePositionForChild(child);
|
|
|
|
RenderLayer* childLayer = child->layer();
|
|
if (childLayer->staticBlockPosition() != logicalTop) {
|
|
childLayer->setStaticBlockPosition(logicalTop);
|
|
if (hasStaticBlockPosition)
|
|
child->setChildNeedsLayout(MarkOnlyThis);
|
|
}
|
|
}
|
|
|
|
RootInlineBox* RenderBlockFlow::createAndAppendRootInlineBox()
|
|
{
|
|
RootInlineBox* rootBox = createRootInlineBox();
|
|
m_lineBoxes.appendLineBox(rootBox);
|
|
|
|
return rootBox;
|
|
}
|
|
|
|
void RenderBlockFlow::deleteLineBoxTree()
|
|
{
|
|
m_lineBoxes.deleteLineBoxTree();
|
|
}
|
|
|
|
void RenderBlockFlow::updateStaticInlinePositionForChild(RenderBox* child)
|
|
{
|
|
if (child->style()->isOriginalDisplayInlineType())
|
|
setStaticInlinePositionForChild(child, startAlignedOffsetForLine(false));
|
|
else
|
|
setStaticInlinePositionForChild(child, startOffsetForContent());
|
|
}
|
|
|
|
void RenderBlockFlow::setStaticInlinePositionForChild(RenderBox* child, LayoutUnit inlinePosition)
|
|
{
|
|
child->layer()->setStaticInlinePosition(inlinePosition);
|
|
}
|
|
|
|
void RenderBlockFlow::addChild(RenderObject* newChild, RenderObject* beforeChild)
|
|
{
|
|
RenderBlock::addChild(newChild, beforeChild);
|
|
}
|
|
|
|
LayoutUnit RenderBlockFlow::logicalLeftSelectionOffset(RenderBlock* rootBlock, LayoutUnit position)
|
|
{
|
|
LayoutUnit logicalLeft = logicalLeftOffsetForLine(false);
|
|
if (logicalLeft == logicalLeftOffsetForContent())
|
|
return RenderBlock::logicalLeftSelectionOffset(rootBlock, position);
|
|
|
|
RenderBlock* cb = this;
|
|
while (cb != rootBlock) {
|
|
logicalLeft += cb->logicalLeft();
|
|
cb = cb->containingBlock();
|
|
}
|
|
return logicalLeft;
|
|
}
|
|
|
|
LayoutUnit RenderBlockFlow::logicalRightSelectionOffset(RenderBlock* rootBlock, LayoutUnit position)
|
|
{
|
|
LayoutUnit logicalRight = logicalRightOffsetForLine(false);
|
|
if (logicalRight == logicalRightOffsetForContent())
|
|
return RenderBlock::logicalRightSelectionOffset(rootBlock, position);
|
|
|
|
RenderBlock* cb = this;
|
|
while (cb != rootBlock) {
|
|
logicalRight += cb->logicalLeft();
|
|
cb = cb->containingBlock();
|
|
}
|
|
return logicalRight;
|
|
}
|
|
|
|
RootInlineBox* RenderBlockFlow::createRootInlineBox()
|
|
{
|
|
return new RootInlineBox(*this);
|
|
}
|
|
|
|
static void updateLogicalWidthForLeftAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth)
|
|
{
|
|
// The direction of the block should determine what happens with wide lines.
|
|
// In particular with RTL blocks, wide lines should still spill out to the left.
|
|
if (isLeftToRightDirection) {
|
|
if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun)
|
|
trailingSpaceRun->m_box->setLogicalWidth(std::max<float>(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth));
|
|
return;
|
|
}
|
|
|
|
if (trailingSpaceRun)
|
|
trailingSpaceRun->m_box->setLogicalWidth(0);
|
|
else if (totalLogicalWidth > availableLogicalWidth)
|
|
logicalLeft -= (totalLogicalWidth - availableLogicalWidth);
|
|
}
|
|
|
|
static void updateLogicalWidthForRightAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth)
|
|
{
|
|
// Wide lines spill out of the block based off direction.
|
|
// So even if text-align is right, if direction is LTR, wide lines should overflow out of the right
|
|
// side of the block.
|
|
if (isLeftToRightDirection) {
|
|
if (trailingSpaceRun) {
|
|
totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
|
|
trailingSpaceRun->m_box->setLogicalWidth(0);
|
|
}
|
|
if (totalLogicalWidth < availableLogicalWidth)
|
|
logicalLeft += availableLogicalWidth - totalLogicalWidth;
|
|
return;
|
|
}
|
|
|
|
if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) {
|
|
trailingSpaceRun->m_box->setLogicalWidth(std::max<float>(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth));
|
|
totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
|
|
} else
|
|
logicalLeft += availableLogicalWidth - totalLogicalWidth;
|
|
}
|
|
|
|
static void updateLogicalWidthForCenterAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth)
|
|
{
|
|
float trailingSpaceWidth = 0;
|
|
if (trailingSpaceRun) {
|
|
totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
|
|
trailingSpaceWidth = std::min(trailingSpaceRun->m_box->logicalWidth(), (availableLogicalWidth - totalLogicalWidth + 1) / 2);
|
|
trailingSpaceRun->m_box->setLogicalWidth(std::max<float>(0, trailingSpaceWidth));
|
|
}
|
|
if (isLeftToRightDirection)
|
|
logicalLeft += std::max<float>((availableLogicalWidth - totalLogicalWidth) / 2, 0);
|
|
else
|
|
logicalLeft += totalLogicalWidth > availableLogicalWidth ? (availableLogicalWidth - totalLogicalWidth) : (availableLogicalWidth - totalLogicalWidth) / 2 - trailingSpaceWidth;
|
|
}
|
|
|
|
void RenderBlockFlow::updateLogicalWidthForAlignment(const ETextAlign& textAlign, const RootInlineBox* rootInlineBox, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float& availableLogicalWidth, unsigned expansionOpportunityCount)
|
|
{
|
|
TextDirection direction;
|
|
if (rootInlineBox && rootInlineBox->renderer().style()->unicodeBidi() == Plaintext)
|
|
direction = rootInlineBox->direction();
|
|
else
|
|
direction = style()->direction();
|
|
|
|
// Armed with the total width of the line (without justification),
|
|
// we now examine our text-align property in order to determine where to position the
|
|
// objects horizontally. The total width of the line can be increased if we end up
|
|
// justifying text.
|
|
switch (textAlign) {
|
|
case LEFT:
|
|
updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
|
|
break;
|
|
case RIGHT:
|
|
updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
|
|
break;
|
|
case CENTER:
|
|
updateLogicalWidthForCenterAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
|
|
break;
|
|
case JUSTIFY:
|
|
adjustInlineDirectionLineBounds(expansionOpportunityCount, logicalLeft, availableLogicalWidth);
|
|
if (expansionOpportunityCount) {
|
|
if (trailingSpaceRun) {
|
|
totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
|
|
trailingSpaceRun->m_box->setLogicalWidth(0);
|
|
}
|
|
break;
|
|
}
|
|
// Fall through
|
|
case TASTART:
|
|
if (direction == LTR)
|
|
updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
|
|
else
|
|
updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
|
|
break;
|
|
case TAEND:
|
|
if (direction == LTR)
|
|
updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
|
|
else
|
|
updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
|
|
break;
|
|
}
|
|
}
|
|
|
|
LayoutUnit RenderBlockFlow::startAlignedOffsetForLine(bool firstLine)
|
|
{
|
|
ETextAlign textAlign = style()->textAlign();
|
|
|
|
if (textAlign == TASTART) // FIXME: Handle TAEND here
|
|
return startOffsetForLine(firstLine);
|
|
|
|
// updateLogicalWidthForAlignment() handles the direction of the block so no need to consider it here
|
|
float totalLogicalWidth = 0;
|
|
float logicalLeft = logicalLeftOffsetForLine(false).toFloat();
|
|
float availableLogicalWidth = logicalRightOffsetForLine(false) - logicalLeft;
|
|
updateLogicalWidthForAlignment(textAlign, 0, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0);
|
|
|
|
if (!style()->isLeftToRightDirection())
|
|
return logicalWidth() - logicalLeft;
|
|
return logicalLeft;
|
|
}
|
|
|
|
} // namespace blink
|