Skip to content

Commit

Permalink
Support transforms forming containing blocks (#42191)
Browse files Browse the repository at this point in the history
Summary:

X-link: facebook/yoga#1539

React native supports transforms and if a node has a transform it will [form a containing block for absolute descendants regardless of position type](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block). So we need to pass that information into Yoga to ensure this happens.

The verbiage for the field "alwaysFormsContainingBlock" is very specific. In a vacuum a node cannot simply "form a containing block". It only forms a containing block in reference to a different node. This can be illustrated in a scenario where we have a static node that is a flex container which has 1 absolute child and 1 relative child. This static node will form a containing block for the relative child but not the absolute one. We could just pass the information on rather something has a transform or not but Yoga is not supposed to know about transforms in general. As a result we have a notion of "always" forming a containing block. Since Yoga is a flexbox spec, non-absolute nodes' containing blocks will ways be their parent. If we add something like a transform to a node then that will also apply to absolute nodes - hence we can say the node will **always** form a CB, no matter who is the descendant.

Changelog: [Internal]

Reviewed By: NickGerleman

Differential Revision: D52521160
  • Loading branch information
joevilches authored and facebook-github-bot committed Jan 9, 2024
1 parent b920b31 commit 9d0805a
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,11 @@ void YogaLayoutableShadowNode::updateYogaProps() {
}

yogaNode_.setStyle(styleResult);
if (getTraits().check(ShadowNodeTraits::ViewKind)) {
auto& viewProps = static_cast<const ViewProps&>(*props_);
YGNodeSetAlwaysFormsContainingBlock(
&yogaNode_, viewProps.transform != Transform::Identity());
}
}

/*static*/ yoga::Style YogaLayoutableShadowNode::applyAliasedProps(
Expand Down
6 changes: 6 additions & 0 deletions packages/react-native/ReactCommon/yoga/yoga/YGNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,12 @@ void YGNodeSetPrintFunc(YGNodeRef node, YGPrintFunc printFunc) {
resolveRef(node)->setPrintFunc(printFunc);
}

void YGNodeSetAlwaysFormsContainingBlock(
YGNodeRef node,
bool alwaysFormsContainingBlock) {
resolveRef(node)->setAlwaysFormsContainingBlock(alwaysFormsContainingBlock);
}

#ifdef DEBUG
void YGNodePrint(const YGNodeConstRef node, const YGPrintOptions options) {
yoga::print(resolveRef(node), scopedEnum(options));
Expand Down
11 changes: 11 additions & 0 deletions packages/react-native/ReactCommon/yoga/yoga/YGNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,17 @@ typedef void (*YGPrintFunc)(YGNodeConstRef node);
*/
YG_EXPORT void YGNodeSetPrintFunc(YGNodeRef node, YGPrintFunc printFunc);

/**
* Make it so that this node will always form a containing block for any
* descendant nodes. This is useful for when a node has a property outside of
* of Yoga that will form a containing block. For example, transforms or some of
* the others listed in
* https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block
*/
YG_EXPORT void YGNodeSetAlwaysFormsContainingBlock(
YGNodeRef node,
bool alwaysFormsContainingBlock);

/**
* Print a node to log output.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,9 @@ void layoutAbsoluteDescendants(
if (needsTrailingPosition(crossAxis)) {
setChildTrailingPosition(currentNode, child, crossAxis);
}
} else if (child->getStyle().positionType() == PositionType::Static) {
} else if (
child->getStyle().positionType() == PositionType::Static &&
!child->alwaysFormsContainingBlock()) {
const Direction childDirection =
child->resolveDirection(currentNodeDirection);
const float childMainOffsetFromContainingBlock =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2034,7 +2034,7 @@ static void calculateLayoutImpl(
// Let the containing block layout its absolute descendants. By definition
// the containing block will not be static unless we are at the root.
if (node->getStyle().positionType() != PositionType::Static ||
depth == 1) {
node->alwaysFormsContainingBlock() || depth == 1) {
layoutAbsoluteDescendants(
node,
node,
Expand Down
9 changes: 9 additions & 0 deletions packages/react-native/ReactCommon/yoga/yoga/node/Node.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ class YG_EXPORT Node : public ::YGNode {
return context_;
}

bool alwaysFormsContainingBlock() const {
return alwaysFormsContainingBlock_;
}

void print();

bool getHasNewLayout() const {
Expand Down Expand Up @@ -242,6 +246,10 @@ class YG_EXPORT Node : public ::YGNode {
context_ = context;
}

void setAlwaysFormsContainingBlock(bool alwaysFormsContainingBlock) {
alwaysFormsContainingBlock_ = alwaysFormsContainingBlock;
}

void setPrintFunc(YGPrintFunc printFunc) {
printFunc_ = printFunc;
}
Expand Down Expand Up @@ -369,6 +377,7 @@ class YG_EXPORT Node : public ::YGNode {
bool hasNewLayout_ : 1 = true;
bool isReferenceBaseline_ : 1 = false;
bool isDirty_ : 1 = false;
bool alwaysFormsContainingBlock_ : 1 = false;
NodeType nodeType_ : bitCount<NodeType>() = NodeType::Default;
void* context_ = nullptr;
YGMeasureFunc measureFunc_ = {nullptr};
Expand Down

0 comments on commit 9d0805a

Please sign in to comment.