Moved out logic to calculate size consumed on a line into seperate function

Summary: Moved out logic to calculate size consumed on a line into seperate function

Reviewed By: emilsjolander

Differential Revision: D6797640

fbshipit-source-id: ad9757e7d603c0ce57f452b1e5c404037605bed9
This commit is contained in:
Pritesh Nandgaonkar
2018-02-05 06:32:59 -08:00
committed by Facebook Github Bot
parent 63be3ff84c
commit 27d2ad198e
4 changed files with 187 additions and 109 deletions

View File

@@ -11,6 +11,41 @@
#include "YGNode.h" #include "YGNode.h"
#include "Yoga-internal.h" #include "Yoga-internal.h"
// This struct is an helper model to hold the data for step 4 of flexbox
// algo, which is collecting the flex items in a line.
//
// - itemsOnLine: Number of items which can fit in a line considering the
// available Inner dimension, the flex items computed flexbasis and their
// margin. It may be different than the difference between start and end
// indicates because we skip over absolute-positioned items.
//
// - sizeConsumedOnCurrentLine: It is accumulation of the dimensions and margin
// of all the children on the current line. This will be used in order to either
// set the dimensions of the node if none already exist or to compute the
// remaining space left for the flexible children.
//
// - totalFlexGrowFactors: total flex grow factors of flex items which are to be
// layed in the current line
//
// - totalFlexShrinkFactors: total flex shrink factors of flex items which are
// to be layed in the current line
//
// - endOfLineIndex: Its the end index of the last flex item which was examined
// and it may or may not be part of the current line(as it may be absolutely
// positioned or inculding it may have caused to overshoot availableInnerDim)
//
// - relativeChildren: Maintain a vector of the child nodes that can shrink
// and/or grow.
struct YGCollectFlexItemsRowValues {
uint32_t itemsOnLine;
float sizeConsumedOnCurrentLine;
float totalFlexGrowFactors;
float totalFlexShrinkScaledFactors;
float endOfLineIndex;
std::vector<YGNodeRef> relativeChildren;
};
bool YGValueEqual(const YGValue a, const YGValue b); bool YGValueEqual(const YGValue a, const YGValue b);
YGFlexDirection YGFlexDirectionCross( YGFlexDirection YGFlexDirectionCross(

View File

@@ -59,6 +59,10 @@ YGVector YGNode::getChildren() const {
return children_; return children_;
} }
uint32_t YGNode::getChildrenCount() const {
return static_cast<uint32_t>(children_.size());
}
YGNodeRef YGNode::getChild(uint32_t index) const { YGNodeRef YGNode::getChild(uint32_t index) const {
return children_.at(index); return children_.at(index);
} }

View File

@@ -72,6 +72,7 @@ struct YGNode {
uint32_t getLineIndex() const; uint32_t getLineIndex() const;
YGNodeRef getParent() const; YGNodeRef getParent() const;
YGVector getChildren() const; YGVector getChildren() const;
uint32_t getChildrenCount() const;
YGNodeRef getChild(uint32_t index) const; YGNodeRef getChild(uint32_t index) const;
YGNodeRef getNextChild() const; YGNodeRef getNextChild() const;
YGConfigRef getConfig() const; YGConfigRef getConfig() const;

View File

@@ -1619,6 +1619,88 @@ static void YGNodeComputeFlexBasisForChildren(
} }
} }
// This function assumes that all the children of node have their
// computedFlexBasis properly computed(To do this use
// YGNodeComputeFlexBasisForChildren function).
// This function calculates YGCollectFlexItemsRowMeasurement
static YGCollectFlexItemsRowValues YGCalculateCollectFlexItemsRowValues(
const YGNodeRef& node,
const YGDirection parentDirection,
const float mainAxisParentSize,
const float availableInnerWidth,
const float availableInnerMainDim,
const uint32_t startOfLineIndex,
const uint32_t lineCount) {
YGCollectFlexItemsRowValues flexAlgoRowMeasurement = {};
flexAlgoRowMeasurement.relativeChildren.reserve(node->getChildren().size());
float sizeConsumedOnCurrentLineIncludingMinConstraint = 0;
const YGFlexDirection mainAxis = YGResolveFlexDirection(
node->getStyle().flexDirection, node->resolveDirection(parentDirection));
const bool isNodeFlexWrap = node->getStyle().flexWrap != YGWrapNoWrap;
// Add items to the current line until it's full or we run out of items.
uint32_t endOfLineIndex = startOfLineIndex;
for (; endOfLineIndex < node->getChildrenCount(); endOfLineIndex++) {
const YGNodeRef child = node->getChild(endOfLineIndex);
if (child->getStyle().display == YGDisplayNone ||
child->getStyle().positionType == YGPositionTypeAbsolute) {
continue;
}
child->setLineIndex(lineCount);
const float childMarginMainAxis =
YGNodeMarginForAxis(child, mainAxis, availableInnerWidth);
const float flexBasisWithMinAndMaxConstraints =
YGNodeBoundAxisWithinMinAndMax(
child,
mainAxis,
child->getLayout().computedFlexBasis,
mainAxisParentSize);
// If this is a multi-line flow and this item pushes us over the
// available size, we've
// hit the end of the current line. Break out of the loop and lay out
// the current line.
if (sizeConsumedOnCurrentLineIncludingMinConstraint +
flexBasisWithMinAndMaxConstraints + childMarginMainAxis >
availableInnerMainDim &&
isNodeFlexWrap && flexAlgoRowMeasurement.itemsOnLine > 0) {
break;
}
sizeConsumedOnCurrentLineIncludingMinConstraint +=
flexBasisWithMinAndMaxConstraints + childMarginMainAxis;
flexAlgoRowMeasurement.sizeConsumedOnCurrentLine +=
flexBasisWithMinAndMaxConstraints + childMarginMainAxis;
flexAlgoRowMeasurement.itemsOnLine++;
if (child->isNodeFlexible()) {
flexAlgoRowMeasurement.totalFlexGrowFactors += child->resolveFlexGrow();
// Unlike the grow factor, the shrink factor is scaled relative to the
// child dimension.
flexAlgoRowMeasurement.totalFlexShrinkScaledFactors +=
-child->resolveFlexShrink() * child->getLayout().computedFlexBasis;
}
flexAlgoRowMeasurement.relativeChildren.push_back(child);
}
// The total flex factor needs to be floored to 1.
if (flexAlgoRowMeasurement.totalFlexGrowFactors > 0 &&
flexAlgoRowMeasurement.totalFlexGrowFactors < 1) {
flexAlgoRowMeasurement.totalFlexGrowFactors = 1;
}
// The total flex shrink factor needs to be floored to 1.
if (flexAlgoRowMeasurement.totalFlexShrinkScaledFactors > 0 &&
flexAlgoRowMeasurement.totalFlexShrinkScaledFactors < 1) {
flexAlgoRowMeasurement.totalFlexShrinkScaledFactors = 1;
}
flexAlgoRowMeasurement.endOfLineIndex = endOfLineIndex;
return flexAlgoRowMeasurement;
}
// //
// This is the main routine that implements a subset of the flexbox layout // This is the main routine that implements a subset of the flexbox layout
// algorithm // algorithm
@@ -1803,7 +1885,6 @@ static void YGNodelayoutImpl(const YGNodeRef node,
YGResolveFlexDirection(node->getStyle().flexDirection, direction); YGResolveFlexDirection(node->getStyle().flexDirection, direction);
const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction); const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction);
const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis); const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis);
const YGJustify justifyContent = node->getStyle().justifyContent;
const bool isNodeFlexWrap = node->getStyle().flexWrap != YGWrapNoWrap; const bool isNodeFlexWrap = node->getStyle().flexWrap != YGWrapNoWrap;
const float mainAxisParentSize = isMainAxisRow ? parentWidth : parentHeight; const float mainAxisParentSize = isMainAxisRow ? parentWidth : parentHeight;
@@ -1881,7 +1962,8 @@ static void YGNodelayoutImpl(const YGNodeRef node,
const bool flexBasisOverflows = measureModeMainDim == YGMeasureModeUndefined const bool flexBasisOverflows = measureModeMainDim == YGMeasureModeUndefined
? false ? false
: totalOuterFlexBasis > availableInnerMainDim; : totalOuterFlexBasis > availableInnerMainDim;
if (isNodeFlexWrap && flexBasisOverflows && measureModeMainDim == YGMeasureModeAtMost) { if (isNodeFlexWrap && flexBasisOverflows &&
measureModeMainDim == YGMeasureModeAtMost) {
measureModeMainDim = YGMeasureModeExactly; measureModeMainDim = YGMeasureModeExactly;
} }
// STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES
@@ -1898,93 +1980,23 @@ static void YGNodelayoutImpl(const YGNodeRef node,
// Max main dimension of all the lines. // Max main dimension of all the lines.
float maxLineMainDim = 0; float maxLineMainDim = 0;
YGCollectFlexItemsRowValues collectedFlexItemsValues;
for (; endOfLineIndex < childCount; lineCount++, startOfLineIndex = endOfLineIndex) { for (; endOfLineIndex < childCount;
// Number of items on the currently line. May be different than the lineCount++, startOfLineIndex = endOfLineIndex) {
// difference collectedFlexItemsValues = YGCalculateCollectFlexItemsRowValues(
// between start and end indicates because we skip over absolute-positioned node,
// items. parentDirection,
uint32_t itemsOnLine = 0; mainAxisParentSize,
availableInnerWidth,
// sizeConsumedOnCurrentLine is accumulation of the dimensions and margin availableInnerMainDim,
// of all the children on the current line. This will be used in order to startOfLineIndex,
// either set the dimensions of the node if none already exist or to compute lineCount);
// the remaining space left for the flexible children. endOfLineIndex = collectedFlexItemsValues.endOfLineIndex;
float sizeConsumedOnCurrentLine = 0;
float sizeConsumedOnCurrentLineIncludingMinConstraint = 0;
float totalFlexGrowFactors = 0;
float totalFlexShrinkScaledFactors = 0;
// Maintain a vector of the child nodes that can shrink and/or grow.
std::vector<YGNodeRef> relativeChildren;
// Add items to the current line until it's full or we run out of items.
for (uint32_t i = startOfLineIndex; i < childCount; i++, endOfLineIndex++) {
const YGNodeRef child = node->getChild(i);
if (child->getStyle().display == YGDisplayNone ||
child->getStyle().positionType == YGPositionTypeAbsolute) {
continue;
}
child->setLineIndex(lineCount);
const float childMarginMainAxis =
YGNodeMarginForAxis(child, mainAxis, availableInnerWidth);
const float flexBasisWithMinAndMaxConstraints =
YGNodeBoundAxisWithinMinAndMax(
child,
mainAxis,
child->getLayout().computedFlexBasis,
mainAxisParentSize);
// If this is a multi-line flow and this item pushes us over the
// available size, we've
// hit the end of the current line. Break out of the loop and lay out
// the current line.
if (sizeConsumedOnCurrentLineIncludingMinConstraint +
flexBasisWithMinAndMaxConstraints + childMarginMainAxis >
availableInnerMainDim &&
isNodeFlexWrap && itemsOnLine > 0) {
break;
}
sizeConsumedOnCurrentLineIncludingMinConstraint +=
flexBasisWithMinAndMaxConstraints + childMarginMainAxis;
sizeConsumedOnCurrentLine +=
flexBasisWithMinAndMaxConstraints + childMarginMainAxis;
itemsOnLine++;
if (child->isNodeFlexible()) {
totalFlexGrowFactors += child->resolveFlexGrow();
// Unlike the grow factor, the shrink factor is scaled relative to the
// child dimension.
totalFlexShrinkScaledFactors +=
-child->resolveFlexShrink() * child->getLayout().computedFlexBasis;
}
// Store a private linked list of children that need to be layed out.
relativeChildren.push_back(child);
}
// The total flex factor needs to be floored to 1.
if (totalFlexGrowFactors > 0 && totalFlexGrowFactors < 1) {
totalFlexGrowFactors = 1;
}
// The total flex shrink factor needs to be floored to 1.
if (totalFlexShrinkScaledFactors > 0 && totalFlexShrinkScaledFactors < 1) {
totalFlexShrinkScaledFactors = 1;
}
// If we don't need to measure the cross axis, we can skip the entire flex // If we don't need to measure the cross axis, we can skip the entire flex
// step. // step.
const bool canSkipFlex = !performLayout && measureModeCrossDim == YGMeasureModeExactly; const bool canSkipFlex =
!performLayout && measureModeCrossDim == YGMeasureModeExactly;
// In order to position the elements in the main axis, we have two
// controls. The space between the beginning and the first element
// and the space between each two elements.
float leadingMainDim = 0;
float betweenMainDim = 0;
// STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS
// Calculate the remaining available space that needs to be allocated. // Calculate the remaining available space that needs to be allocated.
@@ -1994,18 +2006,24 @@ static void YGNodelayoutImpl(const YGNodeRef node,
bool sizeBasedOnContent = false; bool sizeBasedOnContent = false;
// If we don't measure with exact main dimension we want to ensure we don't violate min and max // If we don't measure with exact main dimension we want to ensure we don't violate min and max
if (measureModeMainDim != YGMeasureModeExactly) { if (measureModeMainDim != YGMeasureModeExactly) {
if (!YGFloatIsUndefined(minInnerMainDim) && sizeConsumedOnCurrentLine < minInnerMainDim) { if (!YGFloatIsUndefined(minInnerMainDim) &&
collectedFlexItemsValues.sizeConsumedOnCurrentLine <
minInnerMainDim) {
availableInnerMainDim = minInnerMainDim; availableInnerMainDim = minInnerMainDim;
} else if (!YGFloatIsUndefined(maxInnerMainDim) && } else if (
sizeConsumedOnCurrentLine > maxInnerMainDim) { !YGFloatIsUndefined(maxInnerMainDim) &&
collectedFlexItemsValues.sizeConsumedOnCurrentLine >
maxInnerMainDim) {
availableInnerMainDim = maxInnerMainDim; availableInnerMainDim = maxInnerMainDim;
} else { } else {
if (!node->getConfig()->useLegacyStretchBehaviour && if (!node->getConfig()->useLegacyStretchBehaviour &&
(totalFlexGrowFactors == 0 || node->resolveFlexGrow() == 0)) { (collectedFlexItemsValues.totalFlexGrowFactors == 0 ||
node->resolveFlexGrow() == 0)) {
// If we don't have any children to flex or we can't flex the node // If we don't have any children to flex or we can't flex the node
// itself, space we've used is all space we need. Root node also // itself, space we've used is all space we need. Root node also
// should be shrunk to minimum // should be shrunk to minimum
availableInnerMainDim = sizeConsumedOnCurrentLine; availableInnerMainDim =
collectedFlexItemsValues.sizeConsumedOnCurrentLine;
} }
if (node->getConfig()->useLegacyStretchBehaviour) { if (node->getConfig()->useLegacyStretchBehaviour) {
@@ -2017,13 +2035,14 @@ static void YGNodelayoutImpl(const YGNodeRef node,
float remainingFreeSpace = 0; float remainingFreeSpace = 0;
if (!sizeBasedOnContent && !YGFloatIsUndefined(availableInnerMainDim)) { if (!sizeBasedOnContent && !YGFloatIsUndefined(availableInnerMainDim)) {
remainingFreeSpace = availableInnerMainDim - sizeConsumedOnCurrentLine; remainingFreeSpace = availableInnerMainDim -
} else if (sizeConsumedOnCurrentLine < 0) { collectedFlexItemsValues.sizeConsumedOnCurrentLine;
} else if (collectedFlexItemsValues.sizeConsumedOnCurrentLine < 0) {
// availableInnerMainDim is indefinite which means the node is being sized based on its // availableInnerMainDim is indefinite which means the node is being sized based on its
// content. // content.
// sizeConsumedOnCurrentLine is negative which means the node will allocate 0 points for // sizeConsumedOnCurrentLine is negative which means the node will allocate 0 points for
// its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine. // its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine.
remainingFreeSpace = -sizeConsumedOnCurrentLine; remainingFreeSpace = -collectedFlexItemsValues.sizeConsumedOnCurrentLine;
} }
const float originalRemainingFreeSpace = remainingFreeSpace; const float originalRemainingFreeSpace = remainingFreeSpace;
@@ -2062,7 +2081,8 @@ static void YGNodelayoutImpl(const YGNodeRef node,
float deltaFlexShrinkScaledFactors = 0; float deltaFlexShrinkScaledFactors = 0;
float deltaFlexGrowFactors = 0; float deltaFlexGrowFactors = 0;
for (auto currentRelativeChild : relativeChildren) { for (auto currentRelativeChild :
collectedFlexItemsValues.relativeChildren) {
childFlexBasis = YGNodeBoundAxisWithinMinAndMax( childFlexBasis = YGNodeBoundAxisWithinMinAndMax(
currentRelativeChild, currentRelativeChild,
mainAxis, mainAxis,
@@ -2076,7 +2096,8 @@ static void YGNodelayoutImpl(const YGNodeRef node,
// Is this child able to shrink? // Is this child able to shrink?
if (flexShrinkScaledFactor != 0) { if (flexShrinkScaledFactor != 0) {
baseMainSize = childFlexBasis + baseMainSize = childFlexBasis +
remainingFreeSpace / totalFlexShrinkScaledFactors * remainingFreeSpace /
collectedFlexItemsValues.totalFlexShrinkScaledFactors *
flexShrinkScaledFactor; flexShrinkScaledFactor;
boundMainSize = YGNodeBoundAxis( boundMainSize = YGNodeBoundAxis(
currentRelativeChild, currentRelativeChild,
@@ -2101,7 +2122,9 @@ static void YGNodelayoutImpl(const YGNodeRef node,
// Is this child able to grow? // Is this child able to grow?
if (flexGrowFactor != 0) { if (flexGrowFactor != 0) {
baseMainSize = childFlexBasis + baseMainSize = childFlexBasis +
remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor; remainingFreeSpace /
collectedFlexItemsValues.totalFlexGrowFactors *
flexGrowFactor;
boundMainSize = YGNodeBoundAxis( boundMainSize = YGNodeBoundAxis(
currentRelativeChild, currentRelativeChild,
mainAxis, mainAxis,
@@ -2125,13 +2148,15 @@ static void YGNodelayoutImpl(const YGNodeRef node,
currentRelativeChild = currentRelativeChild->getNextChild(); currentRelativeChild = currentRelativeChild->getNextChild();
} }
totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors; collectedFlexItemsValues.totalFlexShrinkScaledFactors +=
totalFlexGrowFactors += deltaFlexGrowFactors; deltaFlexShrinkScaledFactors;
collectedFlexItemsValues.totalFlexGrowFactors += deltaFlexGrowFactors;
remainingFreeSpace += deltaFreeSpace; remainingFreeSpace += deltaFreeSpace;
// Second pass: resolve the sizes of the flexible items // Second pass: resolve the sizes of the flexible items
deltaFreeSpace = 0; deltaFreeSpace = 0;
for (auto currentRelativeChild : relativeChildren) { for (auto currentRelativeChild :
collectedFlexItemsValues.relativeChildren) {
childFlexBasis = YGNodeBoundAxisWithinMinAndMax( childFlexBasis = YGNodeBoundAxisWithinMinAndMax(
currentRelativeChild, currentRelativeChild,
mainAxis, mainAxis,
@@ -2146,11 +2171,12 @@ static void YGNodelayoutImpl(const YGNodeRef node,
if (flexShrinkScaledFactor != 0) { if (flexShrinkScaledFactor != 0) {
float childSize; float childSize;
if (totalFlexShrinkScaledFactors == 0) { if (collectedFlexItemsValues.totalFlexShrinkScaledFactors == 0) {
childSize = childFlexBasis + flexShrinkScaledFactor; childSize = childFlexBasis + flexShrinkScaledFactor;
} else { } else {
childSize = childFlexBasis + childSize = childFlexBasis +
(remainingFreeSpace / totalFlexShrinkScaledFactors) * (remainingFreeSpace /
collectedFlexItemsValues.totalFlexShrinkScaledFactors) *
flexShrinkScaledFactor; flexShrinkScaledFactor;
} }
@@ -2170,7 +2196,9 @@ static void YGNodelayoutImpl(const YGNodeRef node,
currentRelativeChild, currentRelativeChild,
mainAxis, mainAxis,
childFlexBasis + childFlexBasis +
remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor, remainingFreeSpace /
collectedFlexItemsValues.totalFlexGrowFactors *
flexGrowFactor,
availableInnerMainDim, availableInnerMainDim,
availableInnerWidth); availableInnerWidth);
} }
@@ -2333,6 +2361,13 @@ static void YGNodelayoutImpl(const YGNodeRef node,
} }
} }
// In order to position the elements in the main axis, we have two
// controls. The space between the beginning and the first element
// and the space between each two elements.
float leadingMainDim = 0;
float betweenMainDim = 0;
const YGJustify justifyContent = node->getStyle().justifyContent;
if (numberOfAutoMarginsOnCurrentLine == 0) { if (numberOfAutoMarginsOnCurrentLine == 0) {
switch (justifyContent) { switch (justifyContent) {
case YGJustifyCenter: case YGJustifyCenter:
@@ -2342,20 +2377,23 @@ static void YGNodelayoutImpl(const YGNodeRef node,
leadingMainDim = remainingFreeSpace; leadingMainDim = remainingFreeSpace;
break; break;
case YGJustifySpaceBetween: case YGJustifySpaceBetween:
if (itemsOnLine > 1) { if (collectedFlexItemsValues.itemsOnLine > 1) {
betweenMainDim = fmaxf(remainingFreeSpace, 0) / (itemsOnLine - 1); betweenMainDim = fmaxf(remainingFreeSpace, 0) /
(collectedFlexItemsValues.itemsOnLine - 1);
} else { } else {
betweenMainDim = 0; betweenMainDim = 0;
} }
break; break;
case YGJustifySpaceEvenly: case YGJustifySpaceEvenly:
// Space is distributed evenly across all elements // Space is distributed evenly across all elements
betweenMainDim = remainingFreeSpace / (itemsOnLine + 1); betweenMainDim =
remainingFreeSpace / (collectedFlexItemsValues.itemsOnLine + 1);
leadingMainDim = betweenMainDim; leadingMainDim = betweenMainDim;
break; break;
case YGJustifySpaceAround: case YGJustifySpaceAround:
// Space on the edges is half of the space between elements // Space on the edges is half of the space between elements
betweenMainDim = remainingFreeSpace / itemsOnLine; betweenMainDim =
remainingFreeSpace / collectedFlexItemsValues.itemsOnLine;
leadingMainDim = betweenMainDim / 2; leadingMainDim = betweenMainDim / 2;
break; break;
case YGJustifyFlexStart: case YGJustifyFlexStart: