in lib/yoga/src/main/cpp/yoga/Yoga.cpp [2398:2613]
static void YGJustifyMainAxis(
const YGNodeRef node,
YGCollectFlexItemsRowValues& collectedFlexItemsValues,
const uint32_t startOfLineIndex,
const YGFlexDirection mainAxis,
const YGFlexDirection crossAxis,
const YGMeasureMode measureModeMainDim,
const YGMeasureMode measureModeCrossDim,
const float mainAxisownerSize,
const float ownerWidth,
const float availableInnerMainDim,
const float availableInnerCrossDim,
const float availableInnerWidth,
const bool performLayout,
void* const layoutContext) {
const auto& style = node->getStyle();
const float leadingPaddingAndBorderMain =
node->getLeadingPaddingAndBorder(mainAxis, ownerWidth).unwrap();
const float trailingPaddingAndBorderMain =
node->getTrailingPaddingAndBorder(mainAxis, ownerWidth).unwrap();
// If we are using "at most" rules in the main axis, make sure that
// remainingFreeSpace is 0 when min main dimension is not given
if (measureModeMainDim == YGMeasureModeAtMost &&
collectedFlexItemsValues.remainingFreeSpace > 0) {
if (!style.minDimensions()[dim[mainAxis]].isUndefined() &&
!YGResolveValue(style.minDimensions()[dim[mainAxis]], mainAxisownerSize)
.isUndefined()) {
// This condition makes sure that if the size of main dimension(after
// considering child nodes main dim, leading and trailing padding etc)
// falls below min dimension, then the remainingFreeSpace is reassigned
// considering the min dimension
// `minAvailableMainDim` denotes minimum available space in which child
// can be laid out, it will exclude space consumed by padding and border.
const float minAvailableMainDim =
YGResolveValue(
style.minDimensions()[dim[mainAxis]], mainAxisownerSize)
.unwrap() -
leadingPaddingAndBorderMain - trailingPaddingAndBorderMain;
const float occupiedSpaceByChildNodes =
availableInnerMainDim - collectedFlexItemsValues.remainingFreeSpace;
collectedFlexItemsValues.remainingFreeSpace =
YGFloatMax(0, minAvailableMainDim - occupiedSpaceByChildNodes);
} else {
collectedFlexItemsValues.remainingFreeSpace = 0;
}
}
int numberOfAutoMarginsOnCurrentLine = 0;
for (uint32_t i = startOfLineIndex;
i < collectedFlexItemsValues.endOfLineIndex;
i++) {
const YGNodeRef child = node->getChild(i);
if (child->getStyle().positionType() != YGPositionTypeAbsolute) {
if (child->marginLeadingValue(mainAxis).unit == YGUnitAuto) {
numberOfAutoMarginsOnCurrentLine++;
}
if (child->marginTrailingValue(mainAxis).unit == YGUnitAuto) {
numberOfAutoMarginsOnCurrentLine++;
}
}
}
// 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:
leadingMainDim = collectedFlexItemsValues.remainingFreeSpace / 2;
break;
case YGJustifyFlexEnd:
leadingMainDim = collectedFlexItemsValues.remainingFreeSpace;
break;
case YGJustifySpaceBetween:
if (collectedFlexItemsValues.itemsOnLine > 1) {
betweenMainDim =
YGFloatMax(collectedFlexItemsValues.remainingFreeSpace, 0) /
(collectedFlexItemsValues.itemsOnLine - 1);
} else {
betweenMainDim = 0;
}
break;
case YGJustifySpaceEvenly:
// Space is distributed evenly across all elements
betweenMainDim = collectedFlexItemsValues.remainingFreeSpace /
(collectedFlexItemsValues.itemsOnLine + 1);
leadingMainDim = betweenMainDim;
break;
case YGJustifySpaceAround:
// Space on the edges is half of the space between elements
betweenMainDim = collectedFlexItemsValues.remainingFreeSpace /
collectedFlexItemsValues.itemsOnLine;
leadingMainDim = betweenMainDim / 2;
break;
case YGJustifyFlexStart:
break;
}
}
collectedFlexItemsValues.mainDim =
leadingPaddingAndBorderMain + leadingMainDim;
collectedFlexItemsValues.crossDim = 0;
float maxAscentForCurrentLine = 0;
float maxDescentForCurrentLine = 0;
bool isNodeBaselineLayout = YGIsBaselineLayout(node);
for (uint32_t i = startOfLineIndex;
i < collectedFlexItemsValues.endOfLineIndex;
i++) {
const YGNodeRef child = node->getChild(i);
const YGStyle& childStyle = child->getStyle();
const YGLayout childLayout = child->getLayout();
if (childStyle.display() == YGDisplayNone) {
continue;
}
if (childStyle.positionType() == YGPositionTypeAbsolute &&
child->isLeadingPositionDefined(mainAxis)) {
if (performLayout) {
// In case the child is position absolute and has left/top being
// defined, we override the position to whatever the user said (and
// margin/border).
child->setLayoutPosition(
child->getLeadingPosition(mainAxis, availableInnerMainDim)
.unwrap() +
node->getLeadingBorder(mainAxis) +
child->getLeadingMargin(mainAxis, availableInnerWidth).unwrap(),
pos[mainAxis]);
}
} else {
// Now that we placed the element, we need to update the variables.
// We need to do that only for relative elements. Absolute elements do not
// take part in that phase.
if (childStyle.positionType() != YGPositionTypeAbsolute) {
if (child->marginLeadingValue(mainAxis).unit == YGUnitAuto) {
collectedFlexItemsValues.mainDim +=
collectedFlexItemsValues.remainingFreeSpace /
numberOfAutoMarginsOnCurrentLine;
}
if (performLayout) {
child->setLayoutPosition(
childLayout.position[pos[mainAxis]] +
collectedFlexItemsValues.mainDim,
pos[mainAxis]);
}
if (child->marginTrailingValue(mainAxis).unit == YGUnitAuto) {
collectedFlexItemsValues.mainDim +=
collectedFlexItemsValues.remainingFreeSpace /
numberOfAutoMarginsOnCurrentLine;
}
bool canSkipFlex =
!performLayout && measureModeCrossDim == YGMeasureModeExactly;
if (canSkipFlex) {
// 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
// YGNodeDimWithMargin.
collectedFlexItemsValues.mainDim += betweenMainDim +
child->getMarginForAxis(mainAxis, availableInnerWidth).unwrap() +
childLayout.computedFlexBasis.unwrap();
collectedFlexItemsValues.crossDim = availableInnerCrossDim;
} else {
// The main dimension is the sum of all the elements dimension plus
// the spacing.
collectedFlexItemsValues.mainDim += betweenMainDim +
YGNodeDimWithMargin(child, mainAxis, availableInnerWidth);
if (isNodeBaselineLayout) {
// If the child is baseline aligned then the cross dimension is
// calculated by adding maxAscent and maxDescent from the baseline.
const float ascent = YGBaseline(child, layoutContext) +
child
->getLeadingMargin(
YGFlexDirectionColumn, availableInnerWidth)
.unwrap();
const float descent =
child->getLayout().measuredDimensions[YGDimensionHeight] +
child
->getMarginForAxis(
YGFlexDirectionColumn, availableInnerWidth)
.unwrap() -
ascent;
maxAscentForCurrentLine =
YGFloatMax(maxAscentForCurrentLine, ascent);
maxDescentForCurrentLine =
YGFloatMax(maxDescentForCurrentLine, descent);
} else {
// The cross dimension is the max of the elements dimension since
// there can only be one element in that cross dimension in the case
// when the items are not baseline aligned
collectedFlexItemsValues.crossDim = YGFloatMax(
collectedFlexItemsValues.crossDim,
YGNodeDimWithMargin(child, crossAxis, availableInnerWidth));
}
}
} else if (performLayout) {
child->setLayoutPosition(
childLayout.position[pos[mainAxis]] +
node->getLeadingBorder(mainAxis) + leadingMainDim,
pos[mainAxis]);
}
}
}
collectedFlexItemsValues.mainDim += trailingPaddingAndBorderMain;
if (isNodeBaselineLayout) {
collectedFlexItemsValues.crossDim =
maxAscentForCurrentLine + maxDescentForCurrentLine;
}
}