Fixup margin: auto and justification behavior for overflowed containers (#44069)

Summary:
X-link: https://github.com/facebook/yoga/pull/1646

Pull Request resolved: https://github.com/facebook/react-native/pull/44069

Fixes https://github.com/facebook/yoga/issues/978

1. Don't allow auto margin spaces to become a negative length
2. Replicate fallback alignment behavior specified by box-alignment spec that we are using for align-content.

Reviewed By: joevilches

Differential Revision: D56091577

fbshipit-source-id: 3c02f81f969bb947cdc5c80b15faaa0b0d39c0c2
This commit is contained in:
Nick Gerleman
2024-04-17 21:50:55 -07:00
committed by Facebook GitHub Bot
parent b580a071ca
commit f21d9afe0b
4 changed files with 74 additions and 45 deletions
@@ -26,4 +26,47 @@ inline Align resolveChildAlignment(
return align;
}
/**
* Fallback alignment to use on overflow
* https://www.w3.org/TR/css-align-3/#distribution-values
*/
constexpr Align fallbackAlignment(Align align) {
switch (align) {
// Fallback to flex-start
case Align::SpaceBetween:
case Align::Stretch:
return Align::FlexStart;
// Fallback to safe center. TODO: This should be aligned to Start
// instead of FlexStart (for row-reverse containers)
case Align::SpaceAround:
case Align::SpaceEvenly:
return Align::FlexStart;
default:
return align;
}
}
/**
* Fallback alignment to use on overflow
* https://www.w3.org/TR/css-align-3/#distribution-values
*/
constexpr Justify fallbackAlignment(Justify align) {
switch (align) {
// Fallback to flex-start
case Justify::SpaceBetween:
// TODO: Support `justify-content: stretch`
// case Justify::Stretch:
return Justify::FlexStart;
// Fallback to safe center. TODO: This should be aligned to Start
// instead of FlexStart (for row-reverse containers)
case Justify::SpaceAround:
case Justify::SpaceEvenly:
return Justify::FlexStart;
default:
return align;
}
}
} // namespace facebook::yoga
@@ -959,27 +959,16 @@ static void justifyMainAxis(
}
}
int numberOfAutoMarginsOnCurrentLine = 0;
for (size_t i = startOfLineIndex; i < flexLine.endOfLineIndex; i++) {
auto child = node->getChild(i);
if (child->style().positionType() != PositionType::Absolute) {
if (child->style().flexStartMarginIsAuto(mainAxis, direction)) {
numberOfAutoMarginsOnCurrentLine++;
}
if (child->style().flexEndMarginIsAuto(mainAxis, direction)) {
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 = gap;
const Justify justifyContent = node->style().justifyContent();
const Justify justifyContent = flexLine.layout.remainingFreeSpace >= 0
? node->style().justifyContent()
: fallbackAlignment(node->style().justifyContent());
if (numberOfAutoMarginsOnCurrentLine == 0) {
if (flexLine.numberOfAutoMargins == 0) {
switch (justifyContent) {
case Justify::Center:
leadingMainDim = flexLine.layout.remainingFreeSpace / 2;
@@ -989,8 +978,7 @@ static void justifyMainAxis(
break;
case Justify::SpaceBetween:
if (flexLine.itemsInFlow.size() > 1) {
betweenMainDim +=
yoga::maxOrDefined(flexLine.layout.remainingFreeSpace, 0.0f) /
betweenMainDim += flexLine.layout.remainingFreeSpace /
static_cast<float>(flexLine.itemsInFlow.size() - 1);
}
break;
@@ -1043,9 +1031,10 @@ static void justifyMainAxis(
// We need to do that only for relative elements. Absolute elements do not
// take part in that phase.
if (childStyle.positionType() != PositionType::Absolute) {
if (child->style().flexStartMarginIsAuto(mainAxis, direction)) {
if (child->style().flexStartMarginIsAuto(mainAxis, direction) &&
flexLine.layout.remainingFreeSpace > 0.0f) {
flexLine.layout.mainDim += flexLine.layout.remainingFreeSpace /
static_cast<float>(numberOfAutoMarginsOnCurrentLine);
static_cast<float>(flexLine.numberOfAutoMargins);
}
if (performLayout) {
@@ -1059,9 +1048,10 @@ static void justifyMainAxis(
flexLine.layout.mainDim += betweenMainDim;
}
if (child->style().flexEndMarginIsAuto(mainAxis, direction)) {
if (child->style().flexEndMarginIsAuto(mainAxis, direction) &&
flexLine.layout.remainingFreeSpace > 0.0f) {
flexLine.layout.mainDim += flexLine.layout.remainingFreeSpace /
static_cast<float>(numberOfAutoMarginsOnCurrentLine);
static_cast<float>(flexLine.numberOfAutoMargins);
}
bool canSkipFlex =
!performLayout && sizingModeCrossDim == SizingMode::StretchFit;
@@ -1749,27 +1739,11 @@ static void calculateLayoutImpl(
const float remainingAlignContentDim = innerCrossDim - totalLineCrossDim;
// Apply fallback alignments on overflow
// https://www.w3.org/TR/css-align-3/#distribution-values
const auto appliedAlignContent =
remainingAlignContentDim >= 0 ? node->style().alignContent() : [&]() {
switch (node->style().alignContent()) {
// Fallback to flex-start
case Align::SpaceBetween:
case Align::Stretch:
return Align::FlexStart;
const auto alignContent = remainingAlignContentDim >= 0
? node->style().alignContent()
: fallbackAlignment(node->style().alignContent());
// Fallback to safe center. TODO: This should be aligned to Start
// instead of FlexStart (for row-reverse containers)
case Align::SpaceAround:
case Align::SpaceEvenly:
return Align::FlexStart;
default:
return node->style().alignContent();
}
}();
switch (appliedAlignContent) {
switch (alignContent) {
case Align::FlexEnd:
currentLead += remainingAlignContentDim;
break;
@@ -27,6 +27,7 @@ FlexLine calculateFlexLine(
float sizeConsumed = 0.0f;
float totalFlexGrowFactors = 0.0f;
float totalFlexShrinkScaledFactors = 0.0f;
size_t numberOfAutoMargins = 0;
size_t endOfLineIndex = startOfLineIndex;
size_t firstElementInLineIndex = startOfLineIndex;
@@ -49,6 +50,13 @@ FlexLine calculateFlexLine(
continue;
}
if (child->style().flexStartMarginIsAuto(mainAxis, ownerDirection)) {
numberOfAutoMargins++;
}
if (child->style().flexEndMarginIsAuto(mainAxis, ownerDirection)) {
numberOfAutoMargins++;
}
const bool isFirstElementInLine =
(endOfLineIndex - firstElementInLineIndex) == 0;
@@ -102,10 +110,11 @@ FlexLine calculateFlexLine(
}
return FlexLine{
std::move(itemsInFlow),
sizeConsumed,
endOfLineIndex,
FlexLineRunningLayout{
.itemsInFlow = std::move(itemsInFlow),
.sizeConsumed = sizeConsumed,
.endOfLineIndex = endOfLineIndex,
.numberOfAutoMargins = numberOfAutoMargins,
.layout = FlexLineRunningLayout{
totalFlexGrowFactors,
totalFlexShrinkScaledFactors,
}};
@@ -52,6 +52,9 @@ struct FlexLine {
// The index of the first item beyond the current line.
const size_t endOfLineIndex{0};
// Number of edges along the line flow with an auto margin.
const size_t numberOfAutoMargins{0};
// Layout information about the line computed in steps after line-breaking
FlexLineRunningLayout layout{};
};