diff --git a/ReactCommon/yoga/yoga/Utils.h b/ReactCommon/yoga/yoga/Utils.h index 78765859643757..1bc9086e8ee747 100644 --- a/ReactCommon/yoga/yoga/Utils.h +++ b/ReactCommon/yoga/yoga/Utils.h @@ -11,6 +11,41 @@ #include "YGNode.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 relativeChildren; +}; + bool YGValueEqual(const YGValue a, const YGValue b); YGFlexDirection YGFlexDirectionCross( diff --git a/ReactCommon/yoga/yoga/YGNode.cpp b/ReactCommon/yoga/yoga/YGNode.cpp index d44dce371c1466..e56350dcad4dbb 100644 --- a/ReactCommon/yoga/yoga/YGNode.cpp +++ b/ReactCommon/yoga/yoga/YGNode.cpp @@ -59,6 +59,10 @@ YGVector YGNode::getChildren() const { return children_; } +uint32_t YGNode::getChildrenCount() const { + return static_cast(children_.size()); +} + YGNodeRef YGNode::getChild(uint32_t index) const { return children_.at(index); } diff --git a/ReactCommon/yoga/yoga/YGNode.h b/ReactCommon/yoga/yoga/YGNode.h index 79af81bcccac15..9d0c1029de9094 100644 --- a/ReactCommon/yoga/yoga/YGNode.h +++ b/ReactCommon/yoga/yoga/YGNode.h @@ -72,6 +72,7 @@ struct YGNode { uint32_t getLineIndex() const; YGNodeRef getParent() const; YGVector getChildren() const; + uint32_t getChildrenCount() const; YGNodeRef getChild(uint32_t index) const; YGNodeRef getNextChild() const; YGConfigRef getConfig() const; diff --git a/ReactCommon/yoga/yoga/Yoga.cpp b/ReactCommon/yoga/yoga/Yoga.cpp index c5e1501174fc77..d9771e509f53cf 100644 --- a/ReactCommon/yoga/yoga/Yoga.cpp +++ b/ReactCommon/yoga/yoga/Yoga.cpp @@ -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 // algorithm @@ -1803,7 +1885,6 @@ static void YGNodelayoutImpl(const YGNodeRef node, YGResolveFlexDirection(node->getStyle().flexDirection, direction); const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction); const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis); - const YGJustify justifyContent = node->getStyle().justifyContent; const bool isNodeFlexWrap = node->getStyle().flexWrap != YGWrapNoWrap; const float mainAxisParentSize = isMainAxisRow ? parentWidth : parentHeight; @@ -1879,9 +1960,10 @@ static void YGNodelayoutImpl(const YGNodeRef node, totalOuterFlexBasis); const bool flexBasisOverflows = measureModeMainDim == YGMeasureModeUndefined - ? false - : totalOuterFlexBasis > availableInnerMainDim; - if (isNodeFlexWrap && flexBasisOverflows && measureModeMainDim == YGMeasureModeAtMost) { + ? false + : totalOuterFlexBasis > availableInnerMainDim; + if (isNodeFlexWrap && flexBasisOverflows && + measureModeMainDim == YGMeasureModeAtMost) { measureModeMainDim = YGMeasureModeExactly; } // 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. float maxLineMainDim = 0; - - for (; endOfLineIndex < childCount; lineCount++, startOfLineIndex = endOfLineIndex) { - // Number of items on the currently line. May be different than the - // difference - // between start and end indicates because we skip over absolute-positioned - // items. - uint32_t itemsOnLine = 0; - - // sizeConsumedOnCurrentLine 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. - 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 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; - } + YGCollectFlexItemsRowValues collectedFlexItemsValues; + for (; endOfLineIndex < childCount; + lineCount++, startOfLineIndex = endOfLineIndex) { + collectedFlexItemsValues = YGCalculateCollectFlexItemsRowValues( + node, + parentDirection, + mainAxisParentSize, + availableInnerWidth, + availableInnerMainDim, + startOfLineIndex, + lineCount); + endOfLineIndex = collectedFlexItemsValues.endOfLineIndex; // If we don't need to measure the cross axis, we can skip the entire flex // step. - 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; + const bool canSkipFlex = + !performLayout && measureModeCrossDim == YGMeasureModeExactly; // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS // Calculate the remaining available space that needs to be allocated. @@ -1994,18 +2006,24 @@ static void YGNodelayoutImpl(const YGNodeRef node, bool sizeBasedOnContent = false; // If we don't measure with exact main dimension we want to ensure we don't violate min and max if (measureModeMainDim != YGMeasureModeExactly) { - if (!YGFloatIsUndefined(minInnerMainDim) && sizeConsumedOnCurrentLine < minInnerMainDim) { + if (!YGFloatIsUndefined(minInnerMainDim) && + collectedFlexItemsValues.sizeConsumedOnCurrentLine < + minInnerMainDim) { availableInnerMainDim = minInnerMainDim; - } else if (!YGFloatIsUndefined(maxInnerMainDim) && - sizeConsumedOnCurrentLine > maxInnerMainDim) { + } else if ( + !YGFloatIsUndefined(maxInnerMainDim) && + collectedFlexItemsValues.sizeConsumedOnCurrentLine > + maxInnerMainDim) { availableInnerMainDim = maxInnerMainDim; } else { 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 // itself, space we've used is all space we need. Root node also // should be shrunk to minimum - availableInnerMainDim = sizeConsumedOnCurrentLine; + availableInnerMainDim = + collectedFlexItemsValues.sizeConsumedOnCurrentLine; } if (node->getConfig()->useLegacyStretchBehaviour) { @@ -2017,13 +2035,14 @@ static void YGNodelayoutImpl(const YGNodeRef node, float remainingFreeSpace = 0; if (!sizeBasedOnContent && !YGFloatIsUndefined(availableInnerMainDim)) { - remainingFreeSpace = availableInnerMainDim - sizeConsumedOnCurrentLine; - } else if (sizeConsumedOnCurrentLine < 0) { + remainingFreeSpace = availableInnerMainDim - + collectedFlexItemsValues.sizeConsumedOnCurrentLine; + } else if (collectedFlexItemsValues.sizeConsumedOnCurrentLine < 0) { // availableInnerMainDim is indefinite which means the node is being sized based on its // content. // sizeConsumedOnCurrentLine is negative which means the node will allocate 0 points for // its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine. - remainingFreeSpace = -sizeConsumedOnCurrentLine; + remainingFreeSpace = -collectedFlexItemsValues.sizeConsumedOnCurrentLine; } const float originalRemainingFreeSpace = remainingFreeSpace; @@ -2062,7 +2081,8 @@ static void YGNodelayoutImpl(const YGNodeRef node, float deltaFlexShrinkScaledFactors = 0; float deltaFlexGrowFactors = 0; - for (auto currentRelativeChild : relativeChildren) { + for (auto currentRelativeChild : + collectedFlexItemsValues.relativeChildren) { childFlexBasis = YGNodeBoundAxisWithinMinAndMax( currentRelativeChild, mainAxis, @@ -2076,7 +2096,8 @@ static void YGNodelayoutImpl(const YGNodeRef node, // Is this child able to shrink? if (flexShrinkScaledFactor != 0) { baseMainSize = childFlexBasis + - remainingFreeSpace / totalFlexShrinkScaledFactors * + remainingFreeSpace / + collectedFlexItemsValues.totalFlexShrinkScaledFactors * flexShrinkScaledFactor; boundMainSize = YGNodeBoundAxis( currentRelativeChild, @@ -2101,7 +2122,9 @@ static void YGNodelayoutImpl(const YGNodeRef node, // Is this child able to grow? if (flexGrowFactor != 0) { baseMainSize = childFlexBasis + - remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor; + remainingFreeSpace / + collectedFlexItemsValues.totalFlexGrowFactors * + flexGrowFactor; boundMainSize = YGNodeBoundAxis( currentRelativeChild, mainAxis, @@ -2125,13 +2148,15 @@ static void YGNodelayoutImpl(const YGNodeRef node, currentRelativeChild = currentRelativeChild->getNextChild(); } - totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors; - totalFlexGrowFactors += deltaFlexGrowFactors; + collectedFlexItemsValues.totalFlexShrinkScaledFactors += + deltaFlexShrinkScaledFactors; + collectedFlexItemsValues.totalFlexGrowFactors += deltaFlexGrowFactors; remainingFreeSpace += deltaFreeSpace; // Second pass: resolve the sizes of the flexible items deltaFreeSpace = 0; - for (auto currentRelativeChild : relativeChildren) { + for (auto currentRelativeChild : + collectedFlexItemsValues.relativeChildren) { childFlexBasis = YGNodeBoundAxisWithinMinAndMax( currentRelativeChild, mainAxis, @@ -2146,11 +2171,12 @@ static void YGNodelayoutImpl(const YGNodeRef node, if (flexShrinkScaledFactor != 0) { float childSize; - if (totalFlexShrinkScaledFactors == 0) { + if (collectedFlexItemsValues.totalFlexShrinkScaledFactors == 0) { childSize = childFlexBasis + flexShrinkScaledFactor; } else { childSize = childFlexBasis + - (remainingFreeSpace / totalFlexShrinkScaledFactors) * + (remainingFreeSpace / + collectedFlexItemsValues.totalFlexShrinkScaledFactors) * flexShrinkScaledFactor; } @@ -2170,7 +2196,9 @@ static void YGNodelayoutImpl(const YGNodeRef node, currentRelativeChild, mainAxis, childFlexBasis + - remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor, + remainingFreeSpace / + collectedFlexItemsValues.totalFlexGrowFactors * + flexGrowFactor, availableInnerMainDim, 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) { switch (justifyContent) { case YGJustifyCenter: @@ -2342,20 +2377,23 @@ static void YGNodelayoutImpl(const YGNodeRef node, leadingMainDim = remainingFreeSpace; break; case YGJustifySpaceBetween: - if (itemsOnLine > 1) { - betweenMainDim = fmaxf(remainingFreeSpace, 0) / (itemsOnLine - 1); + if (collectedFlexItemsValues.itemsOnLine > 1) { + betweenMainDim = fmaxf(remainingFreeSpace, 0) / + (collectedFlexItemsValues.itemsOnLine - 1); } else { betweenMainDim = 0; } break; case YGJustifySpaceEvenly: // Space is distributed evenly across all elements - betweenMainDim = remainingFreeSpace / (itemsOnLine + 1); + betweenMainDim = + remainingFreeSpace / (collectedFlexItemsValues.itemsOnLine + 1); leadingMainDim = betweenMainDim; break; case YGJustifySpaceAround: // Space on the edges is half of the space between elements - betweenMainDim = remainingFreeSpace / itemsOnLine; + betweenMainDim = + remainingFreeSpace / collectedFlexItemsValues.itemsOnLine; leadingMainDim = betweenMainDim / 2; break; case YGJustifyFlexStart: