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:
Родитель
3be9f3ef2c
Коммит
154ce78972
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче