Skip to content

Commit

Permalink
Fixup margin: auto and justification behavior for overflowed containers
Browse files Browse the repository at this point in the history
Summary:
Fixes facebook/yoga#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.

Differential Revision: D56091577
  • Loading branch information
NickGerleman authored and facebook-github-bot committed Apr 13, 2024
1 parent 1b152f6 commit 5baf25d
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 45 deletions.
43 changes: 43 additions & 0 deletions packages/react-native/ReactCommon/yoga/yoga/algorithm/Align.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -958,27 +958,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;
Expand All @@ -988,8 +977,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;
Expand Down Expand Up @@ -1042,9 +1030,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) {
Expand All @@ -1058,9 +1047,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;
Expand Down Expand Up @@ -1746,27 +1736,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;

// 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();
}
}();
const auto alignContent = remainingAlignContentDim >= 0
? node->style().alignContent()
: fallbackAlignment(node->style().alignContent());

switch (appliedAlignContent) {
switch (alignContent) {
case Align::FlexEnd:
currentLead += remainingAlignContentDim;
break;
Expand Down
17 changes: 13 additions & 4 deletions packages/react-native/ReactCommon/yoga/yoga/algorithm/FlexLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -48,6 +49,13 @@ FlexLine calculateFlexLine(
continue;
}

if (child->style().flexStartMarginIsAuto(mainAxis, ownerDirection)) {
numberOfAutoMargins++;
}
if (child->style().flexEndMarginIsAuto(mainAxis, ownerDirection)) {
numberOfAutoMargins++;
}

const bool isFirstElementInLine =
(endOfLineIndex - firstElementInLineIndex) == 0;

Expand Down Expand Up @@ -101,10 +109,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,
}};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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{};
};
Expand Down

0 comments on commit 5baf25d

Please sign in to comment.