Skip to content

Commit

Permalink
Use containing block to adjust absolute child position (#1472)
Browse files Browse the repository at this point in the history
Summary:
X-link: facebook/react-native#41490


This change has most of the logic needed for supporting `position: static`. We do two things here that fix a lot of the broken static test:

1) We pass in the containing node to `layoutAbsoluteChild` and use it to properly position the child in the case that insets are defined.
2) We rewrite the absolute child's position to be relative to it's parent in the event that insets are defined for that child (and thus it is positioned relative to its CB). Yoga's layout position has always be relative to parent, so I feel it is easier to just adjust the coordinates of a node to adhere to that design rather than change the consumers of yoga.

The "hard" part of this algorithm is determining how to iterate the offset from the containing block needed to do this translation described above. That is handled in `layoutAbsoluteDescendants`.

Differential Revision: D51224327
  • Loading branch information
Joe Vilches authored and facebook-github-bot committed Nov 16, 2023
1 parent 95c6b94 commit 8b69762
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 60 deletions.
180 changes: 120 additions & 60 deletions yoga/algorithm/CalculateLayout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,11 +318,12 @@ static void computeFlexBasisForChild(
}

static void layoutAbsoluteChild(
const yoga::Node* const containingNode,
const yoga::Node* const node,
yoga::Node* const child,
const float width,
const float containingBlockWidth,
const float containingBlockHeight,
const MeasureMode widthMode,
const float height,
const Direction direction,
LayoutData& layoutMarkerData,
const uint32_t depth,
Expand All @@ -337,48 +338,65 @@ static void layoutAbsoluteChild(
MeasureMode childWidthMeasureMode = MeasureMode::Undefined;
MeasureMode childHeightMeasureMode = MeasureMode::Undefined;

auto marginRow = child->getMarginForAxis(FlexDirection::Row, width);
auto marginColumn = child->getMarginForAxis(FlexDirection::Column, width);
auto marginRow =
child->getMarginForAxis(FlexDirection::Row, containingBlockWidth);
auto marginColumn =
child->getMarginForAxis(FlexDirection::Column, containingBlockWidth);

if (styleDefinesDimension(child, FlexDirection::Row, width)) {
if (styleDefinesDimension(child, FlexDirection::Row, containingBlockWidth)) {
childWidth =
yoga::resolveValue(child->getResolvedDimension(Dimension::Width), width)
yoga::resolveValue(
child->getResolvedDimension(Dimension::Width), containingBlockWidth)
.unwrap() +
marginRow;
} else {
// If the child doesn't have a specified width, compute the width based on
// the left/right offsets if they're defined.
if (child->isInlineStartPositionDefined(FlexDirection::Row, direction) &&
child->isInlineEndPositionDefined(FlexDirection::Row, direction)) {
childWidth = node->getLayout().measuredDimension(Dimension::Width) -
(node->getInlineStartBorder(FlexDirection::Row, direction) +
node->getInlineEndBorder(FlexDirection::Row, direction)) -
(child->getInlineStartPosition(FlexDirection::Row, direction, width) +
child->getInlineEndPosition(FlexDirection::Row, direction, width));
if (child->isFlexStartPositionDefined(FlexDirection::Row) &&
child->isFlexEndPositionDefined(FlexDirection::Row)) {
childWidth =
boundAxis(child, FlexDirection::Row, childWidth, width, width);
containingNode->getLayout().measuredDimension(Dimension::Width) -
(containingNode->getFlexStartBorder(FlexDirection::Row, direction) +
containingNode->getFlexEndBorder(FlexDirection::Row, direction)) -
(child->getFlexStartPosition(
FlexDirection::Row, containingBlockWidth) +
child->getFlexEndPosition(FlexDirection::Row, containingBlockWidth));
childWidth = boundAxis(
child,
FlexDirection::Row,
childWidth,
containingBlockWidth,
containingBlockWidth);
}
}

if (styleDefinesDimension(child, FlexDirection::Column, height)) {
if (styleDefinesDimension(
child, FlexDirection::Column, containingBlockHeight)) {
childHeight = yoga::resolveValue(
child->getResolvedDimension(Dimension::Height), height)
child->getResolvedDimension(Dimension::Height),
containingBlockHeight)
.unwrap() +
marginColumn;
} else {
// If the child doesn't have a specified height, compute the height based on
// the top/bottom offsets if they're defined.
if (child->isInlineStartPositionDefined(FlexDirection::Column, direction) &&
child->isInlineEndPositionDefined(FlexDirection::Column, direction)) {
childHeight = node->getLayout().measuredDimension(Dimension::Height) -
(node->getInlineStartBorder(FlexDirection::Column, direction) +
node->getInlineEndBorder(FlexDirection::Column, direction)) -
(child->getInlineStartPosition(
FlexDirection::Column, direction, height) +
child->getInlineEndPosition(
FlexDirection::Column, direction, height));
if (child->isFlexStartPositionDefined(FlexDirection::Column) &&
child->isFlexEndPositionDefined(FlexDirection::Column)) {
childHeight =
boundAxis(child, FlexDirection::Column, childHeight, height, width);
containingNode->getLayout().measuredDimension(Dimension::Height) -
(containingNode->getFlexStartBorder(
FlexDirection::Column, direction) +
containingNode->getFlexEndBorder(FlexDirection::Column, direction)) -
(child->getFlexStartPosition(
FlexDirection::Column, containingBlockHeight) +
child->getFlexEndPosition(
FlexDirection::Column, containingBlockHeight));
childHeight = boundAxis(
child,
FlexDirection::Column,
childHeight,
containingBlockHeight,
containingBlockWidth);
}
}

Expand Down Expand Up @@ -412,9 +430,9 @@ static void layoutAbsoluteChild(
// wrap to the size of its owner. This is the same behavior as many browsers
// implement.
if (!isMainAxisRow && yoga::isUndefined(childWidth) &&
widthMode != MeasureMode::Undefined && yoga::isDefined(width) &&
width > 0) {
childWidth = width;
widthMode != MeasureMode::Undefined &&
yoga::isDefined(containingBlockWidth) && containingBlockWidth > 0) {
childWidth = containingBlockWidth;
childWidthMeasureMode = MeasureMode::AtMost;
}

Expand All @@ -433,9 +451,9 @@ static void layoutAbsoluteChild(
depth,
generationCount);
childWidth = child->getLayout().measuredDimension(Dimension::Width) +
child->getMarginForAxis(FlexDirection::Row, width);
child->getMarginForAxis(FlexDirection::Row, containingBlockWidth);
childHeight = child->getLayout().measuredDimension(Dimension::Height) +
child->getMarginForAxis(FlexDirection::Column, width);
child->getMarginForAxis(FlexDirection::Column, containingBlockWidth);
}

calculateLayoutInternal(
Expand All @@ -456,11 +474,15 @@ static void layoutAbsoluteChild(
if (child->isFlexEndPositionDefined(mainAxis) &&
!child->isFlexStartPositionDefined(mainAxis)) {
child->setLayoutPosition(
node->getLayout().measuredDimension(dimension(mainAxis)) -
containingNode->getLayout().measuredDimension(dimension(mainAxis)) -
child->getLayout().measuredDimension(dimension(mainAxis)) -
node->getFlexEndBorder(mainAxis, direction) -
child->getFlexEndMargin(mainAxis, isMainAxisRow ? width : height) -
child->getFlexEndPosition(mainAxis, isMainAxisRow ? width : height),
containingNode->getFlexEndBorder(mainAxis, direction) -
child->getFlexEndMargin(
mainAxis,
isMainAxisRow ? containingBlockWidth : containingBlockHeight) -
child->getFlexEndPosition(
mainAxis,
isMainAxisRow ? containingBlockWidth : containingBlockHeight),
flexStartEdge(mainAxis));
} else if (
!child->isFlexStartPositionDefined(mainAxis) &&
Expand All @@ -484,25 +506,28 @@ static void layoutAbsoluteChild(
child->setLayoutPosition(
child->getFlexStartPosition(
mainAxis,
node->getLayout().measuredDimension(dimension(mainAxis))) +
node->getFlexStartBorder(mainAxis, direction) +
containingNode->getLayout().measuredDimension(
dimension(mainAxis))) +
containingNode->getFlexStartBorder(mainAxis, direction) +
child->getFlexStartMargin(
mainAxis,
node->getLayout().measuredDimension(dimension(mainAxis))),
isMainAxisRow ? containingBlockWidth : containingBlockHeight),
flexStartEdge(mainAxis));
}

if (child->isFlexEndPositionDefined(crossAxis) &&
!child->isFlexStartPositionDefined(crossAxis)) {
child->setLayoutPosition(
node->getLayout().measuredDimension(dimension(crossAxis)) -
containingNode->getLayout().measuredDimension(dimension(crossAxis)) -
child->getLayout().measuredDimension(dimension(crossAxis)) -
node->getFlexEndBorder(crossAxis, direction) -
child->getFlexEndMargin(crossAxis, isMainAxisRow ? height : width) -
containingNode->getFlexEndBorder(crossAxis, direction) -
child->getFlexEndMargin(
crossAxis,
isMainAxisRow ? containingBlockHeight : containingBlockWidth) -
child->getFlexEndPosition(
crossAxis, isMainAxisRow ? height : width),
crossAxis,
isMainAxisRow ? containingBlockHeight : containingBlockWidth),
flexStartEdge(crossAxis));

} else if (
!child->isFlexStartPositionDefined(crossAxis) &&
resolveChildAlignment(node, child) == Align::Center) {
Expand All @@ -520,49 +545,74 @@ static void layoutAbsoluteChild(
child->getLayout().measuredDimension(dimension(crossAxis))),
flexStartEdge(crossAxis));
} else if (
node->getConfig()->isExperimentalFeatureEnabled(
containingNode->getConfig()->isExperimentalFeatureEnabled(
ExperimentalFeature::AbsolutePercentageAgainstPaddingEdge) &&
child->isFlexStartPositionDefined(crossAxis)) {
child->setLayoutPosition(
child->getFlexStartPosition(
crossAxis,
node->getLayout().measuredDimension(dimension(crossAxis))) +
node->getFlexStartBorder(crossAxis, direction) +
containingNode->getLayout().measuredDimension(
dimension(crossAxis))) +
containingNode->getFlexStartBorder(crossAxis, direction) +
child->getFlexStartMargin(
crossAxis,
node->getLayout().measuredDimension(dimension(crossAxis))),
isMainAxisRow ? containingBlockHeight : containingBlockWidth),
flexStartEdge(crossAxis));
}
}

static void layoutAbsoluteDescendants(
yoga::Node* containingNode,
yoga::Node* currentNode,
MeasureMode widthMeasureMode,
Direction currentNodeDirection,
LayoutData& layoutMarkerData,
uint32_t currentDepth,
uint32_t generationCount,
float containingBlockWidth,
float containingBlockHeight) {
float currentNodeMainOffsetFromContainingBlock,
float currentNodeCrossOffsetFromContainingBlock) {
const FlexDirection mainAxis = resolveDirection(
currentNode->getStyle().flexDirection(), currentNodeDirection);
const FlexDirection crossAxis =
resolveCrossDirection(mainAxis, currentNodeDirection);
for (auto child : currentNode->getChildren()) {
if (child->getStyle().display() == Display::None) {
continue;
} else if (child->getStyle().positionType() == PositionType::Absolute) {
layoutAbsoluteChild(
containingNode,
currentNode,
child,
containingBlockWidth,
containingNode->getLayout().measuredDimension(Dimension::Width),
containingNode->getLayout().measuredDimension(Dimension::Height),
widthMeasureMode,
containingBlockHeight,
currentNodeDirection,
layoutMarkerData,
currentDepth,
generationCount);

const FlexDirection mainAxis = resolveDirection(
currentNode->getStyle().flexDirection(), currentNodeDirection);
const FlexDirection crossAxis =
resolveCrossDirection(mainAxis, currentNodeDirection);
const bool isMainAxisRow = isRow(mainAxis);
const bool mainInsetsDefined = isMainAxisRow
? child->getStyle().horizontalInsetsDefined()
: child->getStyle().verticalInsetsDefined();
const bool crossInsetsDefined = isMainAxisRow
? child->getStyle().verticalInsetsDefined()
: child->getStyle().horizontalInsetsDefined();

const float childMainOffsetFromParent = mainInsetsDefined
? (child->getLayout().position[flexStartEdge(mainAxis)] -
currentNodeMainOffsetFromContainingBlock)
: child->getLayout().position[flexStartEdge(mainAxis)];
const float childCrossOffsetFromParent = crossInsetsDefined
? (child->getLayout().position[flexStartEdge(crossAxis)] -
currentNodeCrossOffsetFromContainingBlock)
: child->getLayout().position[flexStartEdge(crossAxis)];

child->setLayoutPosition(
childMainOffsetFromParent, flexStartEdge(mainAxis));
child->setLayoutPosition(
childCrossOffsetFromParent, flexStartEdge(crossAxis));

if (needsTrailingPosition(mainAxis)) {
setChildTrailingPosition(currentNode, child, mainAxis);
}
Expand All @@ -572,15 +622,23 @@ static void layoutAbsoluteDescendants(
} else if (child->getStyle().positionType() == PositionType::Static) {
const Direction childDirection =
child->resolveDirection(currentNodeDirection);
const float childMainOffsetFromContainingBlock =
currentNodeMainOffsetFromContainingBlock +
child->getLayout().position[flexStartEdge(mainAxis)];
const float childCrossOffsetFromContainingBlock =
currentNodeCrossOffsetFromContainingBlock +
child->getLayout().position[flexStartEdge(crossAxis)];

layoutAbsoluteDescendants(
containingNode,
child,
widthMeasureMode,
childDirection,
layoutMarkerData,
currentDepth + 1,
generationCount,
containingBlockWidth,
containingBlockHeight);
childMainOffsetFromContainingBlock,
childCrossOffsetFromContainingBlock);
}
}
}
Expand Down Expand Up @@ -2371,14 +2429,15 @@ static void calculateLayoutImpl(
if (node->getStyle().positionType() != PositionType::Static ||
depth == 1) {
layoutAbsoluteDescendants(
node,
node,
isMainAxisRow ? measureModeMainDim : measureModeCrossDim,
direction,
layoutMarkerData,
depth,
generationCount,
node->getLayout().measuredDimension(Dimension::Width),
node->getLayout().measuredDimension(Dimension::Height));
0.0f,
0.0f);
}
} else {
for (auto child : node->getChildren()) {
Expand All @@ -2391,15 +2450,16 @@ static void calculateLayoutImpl(
ExperimentalFeature::AbsolutePercentageAgainstPaddingEdge);

layoutAbsoluteChild(
node,
node,
child,
absolutePercentageAgainstPaddingEdge
? node->getLayout().measuredDimension(Dimension::Width)
: availableInnerWidth,
isMainAxisRow ? measureModeMainDim : measureModeCrossDim,
absolutePercentageAgainstPaddingEdge
? node->getLayout().measuredDimension(Dimension::Height)
: availableInnerHeight,
isMainAxisRow ? measureModeMainDim : measureModeCrossDim,
direction,
layoutMarkerData,
depth,
Expand Down
16 changes: 16 additions & 0 deletions yoga/style/Style.h
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,22 @@ class YG_EXPORT Style {
}
}

bool horizontalInsetsDefined() const {
return position_[YGEdge::YGEdgeLeft].isDefined() ||
position_[YGEdge::YGEdgeRight].isDefined() ||
position_[YGEdge::YGEdgeAll].isDefined() ||
position_[YGEdge::YGEdgeHorizontal].isDefined() ||
position_[YGEdge::YGEdgeStart].isDefined() ||
position_[YGEdge::YGEdgeEnd].isDefined();
}

bool verticalInsetsDefined() const {
return position_[YGEdge::YGEdgeTop].isDefined() ||
position_[YGEdge::YGEdgeBottom].isDefined() ||
position_[YGEdge::YGEdgeAll].isDefined() ||
position_[YGEdge::YGEdgeVertical].isDefined();
}

bool operator==(const Style& other) const {
return flags == other.flags && inexactEquals(flex_, other.flex_) &&
inexactEquals(flexGrow_, other.flexGrow_) &&
Expand Down

0 comments on commit 8b69762

Please sign in to comment.