Skip to content

Commit

Permalink
Avoid computing contentFrame when not using overflow: visible (facebo…
Browse files Browse the repository at this point in the history
…ok#39325)

Summary:

This diff extracts the logic to compute the content bounds of a shadow node (to compute its overflow insets) to a separate method. The new method is renamed as `getContentBounds` because layout metrics already have a method called `getContentFrame` that has a different meaning (the content box of the node, which excludes border and padding).

As a nice side-effect, we can now avoid executing this logic altogether if the node doesn't have overflow: visible (because we weren't assigning the result anywhere anyway).

NOTE: This method is made public because we need it to compute `scrollWidth` and `scrollHeight` in a following diff.

Changelog: [internal]

Reviewed By: NickGerleman

Differential Revision: D49020219
  • Loading branch information
rubennorte authored and facebook-github-bot committed Sep 8, 2023
1 parent a63b443 commit ef55f4d
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -666,24 +666,23 @@ void YogaLayoutableShadowNode::layoutTree(
}

static EdgeInsets calculateOverflowInset(
Rect containerFrame,
Rect contentFrame) {
auto size = containerFrame.size;
Rect contentFrame,
Rect contentBounds) {
auto size = contentFrame.size;
auto overflowInset = EdgeInsets{};
overflowInset.left = std::min(contentFrame.getMinX(), Float{0.0});
overflowInset.top = std::min(contentFrame.getMinY(), Float{0.0});
overflowInset.left = std::min(contentBounds.getMinX(), Float{0.0});
overflowInset.top = std::min(contentBounds.getMinY(), Float{0.0});
overflowInset.right =
-std::max(contentFrame.getMaxX() - size.width, Float{0.0});
-std::max(contentBounds.getMaxX() - size.width, Float{0.0});
overflowInset.bottom =
-std::max(contentFrame.getMaxY() - size.height, Float{0.0});
-std::max(contentBounds.getMaxY() - size.height, Float{0.0});
return overflowInset;
}

void YogaLayoutableShadowNode::layout(LayoutContext layoutContext) {
// Reading data from a dirtied node does not make sense.
react_native_assert(!yogaNode_.isDirty());

auto contentFrame = Rect{};
for (auto childYogaNode : yogaNode_.getChildren()) {
auto& childNode = shadowNodeFromContext(childYogaNode);

Expand Down Expand Up @@ -725,6 +724,33 @@ void YogaLayoutableShadowNode::layout(LayoutContext layoutContext) {
childNode.layout(layoutContext);
}
}
}

if (yogaNode_.getStyle().overflow() == YGOverflowVisible) {
// Note that the parent node's overflow layout is NOT affected by its
// transform matrix. That transform matrix is applied on the parent node as
// well as all of its child nodes, which won't cause changes on the
// overflowInset values. A special note on the scale transform -- the scaled
// layout may look like it's causing overflowInset changes, but it's purely
// cosmetic and will be handled by pixel density conversion logic later when
// render the view. The actual overflowInset value is not changed as if the
// transform is not happening here.
auto contentBounds = getContentBounds();
layoutMetrics_.overflowInset =
calculateOverflowInset(layoutMetrics_.frame, contentBounds);
} else {
layoutMetrics_.overflowInset = {};
}
}

Rect YogaLayoutableShadowNode::getContentBounds() const {
auto contentBounds = Rect{};

for (auto childYogaNode : yogaNode_.getChildren()) {
auto& childNode = shadowNodeFromContext(childYogaNode);

// Verifying that the Yoga node belongs to the ShadowNode.
react_native_assert(&childNode.yogaNode_ == childYogaNode);

auto layoutMetricsWithOverflowInset = childNode.getLayoutMetrics();
if (layoutMetricsWithOverflowInset.displayType != DisplayType::None) {
Expand All @@ -733,43 +759,30 @@ void YogaLayoutableShadowNode::layout(LayoutContext layoutContext) {
? viewChildNode->getConcreteProps().hitSlop
: EdgeInsets{};

// The contentFrame should always union with existing child node layout +
// The contentBounds should always union with existing child node layout +
// overflowInset. The transform may in a deferred animation and not
// applied yet.
contentFrame.unionInPlace(insetBy(
contentBounds.unionInPlace(insetBy(
layoutMetricsWithOverflowInset.frame,
layoutMetricsWithOverflowInset.overflowInset));
contentFrame.unionInPlace(
contentBounds.unionInPlace(
outsetBy(layoutMetricsWithOverflowInset.frame, hitSlop));

auto childTransform = childNode.getTransform();
if (childTransform != Transform::Identity()) {
// The child node's transform matrix will affect the parent node's
// contentFrame. We need to union with child node's after transform
// contentBounds. We need to union with child node's after transform
// layout here.
contentFrame.unionInPlace(insetBy(
contentBounds.unionInPlace(insetBy(
layoutMetricsWithOverflowInset.frame * childTransform,
layoutMetricsWithOverflowInset.overflowInset * childTransform));
contentFrame.unionInPlace(outsetBy(
contentBounds.unionInPlace(outsetBy(
layoutMetricsWithOverflowInset.frame * childTransform, hitSlop));
}
}
}

if (yogaNode_.getStyle().overflow() == YGOverflowVisible) {
// Note that the parent node's overflow layout is NOT affected by its
// transform matrix. That transform matrix is applied on the parent node as
// well as all of its child nodes, which won't cause changes on the
// overflowInset values. A special note on the scale transform -- the scaled
// layout may look like it's causing overflowInset changes, but it's purely
// cosmetic and will be handled by pixel density conversion logic later when
// render the view. The actual overflowInset value is not changed as if the
// transform is not happening here.
layoutMetrics_.overflowInset =
calculateOverflowInset(layoutMetrics_.frame, contentFrame);
} else {
layoutMetrics_.overflowInset = {};
}
return contentBounds;
}

#pragma mark - Yoga Connectors
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ class YogaLayoutableShadowNode : public LayoutableShadowNode {

void layout(LayoutContext layoutContext) override;

Rect getContentBounds() const;

protected:
/*
* Yoga config associated (only) with this particular node.
Expand Down

0 comments on commit ef55f4d

Please sign in to comment.