From 9d0805a9dc27b5e3af07c38f096dcf075572e5f7 Mon Sep 17 00:00:00 2001 From: Joe Vilches Date: Mon, 8 Jan 2024 18:43:58 -0800 Subject: [PATCH] Support transforms forming containing blocks (#42191) Summary: X-link: https://github.com/facebook/yoga/pull/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 --- .../components/view/YogaLayoutableShadowNode.cpp | 5 +++++ .../react-native/ReactCommon/yoga/yoga/YGNode.cpp | 6 ++++++ packages/react-native/ReactCommon/yoga/yoga/YGNode.h | 11 +++++++++++ .../yoga/yoga/algorithm/AbsoluteLayout.cpp | 4 +++- .../yoga/yoga/algorithm/CalculateLayout.cpp | 2 +- .../react-native/ReactCommon/yoga/yoga/node/Node.h | 9 +++++++++ 6 files changed, 35 insertions(+), 2 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp index 7757bd6695dbf6..91b62ffe0e6f42 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp @@ -383,6 +383,11 @@ void YogaLayoutableShadowNode::updateYogaProps() { } yogaNode_.setStyle(styleResult); + if (getTraits().check(ShadowNodeTraits::ViewKind)) { + auto& viewProps = static_cast(*props_); + YGNodeSetAlwaysFormsContainingBlock( + &yogaNode_, viewProps.transform != Transform::Identity()); + } } /*static*/ yoga::Style YogaLayoutableShadowNode::applyAliasedProps( diff --git a/packages/react-native/ReactCommon/yoga/yoga/YGNode.cpp b/packages/react-native/ReactCommon/yoga/yoga/YGNode.cpp index 50cfd98f20af3e..9f2c266682a169 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/YGNode.cpp +++ b/packages/react-native/ReactCommon/yoga/yoga/YGNode.cpp @@ -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)); diff --git a/packages/react-native/ReactCommon/yoga/yoga/YGNode.h b/packages/react-native/ReactCommon/yoga/yoga/YGNode.h index b267cde14808d7..58c966fdd291c5 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/YGNode.h +++ b/packages/react-native/ReactCommon/yoga/yoga/YGNode.h @@ -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. */ diff --git a/packages/react-native/ReactCommon/yoga/yoga/algorithm/AbsoluteLayout.cpp b/packages/react-native/ReactCommon/yoga/yoga/algorithm/AbsoluteLayout.cpp index 886aae133fa10f..230dfb5d25bb9e 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/algorithm/AbsoluteLayout.cpp +++ b/packages/react-native/ReactCommon/yoga/yoga/algorithm/AbsoluteLayout.cpp @@ -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 = diff --git a/packages/react-native/ReactCommon/yoga/yoga/algorithm/CalculateLayout.cpp b/packages/react-native/ReactCommon/yoga/yoga/algorithm/CalculateLayout.cpp index 17703605af2c19..6cf2962159db9d 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/algorithm/CalculateLayout.cpp +++ b/packages/react-native/ReactCommon/yoga/yoga/algorithm/CalculateLayout.cpp @@ -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, diff --git a/packages/react-native/ReactCommon/yoga/yoga/node/Node.h b/packages/react-native/ReactCommon/yoga/yoga/node/Node.h index 712f154e68a20a..b1971a697b9f76 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/node/Node.h +++ b/packages/react-native/ReactCommon/yoga/yoga/node/Node.h @@ -48,6 +48,10 @@ class YG_EXPORT Node : public ::YGNode { return context_; } + bool alwaysFormsContainingBlock() const { + return alwaysFormsContainingBlock_; + } + void print(); bool getHasNewLayout() const { @@ -242,6 +246,10 @@ class YG_EXPORT Node : public ::YGNode { context_ = context; } + void setAlwaysFormsContainingBlock(bool alwaysFormsContainingBlock) { + alwaysFormsContainingBlock_ = alwaysFormsContainingBlock; + } + void setPrintFunc(YGPrintFunc printFunc) { printFunc_ = printFunc; } @@ -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::Default; void* context_ = nullptr; YGMeasureFunc measureFunc_ = {nullptr};