Separate FlexLine functionality (#1374)

Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1374

X-link: https://github.com/facebook/react-native/pull/39396

Yoga today has a struct `CollectFlexItemsRowValues`, and function `calculateFlexItemsRowValues()`. These names have evolved over time into something not making much sense.

The job of `calculateFlexItemsRowValues()` is a flex-wrap container into lines (i.e. line-breaking main-axis content, which may be row or column). It returns line-breaking results, but some other fields on `calculateFlexItemsRowValues()` are set much later in the process, and the struct is acting effectivelty as a holder for the line-specific values.

This change:
1. Does some renaming (mainly to FlexLine)
2. Reconciles the count `itemsOnLine` and list `relativeChildren` to list `itemsInFlow` (`relativeChildren` is a lie, as it can include elements with `YGPositionTypeStatic` and exclude relative elements which have `display: "none"`. It really just means children which are included in the layout flow for the line)
3. Makes non-changing algorithm outputs const for clarity of what is a running value, and what is a result of line-breaking values with flex basis.
4. Moves working layout values to a substructure `flexLine.layout`
5. Replaces some dishonest documentation about `endOfLineIndex`.
6. Extracts this logic out of `CalculateLayout()` to a separate file
7. Extracts `boundAxis` wholesale into a separate file, to be usable outside of `CalculateLayout.cpp`

Reviewed By: rshest

Differential Revision: D49133837

fbshipit-source-id: ec68c5a3d2f01e7c9bd8d26e28298331a3fe2475
This commit is contained in:
Nick Gerleman
2023-09-11 19:51:40 -07:00
committed by Facebook GitHub Bot
parent 241c5e4baf
commit a4f36bdb51
5 changed files with 374 additions and 341 deletions

View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <yoga/algorithm/FlexDirection.h>
#include <yoga/algorithm/ResolveValue.h>
#include <yoga/node/Node.h>
#include <yoga/numeric/Comparison.h>
#include <yoga/numeric/FloatOptional.h>
namespace facebook::yoga {
inline float paddingAndBorderForAxis(
const yoga::Node* const node,
const YGFlexDirection axis,
const float widthSize) {
return (node->getLeadingPaddingAndBorder(axis, widthSize) +
node->getTrailingPaddingAndBorder(axis, widthSize))
.unwrap();
}
inline FloatOptional boundAxisWithinMinAndMax(
const yoga::Node* const node,
const YGFlexDirection axis,
const FloatOptional value,
const float axisSize) {
FloatOptional min;
FloatOptional max;
if (isColumn(axis)) {
min = yoga::resolveValue(
node->getStyle().minDimensions()[YGDimensionHeight], axisSize);
max = yoga::resolveValue(
node->getStyle().maxDimensions()[YGDimensionHeight], axisSize);
} else if (isRow(axis)) {
min = yoga::resolveValue(
node->getStyle().minDimensions()[YGDimensionWidth], axisSize);
max = yoga::resolveValue(
node->getStyle().maxDimensions()[YGDimensionWidth], axisSize);
}
if (max >= FloatOptional{0} && value > max) {
return max;
}
if (min >= FloatOptional{0} && value < min) {
return min;
}
return value;
}
// Like boundAxisWithinMinAndMax but also ensures that the value doesn't
// go below the padding and border amount.
inline float boundAxis(
const yoga::Node* const node,
const YGFlexDirection axis,
const float value,
const float axisSize,
const float widthSize) {
return yoga::maxOrDefined(
boundAxisWithinMinAndMax(node, axis, FloatOptional{value}, axisSize)
.unwrap(),
paddingAndBorderForAxis(node, axis, widthSize));
}
} // namespace facebook::yoga

View File

@@ -15,10 +15,11 @@
#include <yoga/algorithm/Align.h> #include <yoga/algorithm/Align.h>
#include <yoga/algorithm/Baseline.h> #include <yoga/algorithm/Baseline.h>
#include <yoga/algorithm/BoundAxis.h>
#include <yoga/algorithm/Cache.h> #include <yoga/algorithm/Cache.h>
#include <yoga/algorithm/CalculateLayout.h> #include <yoga/algorithm/CalculateLayout.h>
#include <yoga/algorithm/CollectFlexItemsRowValues.h>
#include <yoga/algorithm/FlexDirection.h> #include <yoga/algorithm/FlexDirection.h>
#include <yoga/algorithm/FlexLine.h>
#include <yoga/algorithm/PixelGrid.h> #include <yoga/algorithm/PixelGrid.h>
#include <yoga/algorithm/ResolveValue.h> #include <yoga/algorithm/ResolveValue.h>
#include <yoga/debug/AssertFatal.h> #include <yoga/debug/AssertFatal.h>
@@ -26,6 +27,7 @@
#include <yoga/event/event.h> #include <yoga/event/event.h>
#include <yoga/node/Node.h> #include <yoga/node/Node.h>
#include <yoga/numeric/Comparison.h> #include <yoga/numeric/Comparison.h>
#include <yoga/numeric/FloatOptional.h>
namespace facebook::yoga { namespace facebook::yoga {
@@ -58,15 +60,6 @@ static const std::array<YGEdge, 4> pos = {{
static const std::array<YGDimension, 4> dim = { static const std::array<YGDimension, 4> dim = {
{YGDimensionHeight, YGDimensionHeight, YGDimensionWidth, YGDimensionWidth}}; {YGDimensionHeight, YGDimensionHeight, YGDimensionWidth, YGDimensionWidth}};
static inline float paddingAndBorderForAxis(
const yoga::Node* const node,
const YGFlexDirection axis,
const float widthSize) {
return (node->getLeadingPaddingAndBorder(axis, widthSize) +
node->getTrailingPaddingAndBorder(axis, widthSize))
.unwrap();
}
static bool isBaselineLayout(const yoga::Node* node) { static bool isBaselineLayout(const yoga::Node* node) {
if (isColumn(node->getStyle().flexDirection())) { if (isColumn(node->getStyle().flexDirection())) {
return false; return false;
@@ -120,51 +113,6 @@ static inline bool isLayoutDimensionDefined(
return !yoga::isUndefined(value) && value >= 0.0f; return !yoga::isUndefined(value) && value >= 0.0f;
} }
static FloatOptional boundAxisWithinMinAndMax(
const yoga::Node* const node,
const YGFlexDirection axis,
const FloatOptional value,
const float axisSize) {
FloatOptional min;
FloatOptional max;
if (isColumn(axis)) {
min = yoga::resolveValue(
node->getStyle().minDimensions()[YGDimensionHeight], axisSize);
max = yoga::resolveValue(
node->getStyle().maxDimensions()[YGDimensionHeight], axisSize);
} else if (isRow(axis)) {
min = yoga::resolveValue(
node->getStyle().minDimensions()[YGDimensionWidth], axisSize);
max = yoga::resolveValue(
node->getStyle().maxDimensions()[YGDimensionWidth], axisSize);
}
if (max >= FloatOptional{0} && value > max) {
return max;
}
if (min >= FloatOptional{0} && value < min) {
return min;
}
return value;
}
// Like boundAxisWithinMinAndMax but also ensures that the value doesn't
// go below the padding and border amount.
static inline float boundAxis(
const yoga::Node* const node,
const YGFlexDirection axis,
const float value,
const float axisSize,
const float widthSize) {
return yoga::maxOrDefined(
boundAxisWithinMinAndMax(node, axis, FloatOptional{value}, axisSize)
.unwrap(),
paddingAndBorderForAxis(node, axis, widthSize));
}
static void setChildTrailingPosition( static void setChildTrailingPosition(
const yoga::Node* const node, const yoga::Node* const node,
yoga::Node* const child, yoga::Node* const child,
@@ -955,103 +903,12 @@ static float computeFlexBasisForChildren(
return totalOuterFlexBasis; return totalOuterFlexBasis;
} }
// This function assumes that all the children of node have their
// computedFlexBasis properly computed(To do this use
// computeFlexBasisForChildren function). This function calculates
// YGCollectFlexItemsRowMeasurement
static CollectFlexItemsRowValues calculateCollectFlexItemsRowValues(
yoga::Node* const node,
const YGDirection ownerDirection,
const float mainAxisownerSize,
const float availableInnerWidth,
const float availableInnerMainDim,
const size_t startOfLineIndex,
const size_t lineCount) {
CollectFlexItemsRowValues flexAlgoRowMeasurement = {};
flexAlgoRowMeasurement.relativeChildren.reserve(node->getChildren().size());
float sizeConsumedOnCurrentLineIncludingMinConstraint = 0;
const YGFlexDirection mainAxis = resolveDirection(
node->getStyle().flexDirection(), node->resolveDirection(ownerDirection));
const bool isNodeFlexWrap = node->getStyle().flexWrap() != YGWrapNoWrap;
const float gap = node->getGapForAxis(mainAxis, availableInnerWidth).unwrap();
// Add items to the current line until it's full or we run out of items.
size_t endOfLineIndex = startOfLineIndex;
for (; endOfLineIndex < node->getChildren().size(); endOfLineIndex++) {
auto child = node->getChild(endOfLineIndex);
if (child->getStyle().display() == YGDisplayNone ||
child->getStyle().positionType() == YGPositionTypeAbsolute) {
continue;
}
const bool isFirstElementInLine = (endOfLineIndex - startOfLineIndex) == 0;
child->setLineIndex(lineCount);
const float childMarginMainAxis =
child->getMarginForAxis(mainAxis, availableInnerWidth).unwrap();
const float childLeadingGapMainAxis = isFirstElementInLine ? 0.0f : gap;
const float flexBasisWithMinAndMaxConstraints =
boundAxisWithinMinAndMax(
child,
mainAxis,
child->getLayout().computedFlexBasis,
mainAxisownerSize)
.unwrap();
// 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 +
childLeadingGapMainAxis >
availableInnerMainDim &&
isNodeFlexWrap && flexAlgoRowMeasurement.itemsOnLine > 0) {
break;
}
sizeConsumedOnCurrentLineIncludingMinConstraint +=
flexBasisWithMinAndMaxConstraints + childMarginMainAxis +
childLeadingGapMainAxis;
flexAlgoRowMeasurement.sizeConsumedOnCurrentLine +=
flexBasisWithMinAndMaxConstraints + childMarginMainAxis +
childLeadingGapMainAxis;
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.unwrap();
}
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;
}
// It distributes the free space to the flexible items and ensures that the size // It distributes the free space to the flexible items and ensures that the size
// of the flex items abide the min and max constraints. At the end of this // of the flex items abide the min and max constraints. At the end of this
// function the child nodes would have proper size. Prior using this function // function the child nodes would have proper size. Prior using this function
// please ensure that distributeFreeSpaceFirstPass is called. // please ensure that distributeFreeSpaceFirstPass is called.
static float distributeFreeSpaceSecondPass( static float distributeFreeSpaceSecondPass(
CollectFlexItemsRowValues& collectedFlexItemsValues, FlexLine& flexLine,
yoga::Node* const node, yoga::Node* const node,
const YGFlexDirection mainAxis, const YGFlexDirection mainAxis,
const YGFlexDirection crossAxis, const YGFlexDirection crossAxis,
@@ -1075,55 +932,53 @@ static float distributeFreeSpaceSecondPass(
const bool isMainAxisRow = isRow(mainAxis); const bool isMainAxisRow = isRow(mainAxis);
const bool isNodeFlexWrap = node->getStyle().flexWrap() != YGWrapNoWrap; const bool isNodeFlexWrap = node->getStyle().flexWrap() != YGWrapNoWrap;
for (auto currentRelativeChild : collectedFlexItemsValues.relativeChildren) { for (auto currentLineChild : flexLine.itemsInFlow) {
childFlexBasis = boundAxisWithinMinAndMax( childFlexBasis = boundAxisWithinMinAndMax(
currentRelativeChild, currentLineChild,
mainAxis, mainAxis,
currentRelativeChild->getLayout().computedFlexBasis, currentLineChild->getLayout().computedFlexBasis,
mainAxisownerSize) mainAxisownerSize)
.unwrap(); .unwrap();
float updatedMainSize = childFlexBasis; float updatedMainSize = childFlexBasis;
if (!yoga::isUndefined(collectedFlexItemsValues.remainingFreeSpace) && if (!yoga::isUndefined(flexLine.layout.remainingFreeSpace) &&
collectedFlexItemsValues.remainingFreeSpace < 0) { flexLine.layout.remainingFreeSpace < 0) {
flexShrinkScaledFactor = flexShrinkScaledFactor =
-currentRelativeChild->resolveFlexShrink() * childFlexBasis; -currentLineChild->resolveFlexShrink() * childFlexBasis;
// Is this child able to shrink? // Is this child able to shrink?
if (flexShrinkScaledFactor != 0) { if (flexShrinkScaledFactor != 0) {
float childSize; float childSize;
if (!yoga::isUndefined( if (!yoga::isUndefined(flexLine.layout.totalFlexShrinkScaledFactors) &&
collectedFlexItemsValues.totalFlexShrinkScaledFactors) && flexLine.layout.totalFlexShrinkScaledFactors == 0) {
collectedFlexItemsValues.totalFlexShrinkScaledFactors == 0) {
childSize = childFlexBasis + flexShrinkScaledFactor; childSize = childFlexBasis + flexShrinkScaledFactor;
} else { } else {
childSize = childFlexBasis + childSize = childFlexBasis +
(collectedFlexItemsValues.remainingFreeSpace / (flexLine.layout.remainingFreeSpace /
collectedFlexItemsValues.totalFlexShrinkScaledFactors) * flexLine.layout.totalFlexShrinkScaledFactors) *
flexShrinkScaledFactor; flexShrinkScaledFactor;
} }
updatedMainSize = boundAxis( updatedMainSize = boundAxis(
currentRelativeChild, currentLineChild,
mainAxis, mainAxis,
childSize, childSize,
availableInnerMainDim, availableInnerMainDim,
availableInnerWidth); availableInnerWidth);
} }
} else if ( } else if (
!yoga::isUndefined(collectedFlexItemsValues.remainingFreeSpace) && !yoga::isUndefined(flexLine.layout.remainingFreeSpace) &&
collectedFlexItemsValues.remainingFreeSpace > 0) { flexLine.layout.remainingFreeSpace > 0) {
flexGrowFactor = currentRelativeChild->resolveFlexGrow(); flexGrowFactor = currentLineChild->resolveFlexGrow();
// Is this child able to grow? // Is this child able to grow?
if (!std::isnan(flexGrowFactor) && flexGrowFactor != 0) { if (!std::isnan(flexGrowFactor) && flexGrowFactor != 0) {
updatedMainSize = boundAxis( updatedMainSize = boundAxis(
currentRelativeChild, currentLineChild,
mainAxis, mainAxis,
childFlexBasis + childFlexBasis +
collectedFlexItemsValues.remainingFreeSpace / flexLine.layout.remainingFreeSpace /
collectedFlexItemsValues.totalFlexGrowFactors * flexLine.layout.totalFlexGrowFactors * flexGrowFactor,
flexGrowFactor,
availableInnerMainDim, availableInnerMainDim,
availableInnerWidth); availableInnerWidth);
} }
@@ -1132,10 +987,10 @@ static float distributeFreeSpaceSecondPass(
deltaFreeSpace += updatedMainSize - childFlexBasis; deltaFreeSpace += updatedMainSize - childFlexBasis;
const float marginMain = const float marginMain =
currentRelativeChild->getMarginForAxis(mainAxis, availableInnerWidth) currentLineChild->getMarginForAxis(mainAxis, availableInnerWidth)
.unwrap(); .unwrap();
const float marginCross = const float marginCross =
currentRelativeChild->getMarginForAxis(crossAxis, availableInnerWidth) currentLineChild->getMarginForAxis(crossAxis, availableInnerWidth)
.unwrap(); .unwrap();
float childCrossSize; float childCrossSize;
@@ -1143,7 +998,7 @@ static float distributeFreeSpaceSecondPass(
YGMeasureMode childCrossMeasureMode; YGMeasureMode childCrossMeasureMode;
YGMeasureMode childMainMeasureMode = YGMeasureModeExactly; YGMeasureMode childMainMeasureMode = YGMeasureModeExactly;
const auto& childStyle = currentRelativeChild->getStyle(); const auto& childStyle = currentLineChild->getStyle();
if (!childStyle.aspectRatio().isUndefined()) { if (!childStyle.aspectRatio().isUndefined()) {
childCrossSize = isMainAxisRow childCrossSize = isMainAxisRow
? (childMainSize - marginMain) / childStyle.aspectRatio().unwrap() ? (childMainSize - marginMain) / childStyle.aspectRatio().unwrap()
@@ -1154,18 +1009,16 @@ static float distributeFreeSpaceSecondPass(
} else if ( } else if (
!std::isnan(availableInnerCrossDim) && !std::isnan(availableInnerCrossDim) &&
!styleDefinesDimension( !styleDefinesDimension(
currentRelativeChild, crossAxis, availableInnerCrossDim) && currentLineChild, crossAxis, availableInnerCrossDim) &&
measureModeCrossDim == YGMeasureModeExactly && measureModeCrossDim == YGMeasureModeExactly &&
!(isNodeFlexWrap && mainAxisOverflows) && !(isNodeFlexWrap && mainAxisOverflows) &&
resolveChildAlignment(node, currentRelativeChild) == YGAlignStretch && resolveChildAlignment(node, currentLineChild) == YGAlignStretch &&
currentRelativeChild->marginLeadingValue(crossAxis).unit != currentLineChild->marginLeadingValue(crossAxis).unit != YGUnitAuto &&
YGUnitAuto && currentLineChild->marginTrailingValue(crossAxis).unit != YGUnitAuto) {
currentRelativeChild->marginTrailingValue(crossAxis).unit !=
YGUnitAuto) {
childCrossSize = availableInnerCrossDim; childCrossSize = availableInnerCrossDim;
childCrossMeasureMode = YGMeasureModeExactly; childCrossMeasureMode = YGMeasureModeExactly;
} else if (!styleDefinesDimension( } else if (!styleDefinesDimension(
currentRelativeChild, crossAxis, availableInnerCrossDim)) { currentLineChild, crossAxis, availableInnerCrossDim)) {
childCrossSize = availableInnerCrossDim; childCrossSize = availableInnerCrossDim;
childCrossMeasureMode = yoga::isUndefined(childCrossSize) childCrossMeasureMode = yoga::isUndefined(childCrossSize)
? YGMeasureModeUndefined ? YGMeasureModeUndefined
@@ -1173,12 +1026,12 @@ static float distributeFreeSpaceSecondPass(
} else { } else {
childCrossSize = childCrossSize =
yoga::resolveValue( yoga::resolveValue(
currentRelativeChild->getResolvedDimension(dim[crossAxis]), currentLineChild->getResolvedDimension(dim[crossAxis]),
availableInnerCrossDim) availableInnerCrossDim)
.unwrap() + .unwrap() +
marginCross; marginCross;
const bool isLoosePercentageMeasurement = const bool isLoosePercentageMeasurement =
currentRelativeChild->getResolvedDimension(dim[crossAxis]).unit == currentLineChild->getResolvedDimension(dim[crossAxis]).unit ==
YGUnitPercent && YGUnitPercent &&
measureModeCrossDim != YGMeasureModeExactly; measureModeCrossDim != YGMeasureModeExactly;
childCrossMeasureMode = childCrossMeasureMode =
@@ -1188,14 +1041,14 @@ static float distributeFreeSpaceSecondPass(
} }
constrainMaxSizeForMode( constrainMaxSizeForMode(
currentRelativeChild, currentLineChild,
mainAxis, mainAxis,
availableInnerMainDim, availableInnerMainDim,
availableInnerWidth, availableInnerWidth,
&childMainMeasureMode, &childMainMeasureMode,
&childMainSize); &childMainSize);
constrainMaxSizeForMode( constrainMaxSizeForMode(
currentRelativeChild, currentLineChild,
crossAxis, crossAxis,
availableInnerCrossDim, availableInnerCrossDim,
availableInnerWidth, availableInnerWidth,
@@ -1204,11 +1057,10 @@ static float distributeFreeSpaceSecondPass(
const bool requiresStretchLayout = const bool requiresStretchLayout =
!styleDefinesDimension( !styleDefinesDimension(
currentRelativeChild, crossAxis, availableInnerCrossDim) && currentLineChild, crossAxis, availableInnerCrossDim) &&
resolveChildAlignment(node, currentRelativeChild) == YGAlignStretch && resolveChildAlignment(node, currentLineChild) == YGAlignStretch &&
currentRelativeChild->marginLeadingValue(crossAxis).unit != currentLineChild->marginLeadingValue(crossAxis).unit != YGUnitAuto &&
YGUnitAuto && currentLineChild->marginTrailingValue(crossAxis).unit != YGUnitAuto;
currentRelativeChild->marginTrailingValue(crossAxis).unit != YGUnitAuto;
const float childWidth = isMainAxisRow ? childMainSize : childCrossSize; const float childWidth = isMainAxisRow ? childMainSize : childCrossSize;
const float childHeight = !isMainAxisRow ? childMainSize : childCrossSize; const float childHeight = !isMainAxisRow ? childMainSize : childCrossSize;
@@ -1222,7 +1074,7 @@ static float distributeFreeSpaceSecondPass(
// Recursively call the layout algorithm for this child with the updated // Recursively call the layout algorithm for this child with the updated
// main size. // main size.
calculateLayoutInternal( calculateLayoutInternal(
currentRelativeChild, currentLineChild,
childWidth, childWidth,
childHeight, childHeight,
node->getLayout().direction(), node->getLayout().direction(),
@@ -1240,7 +1092,7 @@ static float distributeFreeSpaceSecondPass(
generationCount); generationCount);
node->setLayoutHadOverflow( node->setLayoutHadOverflow(
node->getLayout().hadOverflow() || node->getLayout().hadOverflow() ||
currentRelativeChild->getLayout().hadOverflow()); currentLineChild->getLayout().hadOverflow());
} }
return deltaFreeSpace; return deltaFreeSpace;
} }
@@ -1249,7 +1101,7 @@ static float distributeFreeSpaceSecondPass(
// whose min and max constraints are triggered, those flex item's clamped size // whose min and max constraints are triggered, those flex item's clamped size
// is removed from the remaingfreespace. // is removed from the remaingfreespace.
static void distributeFreeSpaceFirstPass( static void distributeFreeSpaceFirstPass(
CollectFlexItemsRowValues& collectedFlexItemsValues, FlexLine& flexLine,
const YGFlexDirection mainAxis, const YGFlexDirection mainAxis,
const float mainAxisownerSize, const float mainAxisownerSize,
const float availableInnerMainDim, const float availableInnerMainDim,
@@ -1260,28 +1112,27 @@ static void distributeFreeSpaceFirstPass(
float boundMainSize = 0; float boundMainSize = 0;
float deltaFreeSpace = 0; float deltaFreeSpace = 0;
for (auto currentRelativeChild : collectedFlexItemsValues.relativeChildren) { for (auto currentLineChild : flexLine.itemsInFlow) {
float childFlexBasis = float childFlexBasis = boundAxisWithinMinAndMax(
boundAxisWithinMinAndMax( currentLineChild,
currentRelativeChild, mainAxis,
mainAxis, currentLineChild->getLayout().computedFlexBasis,
currentRelativeChild->getLayout().computedFlexBasis, mainAxisownerSize)
mainAxisownerSize) .unwrap();
.unwrap();
if (collectedFlexItemsValues.remainingFreeSpace < 0) { if (flexLine.layout.remainingFreeSpace < 0) {
flexShrinkScaledFactor = flexShrinkScaledFactor =
-currentRelativeChild->resolveFlexShrink() * childFlexBasis; -currentLineChild->resolveFlexShrink() * childFlexBasis;
// Is this child able to shrink? // Is this child able to shrink?
if (!yoga::isUndefined(flexShrinkScaledFactor) && if (!yoga::isUndefined(flexShrinkScaledFactor) &&
flexShrinkScaledFactor != 0) { flexShrinkScaledFactor != 0) {
baseMainSize = childFlexBasis + baseMainSize = childFlexBasis +
collectedFlexItemsValues.remainingFreeSpace / flexLine.layout.remainingFreeSpace /
collectedFlexItemsValues.totalFlexShrinkScaledFactors * flexLine.layout.totalFlexShrinkScaledFactors *
flexShrinkScaledFactor; flexShrinkScaledFactor;
boundMainSize = boundAxis( boundMainSize = boundAxis(
currentRelativeChild, currentLineChild,
mainAxis, mainAxis,
baseMainSize, baseMainSize,
availableInnerMainDim, availableInnerMainDim,
@@ -1294,23 +1145,23 @@ static void distributeFreeSpaceFirstPass(
// resulting in the item's size calculation being identical in the // resulting in the item's size calculation being identical in the
// first and second passes. // first and second passes.
deltaFreeSpace += boundMainSize - childFlexBasis; deltaFreeSpace += boundMainSize - childFlexBasis;
collectedFlexItemsValues.totalFlexShrinkScaledFactors -= flexLine.layout.totalFlexShrinkScaledFactors -=
(-currentRelativeChild->resolveFlexShrink() * (-currentLineChild->resolveFlexShrink() *
currentRelativeChild->getLayout().computedFlexBasis.unwrap()); currentLineChild->getLayout().computedFlexBasis.unwrap());
} }
} }
} else if ( } else if (
!yoga::isUndefined(collectedFlexItemsValues.remainingFreeSpace) && !yoga::isUndefined(flexLine.layout.remainingFreeSpace) &&
collectedFlexItemsValues.remainingFreeSpace > 0) { flexLine.layout.remainingFreeSpace > 0) {
flexGrowFactor = currentRelativeChild->resolveFlexGrow(); flexGrowFactor = currentLineChild->resolveFlexGrow();
// Is this child able to grow? // Is this child able to grow?
if (!yoga::isUndefined(flexGrowFactor) && flexGrowFactor != 0) { if (!yoga::isUndefined(flexGrowFactor) && flexGrowFactor != 0) {
baseMainSize = childFlexBasis + baseMainSize = childFlexBasis +
collectedFlexItemsValues.remainingFreeSpace / flexLine.layout.remainingFreeSpace /
collectedFlexItemsValues.totalFlexGrowFactors * flexGrowFactor; flexLine.layout.totalFlexGrowFactors * flexGrowFactor;
boundMainSize = boundAxis( boundMainSize = boundAxis(
currentRelativeChild, currentLineChild,
mainAxis, mainAxis,
baseMainSize, baseMainSize,
availableInnerMainDim, availableInnerMainDim,
@@ -1324,12 +1175,12 @@ static void distributeFreeSpaceFirstPass(
// resulting in the item's size calculation being identical in the // resulting in the item's size calculation being identical in the
// first and second passes. // first and second passes.
deltaFreeSpace += boundMainSize - childFlexBasis; deltaFreeSpace += boundMainSize - childFlexBasis;
collectedFlexItemsValues.totalFlexGrowFactors -= flexGrowFactor; flexLine.layout.totalFlexGrowFactors -= flexGrowFactor;
} }
} }
} }
} }
collectedFlexItemsValues.remainingFreeSpace -= deltaFreeSpace; flexLine.layout.remainingFreeSpace -= deltaFreeSpace;
} }
// Do two passes over the flex items to figure out how to distribute the // Do two passes over the flex items to figure out how to distribute the
@@ -1356,7 +1207,7 @@ static void distributeFreeSpaceFirstPass(
// //
static void resolveFlexibleLength( static void resolveFlexibleLength(
yoga::Node* const node, yoga::Node* const node,
CollectFlexItemsRowValues& collectedFlexItemsValues, FlexLine& flexLine,
const YGFlexDirection mainAxis, const YGFlexDirection mainAxis,
const YGFlexDirection crossAxis, const YGFlexDirection crossAxis,
const float mainAxisownerSize, const float mainAxisownerSize,
@@ -1372,10 +1223,10 @@ static void resolveFlexibleLength(
void* const layoutContext, void* const layoutContext,
const uint32_t depth, const uint32_t depth,
const uint32_t generationCount) { const uint32_t generationCount) {
const float originalFreeSpace = collectedFlexItemsValues.remainingFreeSpace; const float originalFreeSpace = flexLine.layout.remainingFreeSpace;
// First pass: detect the flex items whose min/max constraints trigger // First pass: detect the flex items whose min/max constraints trigger
distributeFreeSpaceFirstPass( distributeFreeSpaceFirstPass(
collectedFlexItemsValues, flexLine,
mainAxis, mainAxis,
mainAxisownerSize, mainAxisownerSize,
availableInnerMainDim, availableInnerMainDim,
@@ -1383,7 +1234,7 @@ static void resolveFlexibleLength(
// Second pass: resolve the sizes of the flexible items // Second pass: resolve the sizes of the flexible items
const float distributedFreeSpace = distributeFreeSpaceSecondPass( const float distributedFreeSpace = distributeFreeSpaceSecondPass(
collectedFlexItemsValues, flexLine,
node, node,
mainAxis, mainAxis,
crossAxis, crossAxis,
@@ -1401,13 +1252,12 @@ static void resolveFlexibleLength(
depth, depth,
generationCount); generationCount);
collectedFlexItemsValues.remainingFreeSpace = flexLine.layout.remainingFreeSpace = originalFreeSpace - distributedFreeSpace;
originalFreeSpace - distributedFreeSpace;
} }
static void YGJustifyMainAxis( static void YGJustifyMainAxis(
yoga::Node* const node, yoga::Node* const node,
CollectFlexItemsRowValues& collectedFlexItemsValues, FlexLine& flexLine,
const size_t startOfLineIndex, const size_t startOfLineIndex,
const YGFlexDirection mainAxis, const YGFlexDirection mainAxis,
const YGFlexDirection crossAxis, const YGFlexDirection crossAxis,
@@ -1429,7 +1279,7 @@ static void YGJustifyMainAxis(
// If we are using "at most" rules in the main axis, make sure that // If we are using "at most" rules in the main axis, make sure that
// remainingFreeSpace is 0 when min main dimension is not given // remainingFreeSpace is 0 when min main dimension is not given
if (measureModeMainDim == YGMeasureModeAtMost && if (measureModeMainDim == YGMeasureModeAtMost &&
collectedFlexItemsValues.remainingFreeSpace > 0) { flexLine.layout.remainingFreeSpace > 0) {
if (!style.minDimensions()[dim[mainAxis]].isUndefined() && if (!style.minDimensions()[dim[mainAxis]].isUndefined() &&
!yoga::resolveValue( !yoga::resolveValue(
style.minDimensions()[dim[mainAxis]], mainAxisownerSize) style.minDimensions()[dim[mainAxis]], mainAxisownerSize)
@@ -1447,17 +1297,16 @@ static void YGJustifyMainAxis(
.unwrap() - .unwrap() -
leadingPaddingAndBorderMain - trailingPaddingAndBorderMain; leadingPaddingAndBorderMain - trailingPaddingAndBorderMain;
const float occupiedSpaceByChildNodes = const float occupiedSpaceByChildNodes =
availableInnerMainDim - collectedFlexItemsValues.remainingFreeSpace; availableInnerMainDim - flexLine.layout.remainingFreeSpace;
collectedFlexItemsValues.remainingFreeSpace = yoga::maxOrDefined( flexLine.layout.remainingFreeSpace = yoga::maxOrDefined(
0, minAvailableMainDim - occupiedSpaceByChildNodes); 0, minAvailableMainDim - occupiedSpaceByChildNodes);
} else { } else {
collectedFlexItemsValues.remainingFreeSpace = 0; flexLine.layout.remainingFreeSpace = 0;
} }
} }
int numberOfAutoMarginsOnCurrentLine = 0; int numberOfAutoMarginsOnCurrentLine = 0;
for (size_t i = startOfLineIndex; i < collectedFlexItemsValues.endOfLineIndex; for (size_t i = startOfLineIndex; i < flexLine.endOfLineIndex; i++) {
i++) {
auto child = node->getChild(i); auto child = node->getChild(i);
if (child->getStyle().positionType() != YGPositionTypeAbsolute) { if (child->getStyle().positionType() != YGPositionTypeAbsolute) {
if (child->marginLeadingValue(mainAxis).unit == YGUnitAuto) { if (child->marginLeadingValue(mainAxis).unit == YGUnitAuto) {
@@ -1479,29 +1328,28 @@ static void YGJustifyMainAxis(
if (numberOfAutoMarginsOnCurrentLine == 0) { if (numberOfAutoMarginsOnCurrentLine == 0) {
switch (justifyContent) { switch (justifyContent) {
case YGJustifyCenter: case YGJustifyCenter:
leadingMainDim = collectedFlexItemsValues.remainingFreeSpace / 2; leadingMainDim = flexLine.layout.remainingFreeSpace / 2;
break; break;
case YGJustifyFlexEnd: case YGJustifyFlexEnd:
leadingMainDim = collectedFlexItemsValues.remainingFreeSpace; leadingMainDim = flexLine.layout.remainingFreeSpace;
break; break;
case YGJustifySpaceBetween: case YGJustifySpaceBetween:
if (collectedFlexItemsValues.itemsOnLine > 1) { if (flexLine.itemsInFlow.size() > 1) {
betweenMainDim += betweenMainDim +=
yoga::maxOrDefined( yoga::maxOrDefined(flexLine.layout.remainingFreeSpace, 0) /
collectedFlexItemsValues.remainingFreeSpace, 0) / static_cast<float>(flexLine.itemsInFlow.size() - 1);
static_cast<float>(collectedFlexItemsValues.itemsOnLine - 1);
} }
break; break;
case YGJustifySpaceEvenly: case YGJustifySpaceEvenly:
// Space is distributed evenly across all elements // Space is distributed evenly across all elements
leadingMainDim = collectedFlexItemsValues.remainingFreeSpace / leadingMainDim = flexLine.layout.remainingFreeSpace /
static_cast<float>(collectedFlexItemsValues.itemsOnLine + 1); static_cast<float>(flexLine.itemsInFlow.size() + 1);
betweenMainDim += leadingMainDim; betweenMainDim += leadingMainDim;
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
leadingMainDim = 0.5f * collectedFlexItemsValues.remainingFreeSpace / leadingMainDim = 0.5f * flexLine.layout.remainingFreeSpace /
static_cast<float>(collectedFlexItemsValues.itemsOnLine); static_cast<float>(flexLine.itemsInFlow.size());
betweenMainDim += leadingMainDim * 2; betweenMainDim += leadingMainDim * 2;
break; break;
case YGJustifyFlexStart: case YGJustifyFlexStart:
@@ -1509,19 +1357,17 @@ static void YGJustifyMainAxis(
} }
} }
collectedFlexItemsValues.mainDim = flexLine.layout.mainDim = leadingPaddingAndBorderMain + leadingMainDim;
leadingPaddingAndBorderMain + leadingMainDim; flexLine.layout.crossDim = 0;
collectedFlexItemsValues.crossDim = 0;
float maxAscentForCurrentLine = 0; float maxAscentForCurrentLine = 0;
float maxDescentForCurrentLine = 0; float maxDescentForCurrentLine = 0;
bool isNodeBaselineLayout = isBaselineLayout(node); bool isNodeBaselineLayout = isBaselineLayout(node);
for (size_t i = startOfLineIndex; i < collectedFlexItemsValues.endOfLineIndex; for (size_t i = startOfLineIndex; i < flexLine.endOfLineIndex; i++) {
i++) {
const auto child = node->getChild(i); const auto child = node->getChild(i);
const Style& childStyle = child->getStyle(); const Style& childStyle = child->getStyle();
const LayoutResults& childLayout = child->getLayout(); const LayoutResults& childLayout = child->getLayout();
const bool isLastChild = i == collectedFlexItemsValues.endOfLineIndex - 1; const bool isLastChild = i == flexLine.endOfLineIndex - 1;
// remove the gap if it is the last element of the line // remove the gap if it is the last element of the line
if (isLastChild) { if (isLastChild) {
betweenMainDim -= gap; betweenMainDim -= gap;
@@ -1548,21 +1394,18 @@ static void YGJustifyMainAxis(
// take part in that phase. // take part in that phase.
if (childStyle.positionType() != YGPositionTypeAbsolute) { if (childStyle.positionType() != YGPositionTypeAbsolute) {
if (child->marginLeadingValue(mainAxis).unit == YGUnitAuto) { if (child->marginLeadingValue(mainAxis).unit == YGUnitAuto) {
collectedFlexItemsValues.mainDim += flexLine.layout.mainDim += flexLine.layout.remainingFreeSpace /
collectedFlexItemsValues.remainingFreeSpace /
static_cast<float>(numberOfAutoMarginsOnCurrentLine); static_cast<float>(numberOfAutoMarginsOnCurrentLine);
} }
if (performLayout) { if (performLayout) {
child->setLayoutPosition( child->setLayoutPosition(
childLayout.position[pos[mainAxis]] + childLayout.position[pos[mainAxis]] + flexLine.layout.mainDim,
collectedFlexItemsValues.mainDim,
pos[mainAxis]); pos[mainAxis]);
} }
if (child->marginTrailingValue(mainAxis).unit == YGUnitAuto) { if (child->marginTrailingValue(mainAxis).unit == YGUnitAuto) {
collectedFlexItemsValues.mainDim += flexLine.layout.mainDim += flexLine.layout.remainingFreeSpace /
collectedFlexItemsValues.remainingFreeSpace /
static_cast<float>(numberOfAutoMarginsOnCurrentLine); static_cast<float>(numberOfAutoMarginsOnCurrentLine);
} }
bool canSkipFlex = bool canSkipFlex =
@@ -1571,14 +1414,14 @@ static void YGJustifyMainAxis(
// If we skipped the flex step, then we can't rely on the measuredDims // If we skipped the flex step, then we can't rely on the measuredDims
// because they weren't computed. This means we can't call // because they weren't computed. This means we can't call
// dimensionWithMargin. // dimensionWithMargin.
collectedFlexItemsValues.mainDim += betweenMainDim + flexLine.layout.mainDim += betweenMainDim +
child->getMarginForAxis(mainAxis, availableInnerWidth).unwrap() + child->getMarginForAxis(mainAxis, availableInnerWidth).unwrap() +
childLayout.computedFlexBasis.unwrap(); childLayout.computedFlexBasis.unwrap();
collectedFlexItemsValues.crossDim = availableInnerCrossDim; flexLine.layout.crossDim = availableInnerCrossDim;
} else { } else {
// The main dimension is the sum of all the elements dimension plus // The main dimension is the sum of all the elements dimension plus
// the spacing. // the spacing.
collectedFlexItemsValues.mainDim += betweenMainDim + flexLine.layout.mainDim += betweenMainDim +
dimensionWithMargin(child, mainAxis, availableInnerWidth); dimensionWithMargin(child, mainAxis, availableInnerWidth);
if (isNodeBaselineLayout) { if (isNodeBaselineLayout) {
@@ -1605,8 +1448,8 @@ static void YGJustifyMainAxis(
// The cross dimension is the max of the elements dimension since // The cross dimension is the max of the elements dimension since
// there can only be one element in that cross dimension in the case // there can only be one element in that cross dimension in the case
// when the items are not baseline aligned // when the items are not baseline aligned
collectedFlexItemsValues.crossDim = yoga::maxOrDefined( flexLine.layout.crossDim = yoga::maxOrDefined(
collectedFlexItemsValues.crossDim, flexLine.layout.crossDim,
dimensionWithMargin(child, crossAxis, availableInnerWidth)); dimensionWithMargin(child, crossAxis, availableInnerWidth));
} }
} }
@@ -1618,10 +1461,10 @@ static void YGJustifyMainAxis(
} }
} }
} }
collectedFlexItemsValues.mainDim += trailingPaddingAndBorderMain; flexLine.layout.mainDim += trailingPaddingAndBorderMain;
if (isNodeBaselineLayout) { if (isNodeBaselineLayout) {
collectedFlexItemsValues.crossDim = flexLine.layout.crossDim =
maxAscentForCurrentLine + maxDescentForCurrentLine; maxAscentForCurrentLine + maxDescentForCurrentLine;
} }
} }
@@ -1918,10 +1761,9 @@ static void calculateLayoutImpl(
// Max main dimension of all the lines. // Max main dimension of all the lines.
float maxLineMainDim = 0; float maxLineMainDim = 0;
CollectFlexItemsRowValues collectedFlexItemsValues;
for (; endOfLineIndex < childCount; for (; endOfLineIndex < childCount;
lineCount++, startOfLineIndex = endOfLineIndex) { lineCount++, startOfLineIndex = endOfLineIndex) {
collectedFlexItemsValues = calculateCollectFlexItemsRowValues( auto flexLine = calculateFlexLine(
node, node,
ownerDirection, ownerDirection,
mainAxisownerSize, mainAxisownerSize,
@@ -1929,7 +1771,8 @@ static void calculateLayoutImpl(
availableInnerMainDim, availableInnerMainDim,
startOfLineIndex, startOfLineIndex,
lineCount); lineCount);
endOfLineIndex = collectedFlexItemsValues.endOfLineIndex;
endOfLineIndex = flexLine.endOfLineIndex;
// 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.
@@ -1970,29 +1813,25 @@ static void calculateLayoutImpl(
isMainAxisRow ? maxInnerWidth : maxInnerHeight; isMainAxisRow ? maxInnerWidth : maxInnerHeight;
if (!yoga::isUndefined(minInnerMainDim) && if (!yoga::isUndefined(minInnerMainDim) &&
collectedFlexItemsValues.sizeConsumedOnCurrentLine < flexLine.sizeConsumed < minInnerMainDim) {
minInnerMainDim) {
availableInnerMainDim = minInnerMainDim; availableInnerMainDim = minInnerMainDim;
} else if ( } else if (
!yoga::isUndefined(maxInnerMainDim) && !yoga::isUndefined(maxInnerMainDim) &&
collectedFlexItemsValues.sizeConsumedOnCurrentLine > flexLine.sizeConsumed > maxInnerMainDim) {
maxInnerMainDim) {
availableInnerMainDim = maxInnerMainDim; availableInnerMainDim = maxInnerMainDim;
} else { } else {
bool useLegacyStretchBehaviour = bool useLegacyStretchBehaviour =
node->hasErrata(YGErrataStretchFlexBasis); node->hasErrata(YGErrataStretchFlexBasis);
if (!useLegacyStretchBehaviour && if (!useLegacyStretchBehaviour &&
((!yoga::isUndefined( ((!yoga::isUndefined(flexLine.layout.totalFlexGrowFactors) &&
collectedFlexItemsValues.totalFlexGrowFactors) && flexLine.layout.totalFlexGrowFactors == 0) ||
collectedFlexItemsValues.totalFlexGrowFactors == 0) ||
(!yoga::isUndefined(node->resolveFlexGrow()) && (!yoga::isUndefined(node->resolveFlexGrow()) &&
node->resolveFlexGrow() == 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 = availableInnerMainDim = flexLine.sizeConsumed;
collectedFlexItemsValues.sizeConsumedOnCurrentLine;
} }
sizeBasedOnContent = !useLegacyStretchBehaviour; sizeBasedOnContent = !useLegacyStretchBehaviour;
@@ -2000,21 +1839,20 @@ static void calculateLayoutImpl(
} }
if (!sizeBasedOnContent && !yoga::isUndefined(availableInnerMainDim)) { if (!sizeBasedOnContent && !yoga::isUndefined(availableInnerMainDim)) {
collectedFlexItemsValues.remainingFreeSpace = availableInnerMainDim - flexLine.layout.remainingFreeSpace =
collectedFlexItemsValues.sizeConsumedOnCurrentLine; availableInnerMainDim - flexLine.sizeConsumed;
} else if (collectedFlexItemsValues.sizeConsumedOnCurrentLine < 0) { } else if (flexLine.sizeConsumed < 0) {
// availableInnerMainDim is indefinite which means the node is being sized // availableInnerMainDim is indefinite which means the node is being sized
// based on its content. sizeConsumedOnCurrentLine is negative which means // based on its content. sizeConsumed is negative which means
// the node will allocate 0 points for its content. Consequently, // the node will allocate 0 points for its content. Consequently,
// remainingFreeSpace is 0 - sizeConsumedOnCurrentLine. // remainingFreeSpace is 0 - sizeConsumed.
collectedFlexItemsValues.remainingFreeSpace = flexLine.layout.remainingFreeSpace = -flexLine.sizeConsumed;
-collectedFlexItemsValues.sizeConsumedOnCurrentLine;
} }
if (!canSkipFlex) { if (!canSkipFlex) {
resolveFlexibleLength( resolveFlexibleLength(
node, node,
collectedFlexItemsValues, flexLine,
mainAxis, mainAxis,
crossAxis, crossAxis,
mainAxisownerSize, mainAxisownerSize,
@@ -2034,7 +1872,7 @@ static void calculateLayoutImpl(
node->setLayoutHadOverflow( node->setLayoutHadOverflow(
node->getLayout().hadOverflow() | node->getLayout().hadOverflow() |
(collectedFlexItemsValues.remainingFreeSpace < 0)); (flexLine.layout.remainingFreeSpace < 0));
// STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION
@@ -2045,7 +1883,7 @@ static void calculateLayoutImpl(
YGJustifyMainAxis( YGJustifyMainAxis(
node, node,
collectedFlexItemsValues, flexLine,
startOfLineIndex, startOfLineIndex,
mainAxis, mainAxis,
crossAxis, crossAxis,
@@ -2067,7 +1905,7 @@ static void calculateLayoutImpl(
boundAxis( boundAxis(
node, node,
crossAxis, crossAxis,
collectedFlexItemsValues.crossDim + paddingAndBorderAxisCross, flexLine.layout.crossDim + paddingAndBorderAxisCross,
crossAxisownerSize, crossAxisownerSize,
ownerWidth) - ownerWidth) -
paddingAndBorderAxisCross; paddingAndBorderAxisCross;
@@ -2075,15 +1913,15 @@ static void calculateLayoutImpl(
// If there's no flex wrap, the cross dimension is defined by the container. // If there's no flex wrap, the cross dimension is defined by the container.
if (!isNodeFlexWrap && measureModeCrossDim == YGMeasureModeExactly) { if (!isNodeFlexWrap && measureModeCrossDim == YGMeasureModeExactly) {
collectedFlexItemsValues.crossDim = availableInnerCrossDim; flexLine.layout.crossDim = availableInnerCrossDim;
} }
// Clamp to the min/max size specified on the container. // Clamp to the min/max size specified on the container.
collectedFlexItemsValues.crossDim = flexLine.layout.crossDim =
boundAxis( boundAxis(
node, node,
crossAxis, crossAxis,
collectedFlexItemsValues.crossDim + paddingAndBorderAxisCross, flexLine.layout.crossDim + paddingAndBorderAxisCross,
crossAxisownerSize, crossAxisownerSize,
ownerWidth) - ownerWidth) -
paddingAndBorderAxisCross; paddingAndBorderAxisCross;
@@ -2148,7 +1986,7 @@ static void calculateLayoutImpl(
(isMainAxisRow (isMainAxisRow
? childMainSize / childStyle.aspectRatio().unwrap() ? childMainSize / childStyle.aspectRatio().unwrap()
: childMainSize * childStyle.aspectRatio().unwrap()) : childMainSize * childStyle.aspectRatio().unwrap())
: collectedFlexItemsValues.crossDim; : flexLine.layout.crossDim;
childMainSize += childMainSize +=
child->getMarginForAxis(mainAxis, availableInnerWidth) child->getMarginForAxis(mainAxis, availableInnerWidth)
@@ -2239,9 +2077,9 @@ static void calculateLayoutImpl(
} }
const float appliedCrossGap = lineCount != 0 ? crossAxisGap : 0.0f; const float appliedCrossGap = lineCount != 0 ? crossAxisGap : 0.0f;
totalLineCrossDim += collectedFlexItemsValues.crossDim + appliedCrossGap; totalLineCrossDim += flexLine.layout.crossDim + appliedCrossGap;
maxLineMainDim = maxLineMainDim =
yoga::maxOrDefined(maxLineMainDim, collectedFlexItemsValues.mainDim); yoga::maxOrDefined(maxLineMainDim, flexLine.layout.mainDim);
} }
// STEP 8: MULTI-LINE CONTENT ALIGNMENT // STEP 8: MULTI-LINE CONTENT ALIGNMENT

View File

@@ -1,58 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <vector>
#include <yoga/node/Node.h>
namespace facebook::yoga {
// 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
// laid in the current line
//
// - totalFlexShrinkFactors: total flex shrink factors of flex items which are
// to be laid 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 including it may have caused to overshoot availableInnerDim)
//
// - relativeChildren: Maintain a vector of the child nodes that can shrink
// and/or grow.
struct CollectFlexItemsRowValues {
size_t itemsOnLine;
float sizeConsumedOnCurrentLine;
float totalFlexGrowFactors;
float totalFlexShrinkScaledFactors;
size_t endOfLineIndex;
std::vector<yoga::Node*> relativeChildren;
float remainingFreeSpace;
// The size of the mainDim for the row after considering size, padding, margin
// and border of flex items. This is used to calculate maxLineDim after going
// through all the rows to decide on the main axis size of owner.
float mainDim;
// The size of the crossDim for the row after considering size, padding,
// margin and border of flex items. Used for calculating containers crossSize.
float crossDim;
};
} // namespace facebook::yoga

107
yoga/algorithm/FlexLine.cpp Normal file
View File

@@ -0,0 +1,107 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <yoga/Yoga.h>
#include <yoga/algorithm/BoundAxis.h>
#include <yoga/algorithm/FlexDirection.h>
#include <yoga/algorithm/FlexLine.h>
namespace facebook::yoga {
FlexLine calculateFlexLine(
yoga::Node* const node,
const YGDirection ownerDirection,
const float mainAxisownerSize,
const float availableInnerWidth,
const float availableInnerMainDim,
const size_t startOfLineIndex,
const size_t lineCount) {
std::vector<yoga::Node*> itemsInFlow;
itemsInFlow.reserve(node->getChildren().size());
float sizeConsumed = 0.0f;
float totalFlexGrowFactors = 0.0f;
float totalFlexShrinkScaledFactors = 0.0f;
size_t endOfLineIndex = startOfLineIndex;
float sizeConsumedIncludingMinConstraint = 0;
const YGFlexDirection mainAxis = resolveDirection(
node->getStyle().flexDirection(), node->resolveDirection(ownerDirection));
const bool isNodeFlexWrap = node->getStyle().flexWrap() != YGWrapNoWrap;
const float gap = node->getGapForAxis(mainAxis, availableInnerWidth).unwrap();
// Add items to the current line until it's full or we run out of items.
for (; endOfLineIndex < node->getChildren().size(); endOfLineIndex++) {
auto child = node->getChild(endOfLineIndex);
if (child->getStyle().display() == YGDisplayNone ||
child->getStyle().positionType() == YGPositionTypeAbsolute) {
continue;
}
const bool isFirstElementInLine = (endOfLineIndex - startOfLineIndex) == 0;
child->setLineIndex(lineCount);
const float childMarginMainAxis =
child->getMarginForAxis(mainAxis, availableInnerWidth).unwrap();
const float childLeadingGapMainAxis = isFirstElementInLine ? 0.0f : gap;
const float flexBasisWithMinAndMaxConstraints =
boundAxisWithinMinAndMax(
child,
mainAxis,
child->getLayout().computedFlexBasis,
mainAxisownerSize)
.unwrap();
// 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 (sizeConsumedIncludingMinConstraint + flexBasisWithMinAndMaxConstraints +
childMarginMainAxis + childLeadingGapMainAxis >
availableInnerMainDim &&
isNodeFlexWrap && itemsInFlow.size() > 0) {
break;
}
sizeConsumedIncludingMinConstraint += flexBasisWithMinAndMaxConstraints +
childMarginMainAxis + childLeadingGapMainAxis;
sizeConsumed += flexBasisWithMinAndMaxConstraints + childMarginMainAxis +
childLeadingGapMainAxis;
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.unwrap();
}
itemsInFlow.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;
}
return FlexLine{
std::move(itemsInFlow),
sizeConsumed,
endOfLineIndex,
FlexLineRunningLayout{
totalFlexGrowFactors,
totalFlexShrinkScaledFactors,
}};
}
} // namespace facebook::yoga

74
yoga/algorithm/FlexLine.h Normal file
View File

@@ -0,0 +1,74 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <vector>
#include <yoga/Yoga.h>
#include <yoga/node/Node.h>
namespace facebook::yoga {
struct FlexLineRunningLayout {
// Total flex grow factors of flex items which are to be laid in the current
// line. This is decremented as free space is distributed.
float totalFlexGrowFactors{0.0f};
// Total flex shrink factors of flex items which are to be laid in the current
// line. This is decremented as free space is distributed.
float totalFlexShrinkScaledFactors{0.0f};
// The amount of available space within inner dimensions of the line which may
// still be distributed.
float remainingFreeSpace{0.0f};
// The size of the mainDim for the row after considering size, padding, margin
// and border of flex items. This is used to calculate maxLineDim after going
// through all the rows to decide on the main axis size of owner.
float mainDim{0.0f};
// The size of the crossDim for the row after considering size, padding,
// margin and border of flex items. Used for calculating containers crossSize.
float crossDim{0.0f};
};
struct FlexLine {
// List of children which are part of the line flow. This means they are not
// positioned absolutely, or with `display: "none"`, and do not overflow the
// available dimensions.
const std::vector<yoga::Node*> itemsInFlow{};
// 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.
const float sizeConsumed{0.0f};
// The index of the first item beyond the current line.
const size_t endOfLineIndex{0};
// Layout information about the line computed in steps after line-breaking
FlexLineRunningLayout layout{};
};
// Calculates where a line starting at a given index should break, returning
// information about the collective children on the liune.
//
// This function assumes that all the children of node have their
// computedFlexBasis properly computed(To do this use
// computeFlexBasisForChildren function).
FlexLine calculateFlexLine(
yoga::Node* const node,
YGDirection ownerDirection,
float mainAxisownerSize,
float availableInnerWidth,
float availableInnerMainDim,
size_t startOfLineIndex,
size_t lineCount);
} // namespace facebook::yoga