Take viewport offset into account in UIManager.measureInWindow

Summary:
Changelog: [Internal]

Fabric's UIManager.measureInWindow didn't take viewport's offset into account. This diff fixes it by including viewport's offset in `LayoutContext`.

Reviewed By: JoshuaGross

Differential Revision: D23021903

fbshipit-source-id: 9106a8789d66fe19d8cb0a9378ee5bc8f2c83005
This commit is contained in:
Samuel Susla 2020-08-10 12:49:36 -07:00 коммит произвёл Facebook GitHub Bot
Родитель 3be9f3ef2c
Коммит 154ce78972
14 изменённых файлов: 115 добавлений и 18 удалений

Просмотреть файл

@ -372,10 +372,12 @@
- (void)setSize:(CGSize)size
{
[self setMinimumSize:size maximumSize:size];
// `viewportOffset` is intentionally zero because `RCTSurface` ignores it.
// However, it is needed in `RCTFabricSurface`.
[self setMinimumSize:size maximumSize:size viewportOffset:CGPointZero];
}
- (void)setMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize
- (void)setMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize viewportOffset:(CGPoint)viewportOffset
{
{
std::lock_guard<std::mutex> lock(_mutex);
@ -401,6 +403,11 @@
});
}
- (void)setMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize
{
[self setMinimumSize:minimumSize maximumSize:maximumSize viewportOffset:CGPointZero];
}
- (CGSize)minimumSize
{
std::lock_guard<std::mutex> lock(_mutex);

Просмотреть файл

@ -31,6 +31,12 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)setMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize;
/**
* Sets `minimumSize`, `maximumSize`, and `viewportOffset` layout constraints for the Surface.
* `viewportOffset` is ignored in `RCTSurface` but used in `RCTFabricSurface`.
*/
- (void)setMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize viewportOffset:(CGPoint)viewportOffset;
#pragma mark - Dealing with UIView representation, the Main thread only access
/**

Просмотреть файл

@ -79,8 +79,9 @@ RCT_NOT_IMPLEMENTED(-(nullable instancetype)initWithCoder : (NSCoder *)coder)
RCTSurfaceMinimumSizeAndMaximumSizeFromSizeAndSizeMeasureMode(
self.bounds.size, _sizeMeasureMode, &minimumSize, &maximumSize);
CGRect windowFrame = [self.window convertRect:self.frame fromView:self.superview];
[_surface setMinimumSize:minimumSize maximumSize:maximumSize];
[_surface setMinimumSize:minimumSize maximumSize:maximumSize viewportOffset:windowFrame.origin];
}
- (CGSize)intrinsicContentSize

Просмотреть файл

@ -9,6 +9,7 @@
#import <react/renderer/components/root/RootComponentDescriptor.h>
#import <react/renderer/components/root/RootProps.h>
#import "RCTConversions.h"
using namespace facebook::react;

Просмотреть файл

@ -52,12 +52,13 @@ static inline LayoutConstraints RCTGetLayoutConstraintsForSize(CGSize minimumSiz
};
}
static inline LayoutContext RCTGetLayoutContext()
static inline LayoutContext RCTGetLayoutContext(CGPoint viewportOffset)
{
return {.pointScaleFactor = RCTScreenScale(),
.swapLeftAndRightInRTL =
[[RCTI18nUtil sharedInstance] isRTL] && [[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL],
.fontSizeMultiplier = RCTFontSizeMultiplier()};
.fontSizeMultiplier = RCTFontSizeMultiplier(),
.viewportOffset = RCTPointFromCGPoint(viewportOffset)};
}
static dispatch_queue_t RCTGetBackgroundQueue()
@ -202,7 +203,7 @@ static BackgroundExecutor RCTGetBackgroundExecutor()
if (!scheduler) {
return minimumSize;
}
LayoutContext layoutContext = RCTGetLayoutContext();
LayoutContext layoutContext = RCTGetLayoutContext(surface.viewportOffset);
LayoutConstraints layoutConstraints = RCTGetLayoutConstraintsForSize(minimumSize, maximumSize);
return [scheduler measureSurfaceWithLayoutConstraints:layoutConstraints
layoutContext:layoutContext
@ -216,7 +217,7 @@ static BackgroundExecutor RCTGetBackgroundExecutor()
return;
}
LayoutContext layoutContext = RCTGetLayoutContext();
LayoutContext layoutContext = RCTGetLayoutContext(surface.viewportOffset);
LayoutConstraints layoutConstraints = RCTGetLayoutConstraintsForSize(minimumSize, maximumSize);
[scheduler constraintSurfaceLayoutWithLayoutConstraints:layoutConstraints
layoutContext:layoutContext
@ -384,7 +385,7 @@ static BackgroundExecutor RCTGetBackgroundExecutor()
tag:surface.rootTag];
});
LayoutContext layoutContext = RCTGetLayoutContext();
LayoutContext layoutContext = RCTGetLayoutContext(surface.viewportOffset);
LayoutConstraints layoutConstraints = RCTGetLayoutConstraintsForSize(surface.minimumSize, surface.maximumSize);
@ -436,7 +437,7 @@ static BackgroundExecutor RCTGetBackgroundExecutor()
[_surfaceRegistry enumerateWithBlock:^(NSEnumerator<RCTFabricSurface *> *enumerator) {
for (RCTFabricSurface *surface in enumerator) {
LayoutContext layoutContext = RCTGetLayoutContext();
LayoutContext layoutContext = RCTGetLayoutContext(surface.viewportOffset);
LayoutConstraints layoutConstraints = RCTGetLayoutConstraintsForSize(surface.minimumSize, surface.maximumSize);

Просмотреть файл

@ -84,6 +84,12 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (atomic, assign, readonly) CGSize maximumSize;
/**
* Previously set `viewportOffset` layout constraint.
* Defaults to `{0, 0}`.
*/
@property (atomic, assign, readonly) CGPoint viewportOffset;
/**
* Simple shortcut to `-[RCTSurface setMinimumSize:size maximumSize:size]`.
*/

Просмотреть файл

@ -33,6 +33,7 @@ using namespace facebook::react;
NSDictionary *_properties;
CGSize _minimumSize;
CGSize _maximumSize;
CGPoint _viewportOffset;
CGSize _intrinsicSize;
// The Main thread only
@ -200,24 +201,31 @@ using namespace facebook::react;
- (void)setSize:(CGSize)size
{
[self setMinimumSize:size maximumSize:size];
[self setMinimumSize:size maximumSize:size viewportOffset:_viewportOffset];
}
- (void)setMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize
- (void)setMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize viewportOffset:(CGPoint)viewportOffset
{
{
std::lock_guard<std::mutex> lock(_mutex);
if (CGSizeEqualToSize(minimumSize, _minimumSize) && CGSizeEqualToSize(maximumSize, _maximumSize)) {
if (CGSizeEqualToSize(minimumSize, _minimumSize) && CGSizeEqualToSize(maximumSize, _maximumSize) &&
CGPointEqualToPoint(viewportOffset, _viewportOffset)) {
return;
}
_maximumSize = maximumSize;
_minimumSize = minimumSize;
_viewportOffset = viewportOffset;
}
[_surfacePresenter setMinimumSize:minimumSize maximumSize:maximumSize surface:self];
}
- (void)setMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize
{
[self setMinimumSize:minimumSize maximumSize:maximumSize viewportOffset:_viewportOffset];
}
- (CGSize)minimumSize
{
std::lock_guard<std::mutex> lock(_mutex);
@ -230,6 +238,12 @@ using namespace facebook::react;
return _maximumSize;
}
- (CGPoint)viewportOffset
{
std::lock_guard<std::mutex> lock(_mutex);
return _viewportOffset;
}
#pragma mark - intrinsicSize
- (void)setIntrinsicSize:(CGSize)intrinsicSize

Просмотреть файл

@ -33,6 +33,11 @@ bool RootShadowNode::layoutIfNeeded(
return true;
}
Transform RootShadowNode::getTransform() const {
auto viewportOffset = getConcreteProps().layoutContext.viewportOffset;
return Transform::Translate(viewportOffset.x, viewportOffset.y, 0);
}
RootShadowNode::Unshared RootShadowNode::clone(
LayoutConstraints const &layoutConstraints,
LayoutContext const &layoutContext) const {

Просмотреть файл

@ -34,6 +34,12 @@ class RootShadowNode final
using Shared = std::shared_ptr<RootShadowNode const>;
using Unshared = std::shared_ptr<RootShadowNode>;
static ShadowNodeTraits BaseTraits() {
auto traits = ConcreteViewShadowNode::BaseTraits();
traits.set(ShadowNodeTraits::Trait::RootNodeKind);
return traits;
}
/*
* Layouts the shadow tree if needed.
* Returns `false` if the three is already laid out.
@ -47,6 +53,8 @@ class RootShadowNode final
RootShadowNode::Unshared clone(
LayoutConstraints const &layoutConstraints,
LayoutContext const &layoutContext) const;
Transform getTransform() const override;
};
} // namespace react

Просмотреть файл

@ -55,6 +55,12 @@ struct LayoutContext {
* Multiplier used to change size of the font in surface.
*/
Float fontSizeMultiplier{1.0};
/*
* Viewport offset is offset of the React Native's root view.
* If React Native takes up entire screen, it will be {0, 0}.
*/
Point viewportOffset{};
};
} // namespace react

Просмотреть файл

@ -98,7 +98,12 @@ LayoutMetrics LayoutableShadowNode::computeRelativeLayoutMetrics(
currentFrame.origin = {0, 0};
}
if (policy.includeTransform) {
auto isRootNode = currentShadowNode->getTraits().check(
ShadowNodeTraits::Trait::RootNodeKind);
auto shouldApplyTransformation = (policy.includeTransform && !isRootNode) ||
(policy.includeViewportOffset && isRootNode);
if (shouldApplyTransformation) {
resultFrame.size = resultFrame.size * currentShadowNode->getTransform();
currentFrame = currentFrame * currentShadowNode->getTransform();
}

Просмотреть файл

@ -43,9 +43,9 @@ class LayoutableShadowNode : public ShadowNode {
static ShadowNodeTraits BaseTraits();
class LayoutInspectingPolicy final {
public:
struct LayoutInspectingPolicy {
bool includeTransform{true};
bool includeViewportOffset{false};
};
using UnsharedList = better::

Просмотреть файл

@ -376,9 +376,9 @@ TEST(
})
})
});
auto parentShadowNode = builder.build(element);
auto relativeLayoutMetrics = childShadowNode->getRelativeLayoutMetrics(*parentShadowNode, {});
// relativeLayoutMetrics do not include offsset of nodeAA_ because it is a
@ -386,3 +386,39 @@ TEST(
EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 10);
EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 10);
}
TEST(LayoutableShadowNodeTest, includeViewportOffset) {
auto builder = simpleComponentBuilder();
auto viewShadowNode = std::shared_ptr<ViewShadowNode>{};
// clang-format off
auto element =
Element<RootShadowNode>()
.props([] {
auto sharedProps = std::make_shared<RootProps>();
sharedProps->layoutContext.viewportOffset = {10, 20};
return sharedProps;
})
.children({
Element<ViewShadowNode>()
.reference(viewShadowNode)
});
// clang-format on
auto rootShadowNode = builder.build(element);
// `includeViewportOffset` has to work with `includeTransform` enabled and
// disabled.
auto layoutMetrics = viewShadowNode->getRelativeLayoutMetrics(
*rootShadowNode,
{/* includeTransform = */ false, /* includeViewportOffset = */ true});
EXPECT_EQ(layoutMetrics.frame.origin.x, 10);
EXPECT_EQ(layoutMetrics.frame.origin.y, 20);
layoutMetrics = viewShadowNode->getRelativeLayoutMetrics(
*rootShadowNode,
{/* includeTransform = */ true, /* includeViewportOffset = */ true});
EXPECT_EQ(layoutMetrics.frame.origin.x, 10);
EXPECT_EQ(layoutMetrics.frame.origin.y, 20);
}

Просмотреть файл

@ -601,7 +601,8 @@ jsi::Value UIManagerBinding::get(
auto layoutMetrics = uiManager->getRelativeLayoutMetrics(
*shadowNodeFromValue(runtime, arguments[0]),
nullptr,
{/* .includeTransform = */ true});
{/* .includeTransform = */ true,
/* includeViewportOffset = */ true});
auto onSuccessFunction =
arguments[1].getObject(runtime).getFunction(runtime);