mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
fdd133e214
Summary: Changelog: [Internal] # Analysis Measure returns following values for `frame.y` when tapping item in bottom sheet. Fabric 412.33331298828125. Paper 49. In Paper, the frame.y returned is the position of tapped item in bottom sheet relative to the bottom sheet itself, which is correct. This can happen in both BottomSheet and Modal. # Why it happens? In [UIManager.getRelativeLayoutMetrics](https://our.intern.facebook.com/intern/diffusion/FBS/browse/master/xplat/js/react-native-github/ReactCommon/fabric/uimanager/UIManager.cpp?commit=a372cf516ba1245ad9462e68376ee759c118a884&lines=172-181) if `ancestorShadowNode` is nullptr we populate `ancestorShadowNode` with `rootShadowNode` for the surface. Problem is that BottomSheet that is presented, is not a separate surface. This means that we climb up the shadow node hierarchy all the way to root shadow node and keep adding offsets. Even though we should stop when we hit shadow node representing BottomSheet. # How could be this fixed? I think we should add a new shadow node trait that would mark node as root node. As we are traversing the shadow node tree upwards and adding offset of that node, once we hit a node that is "anchor", we would immediately stop the traversal. This solution is inspired by Paper where the node representing BottomSheet has a special flag which marks it as "root" node. accepttoship Reviewed By: JoshuaGross, mdvacca Differential Revision: D19640454 fbshipit-source-id: bde623b1f41a9745a41f0aada7221bf924fad453
234 lines
6.6 KiB
C++
234 lines
6.6 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#include "LayoutableShadowNode.h"
|
|
|
|
#include <react/core/LayoutConstraints.h>
|
|
#include <react/core/LayoutContext.h>
|
|
#include <react/core/LayoutMetrics.h>
|
|
#include <react/core/ShadowNode.h>
|
|
#include <react/debug/DebugStringConvertibleItem.h>
|
|
#include <react/graphics/conversions.h>
|
|
|
|
namespace facebook {
|
|
namespace react {
|
|
|
|
/*
|
|
* `shadowNode` might not be the newest revision of `ShadowNodeFamily`.
|
|
* This function looks at `parentNode`'s children and finds one that belongs
|
|
* to the same family as `shadowNode`.
|
|
*/
|
|
static ShadowNode const *findNewestChildInParent(
|
|
ShadowNode const &parentNode,
|
|
ShadowNode const &shadowNode) {
|
|
for (auto const &child : parentNode.getChildren()) {
|
|
if (ShadowNode::sameFamily(*child, shadowNode)) {
|
|
return child.get();
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static LayoutMetrics calculateOffsetForLayoutMetrics(
|
|
LayoutMetrics layoutMetrics,
|
|
ShadowNode::AncestorList const &ancestors,
|
|
LayoutableShadowNode::LayoutInspectingPolicy const &policy) {
|
|
// `AncestorList` starts from the given ancestor node and ends with the parent
|
|
// node. We iterate from parent node (reverse iteration) and stop before the
|
|
// given ancestor (rend() - 1).
|
|
for (auto it = ancestors.rbegin(); it != ancestors.rend() - 1; ++it) {
|
|
auto ¤tShadowNode = it->first.get();
|
|
|
|
if (currentShadowNode.getTraits().check(
|
|
ShadowNodeTraits::Trait::RootNodeKind)) {
|
|
break;
|
|
}
|
|
|
|
auto layoutableCurrentShadowNode =
|
|
dynamic_cast<LayoutableShadowNode const *>(¤tShadowNode);
|
|
|
|
if (!layoutableCurrentShadowNode) {
|
|
return EmptyLayoutMetrics;
|
|
}
|
|
|
|
auto origin = layoutableCurrentShadowNode->getLayoutMetrics().frame.origin;
|
|
|
|
if (policy.includeTransform || policy.includeScrollViewContentOffset) {
|
|
// The check for ScrollView will be implemented after we have
|
|
// a dedicated trait (part of `ShadowNodeTraits`) for that.
|
|
origin = origin * layoutableCurrentShadowNode->getTransform();
|
|
}
|
|
|
|
layoutMetrics.frame.origin += origin;
|
|
}
|
|
return layoutMetrics;
|
|
}
|
|
|
|
LayoutMetrics LayoutableShadowNode::getLayoutMetrics() const {
|
|
return layoutMetrics_;
|
|
}
|
|
|
|
bool LayoutableShadowNode::setLayoutMetrics(LayoutMetrics layoutMetrics) {
|
|
ensureUnsealed();
|
|
|
|
if (layoutMetrics_ == layoutMetrics) {
|
|
return false;
|
|
}
|
|
|
|
layoutMetrics_ = layoutMetrics;
|
|
return true;
|
|
}
|
|
|
|
bool LayoutableShadowNode::LayoutableShadowNode::isLayoutOnly() const {
|
|
return false;
|
|
}
|
|
|
|
Transform LayoutableShadowNode::getTransform() const {
|
|
return Transform::Identity();
|
|
}
|
|
|
|
LayoutMetrics LayoutableShadowNode::getRelativeLayoutMetrics(
|
|
LayoutableShadowNode const &ancestorLayoutableShadowNode,
|
|
LayoutInspectingPolicy policy) const {
|
|
auto &ancestorShadowNode =
|
|
dynamic_cast<ShadowNode const &>(ancestorLayoutableShadowNode);
|
|
auto &shadowNode = dynamic_cast<ShadowNode const &>(*this);
|
|
|
|
if (ShadowNode::sameFamily(shadowNode, ancestorShadowNode)) {
|
|
auto layoutMetrics = getLayoutMetrics();
|
|
layoutMetrics.frame.origin = {0, 0};
|
|
return layoutMetrics;
|
|
}
|
|
|
|
auto ancestors = shadowNode.getFamily().getAncestors(ancestorShadowNode);
|
|
|
|
if (ancestors.size() == 0) {
|
|
return EmptyLayoutMetrics;
|
|
}
|
|
|
|
auto newestChild =
|
|
findNewestChildInParent(ancestors.rbegin()->first.get(), shadowNode);
|
|
|
|
if (!newestChild) {
|
|
return EmptyLayoutMetrics;
|
|
}
|
|
|
|
auto layoutMetrics = dynamic_cast<LayoutableShadowNode const *>(newestChild)
|
|
->getLayoutMetrics();
|
|
|
|
return calculateOffsetForLayoutMetrics(layoutMetrics, ancestors, policy);
|
|
}
|
|
|
|
Size LayoutableShadowNode::measure(LayoutConstraints layoutConstraints) const {
|
|
return Size();
|
|
}
|
|
|
|
Float LayoutableShadowNode::firstBaseline(Size size) const {
|
|
return 0;
|
|
}
|
|
|
|
Float LayoutableShadowNode::lastBaseline(Size size) const {
|
|
return 0;
|
|
}
|
|
|
|
void LayoutableShadowNode::layout(LayoutContext layoutContext) {
|
|
layoutChildren(layoutContext);
|
|
|
|
for (auto child : getLayoutableChildNodes()) {
|
|
if (!child->getHasNewLayout()) {
|
|
continue;
|
|
}
|
|
|
|
child->ensureUnsealed();
|
|
child->setHasNewLayout(false);
|
|
|
|
auto childLayoutMetrics = child->getLayoutMetrics();
|
|
if (childLayoutMetrics.displayType == DisplayType::None) {
|
|
continue;
|
|
}
|
|
|
|
auto childLayoutContext = LayoutContext(layoutContext);
|
|
childLayoutContext.absolutePosition += childLayoutMetrics.frame.origin;
|
|
|
|
child->layout(layoutContext);
|
|
}
|
|
}
|
|
|
|
ShadowNode::Shared LayoutableShadowNode::findNodeAtPoint(
|
|
ShadowNode::Shared node,
|
|
Point point) {
|
|
auto layoutableShadowNode =
|
|
dynamic_cast<const LayoutableShadowNode *>(node.get());
|
|
|
|
if (!layoutableShadowNode) {
|
|
return nullptr;
|
|
}
|
|
auto frame = layoutableShadowNode->getLayoutMetrics().frame;
|
|
auto isPointInside = frame.containsPoint(point);
|
|
|
|
if (!isPointInside) {
|
|
return nullptr;
|
|
}
|
|
|
|
auto newPoint = point - frame.origin;
|
|
for (const auto &childShadowNode : node->getChildren()) {
|
|
auto hitView = findNodeAtPoint(childShadowNode, newPoint);
|
|
if (hitView) {
|
|
return hitView;
|
|
}
|
|
}
|
|
return isPointInside ? node : nullptr;
|
|
}
|
|
|
|
void LayoutableShadowNode::layoutChildren(LayoutContext layoutContext) {
|
|
// Default implementation does nothing.
|
|
}
|
|
|
|
#if RN_DEBUG_STRING_CONVERTIBLE
|
|
SharedDebugStringConvertibleList LayoutableShadowNode::getDebugProps() const {
|
|
auto list = SharedDebugStringConvertibleList{};
|
|
|
|
if (getHasNewLayout()) {
|
|
list.push_back(
|
|
std::make_shared<DebugStringConvertibleItem>("hasNewLayout"));
|
|
}
|
|
|
|
if (!getIsLayoutClean()) {
|
|
list.push_back(std::make_shared<DebugStringConvertibleItem>("dirty"));
|
|
}
|
|
|
|
auto layoutMetrics = getLayoutMetrics();
|
|
auto defaultLayoutMetrics = LayoutMetrics();
|
|
|
|
list.push_back(std::make_shared<DebugStringConvertibleItem>(
|
|
"frame", toString(layoutMetrics.frame)));
|
|
|
|
if (layoutMetrics.borderWidth != defaultLayoutMetrics.borderWidth) {
|
|
list.push_back(std::make_shared<DebugStringConvertibleItem>(
|
|
"borderWidth", toString(layoutMetrics.borderWidth)));
|
|
}
|
|
|
|
if (layoutMetrics.contentInsets != defaultLayoutMetrics.contentInsets) {
|
|
list.push_back(std::make_shared<DebugStringConvertibleItem>(
|
|
"contentInsets", toString(layoutMetrics.contentInsets)));
|
|
}
|
|
|
|
if (layoutMetrics.displayType == DisplayType::None) {
|
|
list.push_back(std::make_shared<DebugStringConvertibleItem>("hidden"));
|
|
}
|
|
|
|
if (layoutMetrics.layoutDirection == LayoutDirection::RightToLeft) {
|
|
list.push_back(std::make_shared<DebugStringConvertibleItem>("rtl"));
|
|
}
|
|
|
|
return list;
|
|
}
|
|
#endif
|
|
|
|
} // namespace react
|
|
} // namespace facebook
|