Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
Expand Down Expand Up @@ -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;

// 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 @@ -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;

Expand Down Expand Up @@ -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,
}};
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
Loading