mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Create new option in LayoutInspectingPolicy to apply parent clipping to layout metrics (#37435)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/37435 We're implementing the `IntersectionObserver` API in React Native. That API reports the bounding rectangle of the root node and the target node (which we already implement in React Native via `LayoutableShadowNode::computeRelativeLayoutMetrics`), plus the intersection rectangle. This adds a new option in `LayoutInspectingPolicy` so we can get the layout metrics when clipping is applied (e.g.: if a parent node has `overflow: hidden` and we get the layout metrics of one of its children that extends beyond the limits of the parent, it would report only the rectangle that's visible for that node). Changelog: [internal] Reviewed By: sammy-SC Differential Revision: D45866245 fbshipit-source-id: ed3bfc2021e6b7819b8efea6fa3a81ed9d0bb181
This commit is contained in:
committed by
Facebook GitHub Bot
parent
64416d9503
commit
0abd065b15
@@ -179,7 +179,7 @@ LayoutMetrics LayoutableShadowNode::computeRelativeLayoutMetrics(
|
||||
resultFrame.origin = {0, 0};
|
||||
|
||||
// Step 3.
|
||||
// Iterating on a list of nodes computing compound offset.
|
||||
// Iterating on a list of nodes computing compound offset and size.
|
||||
auto size = shadowNodeList.size();
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
auto currentShadowNode =
|
||||
@@ -205,6 +205,7 @@ LayoutMetrics LayoutableShadowNode::computeRelativeLayoutMetrics(
|
||||
|
||||
auto isRootNode = currentShadowNode->getTraits().check(
|
||||
ShadowNodeTraits::Trait::RootNodeKind);
|
||||
|
||||
auto shouldApplyTransformation = (policy.includeTransform && !isRootNode) ||
|
||||
(policy.includeViewportOffset && isRootNode);
|
||||
|
||||
@@ -223,6 +224,16 @@ LayoutMetrics LayoutableShadowNode::computeRelativeLayoutMetrics(
|
||||
policy.includeTransform) {
|
||||
resultFrame.origin += currentShadowNode->getContentOriginOffset();
|
||||
}
|
||||
|
||||
if (policy.enableOverflowClipping) {
|
||||
auto overflowInset = currentShadowNode->getLayoutMetrics().overflowInset;
|
||||
auto overflowRect = insetBy(
|
||||
currentFrame * currentShadowNode->getTransform(), overflowInset);
|
||||
resultFrame = Rect::intersect(resultFrame, overflowRect);
|
||||
if (resultFrame.size.width == 0 && resultFrame.size.height == 0) {
|
||||
return EmptyLayoutMetrics;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
|
||||
@@ -46,6 +46,7 @@ class LayoutableShadowNode : public ShadowNode {
|
||||
struct LayoutInspectingPolicy {
|
||||
bool includeTransform{true};
|
||||
bool includeViewportOffset{false};
|
||||
bool enableOverflowClipping{false};
|
||||
};
|
||||
|
||||
using UnsharedList = butter::
|
||||
|
||||
+143
@@ -384,6 +384,149 @@ TEST(LayoutableShadowNodeTest, relativeLayoutMetricsOnTransformedParent) {
|
||||
EXPECT_EQ(relativeLayoutMetrics.frame.size.height, 25);
|
||||
}
|
||||
|
||||
/*
|
||||
* ┌────────────────────────┐
|
||||
* │<Root> │
|
||||
* │ ┌─────────────────────┐│
|
||||
* │ │ <View> ││
|
||||
* │ │ ┌──────────────┐││
|
||||
* │ │ │<View> │││
|
||||
* │ │ │ ┌──────────┐│││
|
||||
* │ │ │ │<View> ││││
|
||||
* │ │ │ │ ││││
|
||||
* │ │ │ │ ││││
|
||||
* │ │ │ └──────────┘│││
|
||||
* │ │ └──────────────┘││
|
||||
* │ └─────────────────────┘│
|
||||
* └────────────────────────┘
|
||||
*/
|
||||
TEST(LayoutableShadowNodeTest, relativeLayoutMetricsOnParentWithClipping) {
|
||||
auto builder = simpleComponentBuilder();
|
||||
|
||||
auto childShadowNode = std::shared_ptr<ViewShadowNode>{};
|
||||
// clang-format off
|
||||
auto element =
|
||||
Element<RootShadowNode>()
|
||||
.finalize([](RootShadowNode &shadowNode){
|
||||
auto layoutMetrics = EmptyLayoutMetrics;
|
||||
layoutMetrics.frame.size = {900, 900};
|
||||
shadowNode.setLayoutMetrics(layoutMetrics);
|
||||
})
|
||||
.children({
|
||||
Element<ViewShadowNode>()
|
||||
.finalize([](ViewShadowNode &shadowNode){
|
||||
auto layoutMetrics = EmptyLayoutMetrics;
|
||||
layoutMetrics.frame.origin = {10, 10};
|
||||
layoutMetrics.frame.size = {100, 100};
|
||||
shadowNode.setLayoutMetrics(layoutMetrics);
|
||||
})
|
||||
.children({
|
||||
Element<ViewShadowNode>()
|
||||
.reference(childShadowNode)
|
||||
.finalize([](ViewShadowNode &shadowNode){
|
||||
auto layoutMetrics = EmptyLayoutMetrics;
|
||||
layoutMetrics.frame.origin = {10, 10};
|
||||
layoutMetrics.frame.size = {150, 150};
|
||||
shadowNode.setLayoutMetrics(layoutMetrics);
|
||||
})
|
||||
})
|
||||
});
|
||||
// clang-format on
|
||||
|
||||
auto parentShadowNode = builder.build(element);
|
||||
|
||||
auto relativeLayoutMetrics =
|
||||
LayoutableShadowNode::computeRelativeLayoutMetrics(
|
||||
childShadowNode->getFamily(),
|
||||
*parentShadowNode,
|
||||
{
|
||||
/* includeTransform = */ true,
|
||||
/* includeViewportOffset = */ false,
|
||||
/* enableOverflowClipping = */ true,
|
||||
});
|
||||
|
||||
EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 20);
|
||||
EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 20);
|
||||
|
||||
EXPECT_EQ(relativeLayoutMetrics.frame.size.width, 90);
|
||||
EXPECT_EQ(relativeLayoutMetrics.frame.size.height, 90);
|
||||
}
|
||||
|
||||
/*
|
||||
* ┌────────────────────────┐
|
||||
* │<Root> │
|
||||
* │ ┌─────────────────────┐│
|
||||
* │ │ <View> ││
|
||||
* │ │ ┌──────────────┐││
|
||||
* │ │ │<View> │││
|
||||
* │ │ │ ┌──────────┐│││
|
||||
* │ │ │ │<View> ││││
|
||||
* │ │ │ │ ││││
|
||||
* │ │ │ │ ││││
|
||||
* │ │ │ └──────────┘│││
|
||||
* │ │ └──────────────┘││
|
||||
* │ └─────────────────────┘│
|
||||
* └────────────────────────┘
|
||||
*/
|
||||
TEST(
|
||||
LayoutableShadowNodeTest,
|
||||
relativeLayoutMetricsOnTransformedParentWithClipping) {
|
||||
auto builder = simpleComponentBuilder();
|
||||
|
||||
auto childShadowNode = std::shared_ptr<ViewShadowNode>{};
|
||||
// clang-format off
|
||||
auto element =
|
||||
Element<RootShadowNode>()
|
||||
.finalize([](RootShadowNode &shadowNode){
|
||||
auto layoutMetrics = EmptyLayoutMetrics;
|
||||
layoutMetrics.frame.size = {900, 900};
|
||||
shadowNode.setLayoutMetrics(layoutMetrics);
|
||||
})
|
||||
.children({
|
||||
Element<ViewShadowNode>()
|
||||
.props([] {
|
||||
auto sharedProps = std::make_shared<ViewShadowNodeProps>();
|
||||
sharedProps->transform = Transform::Scale(0.5, 0.5, 1);
|
||||
return sharedProps;
|
||||
})
|
||||
.finalize([](ViewShadowNode &shadowNode){
|
||||
auto layoutMetrics = EmptyLayoutMetrics;
|
||||
layoutMetrics.frame.origin = {10, 10};
|
||||
layoutMetrics.frame.size = {100, 100};
|
||||
shadowNode.setLayoutMetrics(layoutMetrics);
|
||||
})
|
||||
.children({
|
||||
Element<ViewShadowNode>()
|
||||
.reference(childShadowNode)
|
||||
.finalize([](ViewShadowNode &shadowNode){
|
||||
auto layoutMetrics = EmptyLayoutMetrics;
|
||||
layoutMetrics.frame.origin = {10, 10};
|
||||
layoutMetrics.frame.size = {150, 150};
|
||||
shadowNode.setLayoutMetrics(layoutMetrics);
|
||||
})
|
||||
})
|
||||
});
|
||||
// clang-format on
|
||||
|
||||
auto parentShadowNode = builder.build(element);
|
||||
|
||||
auto relativeLayoutMetrics =
|
||||
LayoutableShadowNode::computeRelativeLayoutMetrics(
|
||||
childShadowNode->getFamily(),
|
||||
*parentShadowNode,
|
||||
{
|
||||
/* includeTransform = */ true,
|
||||
/* includeViewportOffset = */ false,
|
||||
/* enableOverflowClipping = */ true,
|
||||
});
|
||||
|
||||
EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 40);
|
||||
EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 40);
|
||||
|
||||
EXPECT_EQ(relativeLayoutMetrics.frame.size.width, 45);
|
||||
EXPECT_EQ(relativeLayoutMetrics.frame.size.height, 45);
|
||||
}
|
||||
|
||||
/*
|
||||
* ┌────────────────┐
|
||||
* │<View> │
|
||||
|
||||
@@ -69,6 +69,24 @@ struct Rect {
|
||||
point.y <= (origin.y + size.height);
|
||||
}
|
||||
|
||||
static Rect intersect(Rect const &rect1, Rect const &rect2) {
|
||||
Float x1 = std::max(rect1.origin.x, rect2.origin.x);
|
||||
Float y1 = std::max(rect1.origin.y, rect2.origin.y);
|
||||
Float x2 = std::min(
|
||||
rect1.origin.x + rect1.size.width, rect2.origin.x + rect2.size.width);
|
||||
Float y2 = std::min(
|
||||
rect1.origin.y + rect1.size.height, rect2.origin.y + rect2.size.height);
|
||||
|
||||
Float intersectionWidth = x2 - x1;
|
||||
Float intersectionHeight = y2 - y1;
|
||||
|
||||
if (intersectionWidth < 0 || intersectionHeight < 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {{x1, y1}, {intersectionWidth, intersectionHeight}};
|
||||
}
|
||||
|
||||
static Rect boundingRect(
|
||||
Point const &a,
|
||||
Point const &b,
|
||||
|
||||
Reference in New Issue
Block a user