diff --git a/core/renderer/dom/fiber/fiber_element.cc b/core/renderer/dom/fiber/fiber_element.cc index de6543d7f6..bde3ae796f 100644 --- a/core/renderer/dom/fiber/fiber_element.cc +++ b/core/renderer/dom/fiber/fiber_element.cc @@ -5220,6 +5220,11 @@ void Element::UpdateDynamicElementStyleRecursively(uint32_t style, } } + if (IsOverlay() && (style & DynamicCSSStylesManager::kUpdateScreenMetrics)) { + inner_force_update |= true; + MarkLayoutDirty(); + } + if ((dynamic_style_flags_ > 0 || inner_force_update) && !is_wrapper()) { // Style could never be "all" here. NotifyUnitValuesUpdatedToAnimation(style); diff --git a/core/renderer/ui_wrapper/painting/ios/painting_context_darwin.mm b/core/renderer/ui_wrapper/painting/ios/painting_context_darwin.mm index 10216ba321..502f2ce0be 100644 --- a/core/renderer/ui_wrapper/painting/ios/painting_context_darwin.mm +++ b/core/renderer/ui_wrapper/painting/ios/painting_context_darwin.mm @@ -484,6 +484,8 @@ void RunOnMainThreadSync(dispatch_block_t block) { std::vector PaintingContextDarwin::getWindowSize(int id) { std::vector res; + // TODO(xiamengfei.moonface): [ResizableWindowSize] Check whether this should use window or + // physical screen metrics. CGSize size = UIScreen.mainScreen.bounds.size; res.push_back(size.width); res.push_back(size.height); @@ -532,6 +534,8 @@ void RunOnMainThreadSync(dispatch_block_t block) { LynxUI* ui = [uiOwner_ findUIBySign:id]; if (ui != NULL) { CGRect re = [ui getRectToWindow]; + // TODO(xiamengfei.moonface): [ResizableWindowScale] Check whether this should use window or + // physical screen metrics. int scale = UIScreen.mainScreen.scale; res.push_back(re.origin.x * scale); res.push_back(re.origin.y * scale); diff --git a/core/renderer/ui_wrapper/painting/ios/platform_renderer_context_darwin.mm b/core/renderer/ui_wrapper/painting/ios/platform_renderer_context_darwin.mm index 876dbb9895..58d36b52b1 100644 --- a/core/renderer/ui_wrapper/painting/ios/platform_renderer_context_darwin.mm +++ b/core/renderer/ui_wrapper/painting/ios/platform_renderer_context_darwin.mm @@ -30,6 +30,8 @@ } CGSize PlatformRendererContextDarwin::GetScreenSize() { + // TODO(xiamengfei.moonface): [ResizableWindowSize] Check whether this should use window or + // physical screen metrics. CGSize size = UIScreen.mainScreen.bounds.size; return size; } diff --git a/platform/darwin/common/lynx/LynxScreenMetrics.m b/platform/darwin/common/lynx/LynxScreenMetrics.m index 577b36b8d2..d1777de85e 100644 --- a/platform/darwin/common/lynx/LynxScreenMetrics.m +++ b/platform/darwin/common/lynx/LynxScreenMetrics.m @@ -32,12 +32,22 @@ - (instancetype)init { - (instancetype)initWithScreenSize:(CGSize)screenSize scale:(CGFloat)scale { if (self = [self init]) { - [self setLynxScreenSize:screenSize]; + _screenSize = screenSize; _scale = scale; } return self; } +// Keep the legacy setters for source and binary compatibility. New code should create immutable +// snapshots with initWithScreenSize:scale: instead. +- (void)setScreenSize:(CGSize)screenSize { + _screenSize = screenSize; +} + +- (void)setScale:(CGFloat)scale { + _scale = scale; +} + - (void)setLynxScreenSize:(CGSize)screenSize { _screenSize = screenSize; } diff --git a/platform/darwin/common/lynx/public/LynxScreenMetrics.h b/platform/darwin/common/lynx/public/LynxScreenMetrics.h index dd250b1755..68a1289cee 100644 --- a/platform/darwin/common/lynx/public/LynxScreenMetrics.h +++ b/platform/darwin/common/lynx/public/LynxScreenMetrics.h @@ -10,12 +10,17 @@ NS_ASSUME_NONNULL_BEGIN @interface LynxScreenMetrics : NSObject -@property(nonatomic, assign, readwrite) CGSize screenSize; -@property(nonatomic, assign, readwrite) CGFloat scale; +@property(nonatomic, assign, readonly) CGSize screenSize; +@property(nonatomic, assign, readonly) CGFloat scale; + (LynxScreenMetrics*)getDefaultLynxScreenMetrics; - (instancetype)initWithScreenSize:(CGSize)screenSize scale:(CGFloat)scale; -- (void)setLynxScreenSize:(CGSize)screenSize; +- (void)setScreenSize:(CGSize)screenSize + __attribute__((deprecated("Use initWithScreenSize:scale: to create an immutable snapshot."))); +- (void)setScale:(CGFloat)scale + __attribute__((deprecated("Use initWithScreenSize:scale: to create an immutable snapshot."))); +- (void)setLynxScreenSize:(CGSize)screenSize + __attribute__((deprecated("Use initWithScreenSize:scale: to create an immutable snapshot."))); @end diff --git a/platform/darwin/common/lynx/public/LynxTemplateRenderProtocol.h b/platform/darwin/common/lynx/public/LynxTemplateRenderProtocol.h index 44138b91c5..97d56fbdfb 100644 --- a/platform/darwin/common/lynx/public/LynxTemplateRenderProtocol.h +++ b/platform/darwin/common/lynx/public/LynxTemplateRenderProtocol.h @@ -22,6 +22,7 @@ NS_ASSUME_NONNULL_BEGIN @class LynxTemplateData; @class LynxUpdateMeta; @class LynxContext; +@class LynxScreenMetrics; @protocol LynxModule; @protocol LynxTemplateRenderProtocol @@ -164,6 +165,10 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)updateScreenMetricsWithWidth:(CGFloat)width height:(CGFloat)height; +- (void)updateScreenMetricsWithWidth:(CGFloat)width height:(CGFloat)height scale:(CGFloat)scale; + +- (void)updateScreenMetrics:(LynxScreenMetrics*)screenMetrics; + - (void)updateFontScale:(CGFloat)scale; - (void)updateColorScheme:(LynxColorScheme)scheme; diff --git a/platform/darwin/ios/api/lynx_ios.api b/platform/darwin/ios/api/lynx_ios.api index 6404d1a0a5..2d23838819 100644 --- a/platform/darwin/ios/api/lynx_ios.api +++ b/platform/darwin/ios/api/lynx_ios.api @@ -633,6 +633,7 @@ public class LynxBaseConfigurator : NSObject { public id LynxBaseConfigurator::templateResourceFetcher templateResourceFetcher; public BOOL LynxBaseConfigurator::enableFetchUIImage enableFetchUIImage; public CGSize LynxBaseConfigurator::screenSize screenSize; + public CGFloat LynxBaseConfigurator::screenScale screenScale; public BOOL LynxBaseConfigurator::debuggable debuggable; public BOOL LynxBaseConfigurator::enablePreUpdateData enablePreUpdateData; public LynxBackgroundJsRuntimeType LynxBaseConfigurator::backgroundJsRuntimeType backgroundJsRuntimeType; @@ -3481,7 +3482,9 @@ public class LynxScreenMetrics : NSObject { public CGFloat LynxScreenMetrics::scale scale; public LynxScreenMetrics * LynxScreenMetrics::getDefaultLynxScreenMetrics(); public instancetype LynxScreenMetrics::initWithScreenSize:scale:(CGSize screenSize,[scale] CGFloat scale); - public void LynxScreenMetrics::setLynxScreenSize:(CGSize screenSize); + public void LynxScreenMetrics::setScreenSize:((deprecated("Use initWithScreenSize:scale: to create an immutable snapshot.")) __attribute__); + public void LynxScreenMetrics::setScale:((deprecated("Use initWithScreenSize:scale: to create an immutable snapshot.")) __attribute__); + public void LynxScreenMetrics::setLynxScreenSize:((deprecated("Use initWithScreenSize:scale: to create an immutable snapshot.")) __attribute__); } public class LynxScrollEventManager : NSObject { @@ -4066,6 +4069,8 @@ public protocol LynxTemplateRenderProtocol-p : { public void LynxTemplateRenderProtocol-p::updateViewport(); public void LynxTemplateRenderProtocol-p::updateViewport:(BOOL needLayout); public void LynxTemplateRenderProtocol-p::updateScreenMetricsWithWidth:height:(CGFloat width,[height] CGFloat height); + public void LynxTemplateRenderProtocol-p::updateScreenMetricsWithWidth:height:scale:(CGFloat width,[height] CGFloat height,[scale] CGFloat scale); + public void LynxTemplateRenderProtocol-p::updateScreenMetrics:(LynxScreenMetrics *screenMetrics); public void LynxTemplateRenderProtocol-p::updateFontScale:(CGFloat scale); public void LynxTemplateRenderProtocol-p::updateColorScheme:(LynxColorScheme scheme); public void LynxTemplateRenderProtocol-p::pauseRootLayoutAnimation(); @@ -4747,6 +4752,7 @@ public class LynxUIContext : NSObject { public int64_t LynxUIContext::shellPtr shellPtr; public id LynxUIContext::fetcher fetcher; public LynxScreenMetrics* LynxUIContext::screenMetrics screenMetrics; + public LynxViewportMetrics* LynxUIContext::viewportMetrics viewportMetrics; public LynxUIIntersectionObserverManager* LynxUIContext::intersectionManager intersectionManager; public LynxUIExposure* LynxUIContext::uiExposure uiExposure; public NSDictionary* LynxUIContext::keyframesDict keyframesDict; @@ -4813,6 +4819,8 @@ public class LynxUIContext : NSObject { public void LynxUIContext::resumeExposure(); public void LynxUIContext::findShadowNodeAndRunTask:task:(NSInteger sign,[task] void(^ task)(LynxShadowNode *)); public nullable id< LynxResourceFetcher > LynxUIContext::getGenericResourceFetcher(); + public void LynxUIContext()::updateScreenMetrics:(LynxScreenMetrics *screenMetrics); + public void LynxUIContext()::updateViewportMetrics:(nullable LynxViewportMetrics *viewportMetrics); public void LynxUIContext()::setUIConfig:(id< LUIConfig > config); public void LynxUIContext()::setDefaultOverflowVisible:(BOOL enable); public void LynxUIContext()::setDefaultImplicitAnimation:(BOOL enable); @@ -5318,6 +5326,8 @@ public protocol LynxUIRendererProtocol-p : { public void LynxUIRendererProtocol-p::setImageFetcherInUIOwner:(id< LynxImageFetcher > imageFetcher); public void LynxUIRendererProtocol-p::setResourceFetcherInUIOwner:(id< LynxResourceFetcher > resourceFetcher); public void LynxUIRendererProtocol-p::updateScreenWidth:height:(CGFloat width,[height] CGFloat height); + public void LynxUIRendererProtocol-p::updateScreenMetrics:(LynxScreenMetrics *screenMetrics); + public void LynxUIRendererProtocol-p::updateViewportMetrics:(nullable LynxViewportMetrics *viewportMetrics); public void LynxUIRendererProtocol-p::pauseRootLayoutAnimation(); public void LynxUIRendererProtocol-p::resumeRootLayoutAnimation(); public void LynxUIRendererProtocol-p::restartAnimation(); @@ -5539,6 +5549,8 @@ public class LynxView : UIView, , , _Nullable)); public void LynxView::unSubscribeSessionStorage:withId:(nonnull NSString *key,[withId] double callbackId); public void LynxView::updateScreenMetricsWithWidth:height:(CGFloat width,[height] CGFloat height); + public void LynxView::updateScreenMetricsWithWidth:height:scale:(CGFloat width,[height] CGFloat height,[scale] CGFloat scale); + public void LynxView::updateScreenMetrics:(nonnull LynxScreenMetrics *screenMetrics); public void LynxView::updateFontScale:(CGFloat scale); public void LynxView::getLynxElementRoot:(void(^_Nullable callback)(LynxElement *_Nullable root)); public void LynxView::updateColorScheme:(LynxColorScheme scheme); @@ -5696,6 +5708,13 @@ public protocol LynxViewLifecycleV2-p : , { public BOOL LynxViewVisibleHelper-p::IsViewVisible(); } diff --git a/platform/darwin/ios/lynx/BUILD.gn b/platform/darwin/ios/lynx/BUILD.gn index 62d60296da..adc7a850f0 100644 --- a/platform/darwin/ios/lynx/BUILD.gn +++ b/platform/darwin/ios/lynx/BUILD.gn @@ -158,6 +158,7 @@ subspec_target("lynx") { "LynxViewBuilder+Internal.h", "LynxViewBuilder.mm", "LynxViewGroup.mm", + "LynxViewportMetrics.m", "LynxWasmFuncRegistry.mm", "PaintingContextProxy.h", "PaintingContextProxy.mm", @@ -480,6 +481,7 @@ subspec_target("UnitTests") { "../../common/lynx/performance/memory/LynxMemoryUsageQueryUnitTest.m", "LynxElementUnitTest.m", "LynxModuleManagerDarwinUnitTest.mm", + "LynxRuntimeMetricsUnitTest.m", "LynxTemplateRenderMemoryUsageUnitTest.mm", "LynxUnitTestUtils.h", "LynxUnitTestUtils.mm", diff --git a/platform/darwin/ios/lynx/LynxBaseConfigurator.mm b/platform/darwin/ios/lynx/LynxBaseConfigurator.mm index 1e150e8078..70b6ffebeb 100644 --- a/platform/darwin/ios/lynx/LynxBaseConfigurator.mm +++ b/platform/darwin/ios/lynx/LynxBaseConfigurator.mm @@ -12,11 +12,34 @@ #import "LynxTraceEventDef.h" #import "LynxUIRendererCreator.h" +#include + @implementation LynxBaseConfigurator { LynxEmbeddedMode _embeddedMode; NSMutableDictionary* _builderRegisteredAliasFontMap; } +- (void)setScreenSize:(CGSize)screenSize { + if (!CGSizeEqualToSize(screenSize, CGSizeZero) && + (!std::isfinite(screenSize.width) || !std::isfinite(screenSize.height))) { + _LogE(@"Non-finite Lynx screen size %@ will fall back to the next configuration source.", + NSStringFromCGSize(screenSize)); + } else if (!CGSizeEqualToSize(screenSize, CGSizeZero) && + (screenSize.width <= 0 || screenSize.height <= 0)) { + _LogW(@"Invalid partial Lynx screen size %@ will fall back as a whole.", + NSStringFromCGSize(screenSize)); + } + _screenSize = screenSize; +} + +- (void)setScreenScale:(CGFloat)screenScale { + if (!std::isfinite(screenScale)) { + _LogE(@"Non-finite Lynx screen scale %f will fall back to the next configuration source.", + screenScale); + } + _screenScale = screenScale; +} + - (id)init { TRACE_EVENT(LYNX_TRACE_CATEGORY, LYNX_VIEW_BUILDER_INIT); self = [super init]; diff --git a/platform/darwin/ios/lynx/LynxRuntimeMetricsUnitTest.m b/platform/darwin/ios/lynx/LynxRuntimeMetricsUnitTest.m new file mode 100644 index 0000000000..562a0e9a85 --- /dev/null +++ b/platform/darwin/ios/lynx/LynxRuntimeMetricsUnitTest.m @@ -0,0 +1,83 @@ +// Copyright 2026 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +#import +#import +#import +#import +#import +#import +#import + +@interface LynxScreenMetricsUnitTest : XCTestCase +@end + +@implementation LynxScreenMetricsUnitTest + +- (void)testBuilderScreenMetricsOverrideGroup { + LynxViewGroup* group = [[LynxViewGroup alloc] init]; + group.screenSize = CGSizeMake(390, 844); + group.screenScale = 3; + + LynxViewBuilder* builder = [[LynxViewBuilder alloc] init]; + builder.lynxViewGroup = group; + XCTAssertTrue(CGSizeEqualToSize(builder.screenSize, CGSizeMake(390, 844))); + XCTAssertEqualWithAccuracy(builder.screenScale, 3, 0.001); + + builder.screenSize = CGSizeMake(360, 800); + builder.screenScale = 2; + XCTAssertTrue(CGSizeEqualToSize(builder.screenSize, CGSizeMake(360, 800))); + XCTAssertEqualWithAccuracy(builder.screenScale, 2, 0.001); +} + +- (void)testInvalidBuilderScreenMetricsFallBackToGroup { + LynxViewGroup* group = [[LynxViewGroup alloc] init]; + group.screenSize = CGSizeMake(390, 844); + group.screenScale = 3; + + LynxViewBuilder* builder = [[LynxViewBuilder alloc] init]; + builder.lynxViewGroup = group; + builder.screenSize = CGSizeMake(360, 0); + builder.screenScale = NAN; + + XCTAssertTrue(CGSizeEqualToSize(builder.screenSize, CGSizeMake(390, 844))); + XCTAssertEqualWithAccuracy(builder.screenScale, 3, 0.001); +} + +- (void)testUIContextReplacesScreenMetricsSnapshot { + LynxScreenMetrics* initial = [[LynxScreenMetrics alloc] initWithScreenSize:CGSizeMake(390, 844) + scale:3]; + LynxUIContext* context = [[LynxUIContext alloc] initWithScreenMetrics:initial]; + LynxScreenMetrics* next = [[LynxScreenMetrics alloc] initWithScreenSize:CGSizeMake(360, 800) + scale:2]; + + [context updateScreenMetrics:next]; + + XCTAssertEqual(context.screenMetrics, next); + XCTAssertTrue(CGSizeEqualToSize(initial.screenSize, CGSizeMake(390, 844))); + XCTAssertEqualWithAccuracy(initial.scale, 3, 0.001); +} + +- (void)testUIContextViewportMetricsLifecycle { + LynxScreenMetrics* screenMetrics = + [[LynxScreenMetrics alloc] initWithScreenSize:CGSizeMake(390, 844) scale:3]; + LynxUIContext* context = [[LynxUIContext alloc] initWithScreenMetrics:screenMetrics]; + XCTAssertNil(context.viewportMetrics); + + LynxViewportMetrics* viewportMetrics = + [[LynxViewportMetrics alloc] initWithSize:CGSizeMake(320, 600) + widthMode:LynxViewSizeModeMax + heightMode:LynxViewSizeModeExact]; + [context updateViewportMetrics:viewportMetrics]; + + XCTAssertEqual(context.viewportMetrics, viewportMetrics); + XCTAssertTrue(CGSizeEqualToSize(context.viewportMetrics.size, CGSizeMake(320, 600))); + XCTAssertEqual(context.viewportMetrics.widthMode, LynxViewSizeModeMax); + XCTAssertEqual(context.viewportMetrics.heightMode, LynxViewSizeModeExact); + + [context updateViewportMetrics:nil]; + XCTAssertNil(context.viewportMetrics); +} + +@end diff --git a/platform/darwin/ios/lynx/LynxTemplateRender.mm b/platform/darwin/ios/lynx/LynxTemplateRender.mm index 148603f712..51987623c5 100644 --- a/platform/darwin/ios/lynx/LynxTemplateRender.mm +++ b/platform/darwin/ios/lynx/LynxTemplateRender.mm @@ -33,6 +33,7 @@ #import #import #import +#import #import "LynxAccessibilityModule.h" #import "LynxBaseConfigurator+Internal.h" #import "LynxCallStackUtil.h" @@ -64,6 +65,7 @@ @interface LynxViewBuilder (RuntimeOptions) - (LynxBackgroundRuntimeOptions*)effectiveLynxRuntimeOptions; @end +#include #include #include #include @@ -188,13 +190,17 @@ - (instancetype)initWithBuilderBlock:(LynxViewBuilderBlock)block } /// Member Variable - CGSize screenSize; - if (!CGSizeEqualToSize(builder.screenSize, CGSizeZero)) { - screenSize = builder.screenSize; - } else { + CGSize screenSize = builder.screenSize; + if (!std::isfinite(screenSize.width) || !std::isfinite(screenSize.height) || + screenSize.width <= 0 || screenSize.height <= 0) { screenSize = [UIScreen mainScreen].bounds.size; } + CGFloat screenScale = builder.screenScale; + if (!std::isfinite(screenScale) || screenScale <= 0) { + screenScale = [UIScreen mainScreen].scale; + } builder.screenSize = screenSize; + builder.screenScale = screenScale; [self setUpVariableWithBuilder:builder containerView:containerView screenSize:screenSize]; /// DevTool @@ -1531,17 +1537,78 @@ - (void)updateViewport:(BOOL)needLayout { CGFloat height = _layoutHeightMode == LynxViewSizeModeMax ? _preferredMaxLayoutHeight : _preferredLayoutHeight; shell_->UpdateViewport(width, widthMode, height, heightMode, needLayout); + if ([_lynxUIRenderer respondsToSelector:@selector(updateViewportMetrics:)]) { + LynxViewportMetrics* viewportMetrics = + [[LynxViewportMetrics alloc] initWithSize:CGSizeMake(width, height) + widthMode:_layoutWidthMode + heightMode:_layoutHeightMode]; + [_lynxUIRenderer updateViewportMetrics:viewportMetrics]; + } } - (void)updateScreenMetricsWithWidth:(CGFloat)width height:(CGFloat)height { + [self updateScreenMetricsWithWidth:width height:height scale:0]; +} + +- (void)updateScreenMetricsWithWidth:(CGFloat)width height:(CGFloat)height scale:(CGFloat)scale { + if (![NSThread isMainThread]) { + _LogE(@"updateScreenMetrics must be called on the main thread."); + NSAssert(NO, @"updateScreenMetrics must be called on the main thread."); + return; + } + if (!std::isfinite(width) || !std::isfinite(height) || !std::isfinite(scale)) { + _LogE(@"Rejecting non-finite Screen Metrics patch: width=%f, height=%f, scale=%f.", width, + height, scale); + return; + } + + LynxScreenMetrics* current = [_lynxUIRenderer getScreenMetrics]; + if (current == nil) { + _LogE(@"Cannot update Screen Metrics because the current metrics are unavailable."); + return; + } + + CGFloat nextWidth = width > 0 ? width : current.screenSize.width; + CGFloat nextHeight = height > 0 ? height : current.screenSize.height; + CGFloat nextScale = scale > 0 ? scale : current.scale; + LynxScreenMetrics* next = + [[LynxScreenMetrics alloc] initWithScreenSize:CGSizeMake(nextWidth, nextHeight) + scale:nextScale]; + [self updateScreenMetrics:next]; +} + +- (void)updateScreenMetrics:(LynxScreenMetrics*)screenMetrics { + if (![NSThread isMainThread]) { + _LogE(@"updateScreenMetrics must be called on the main thread."); + NSAssert(NO, @"updateScreenMetrics must be called on the main thread."); + return; + } + if (screenMetrics == nil || !std::isfinite(screenMetrics.screenSize.width) || + !std::isfinite(screenMetrics.screenSize.height) || !std::isfinite(screenMetrics.scale) || + screenMetrics.screenSize.width <= 0 || screenMetrics.screenSize.height <= 0 || + screenMetrics.scale <= 0) { + _LogE(@"Rejecting invalid Screen Metrics replacement."); + return; + } if (shell_->IsDestroyed()) { return; } - CGFloat scale = [UIScreen mainScreen].scale; - shell_->UpdateScreenMetrics(width, height, scale); + LynxScreenMetrics* current = [_lynxUIRenderer getScreenMetrics]; + if (current != nil && CGSizeEqualToSize(current.screenSize, screenMetrics.screenSize) && + current.scale == screenMetrics.scale) { + return; + } + + shell_->UpdateScreenMetrics(screenMetrics.screenSize.width, screenMetrics.screenSize.height, + screenMetrics.scale); - [_lynxUIRenderer updateScreenWidth:width height:height]; + if ([_lynxUIRenderer respondsToSelector:@selector(updateScreenMetrics:)]) { + [_lynxUIRenderer updateScreenMetrics:screenMetrics]; + } else { + [_lynxUIRenderer updateScreenWidth:screenMetrics.screenSize.width + height:screenMetrics.screenSize.height]; + } } - (void)updateFontScale:(CGFloat)scale { @@ -2742,6 +2809,7 @@ - (LynxViewBuilderBlock)getLynxViewBuilderBlock { builder.mediaResourceFetcher = [self->_lynxUIRenderer mediaResourceFetcher]; builder.templateResourceFetcher = [self->_lynxUIRenderer templateResourceFetcher]; builder.screenSize = [[self->_lynxUIRenderer getScreenMetrics] screenSize]; + builder.screenScale = [[self->_lynxUIRenderer getScreenMetrics] scale]; builder.lynxBackgroundRuntimeOptions = [[LynxBackgroundRuntimeOptions alloc] initWithOptions:self->_runtimeOptions]; builder.group = self->_group; diff --git a/platform/darwin/ios/lynx/LynxTemplateRenderHelper.mm b/platform/darwin/ios/lynx/LynxTemplateRenderHelper.mm index dcd998c48b..e78ff38a6d 100644 --- a/platform/darwin/ios/lynx/LynxTemplateRenderHelper.mm +++ b/platform/darwin/ios/lynx/LynxTemplateRenderHelper.mm @@ -24,6 +24,7 @@ #import #import #import +#import #import #import #import @@ -543,6 +544,16 @@ - (void)setUpWithBuilder:(LynxViewBuilder*)builder screenSize:(CGSize)screenSize /// UIRenderer [self setUpUIRendererWithBuilder:builder screenSize:screenSize]; + if ([_lynxUIRenderer respondsToSelector:@selector(updateScreenMetrics:)]) { + LynxScreenMetrics* screenMetrics = + [[LynxScreenMetrics alloc] initWithScreenSize:builder.screenSize scale:builder.screenScale]; + [_lynxUIRenderer updateScreenMetrics:screenMetrics]; + } + if (_isEngineInitFromReusePool && + [_lynxUIRenderer respondsToSelector:@selector(updateViewportMetrics:)]) { + [_lynxUIRenderer updateViewportMetrics:nil]; + } + /// LynxShell [self setUpLynxShellWithLastInstanceId:kUnknownInstanceId]; diff --git a/platform/darwin/ios/lynx/LynxUIRenderer.mm b/platform/darwin/ios/lynx/LynxUIRenderer.mm index c6ba2f4538..4db25666d5 100644 --- a/platform/darwin/ios/lynx/LynxUIRenderer.mm +++ b/platform/darwin/ios/lynx/LynxUIRenderer.mm @@ -118,8 +118,7 @@ - (instancetype)initWithLynxContext:(LynxContext *)context - (void)setupUIOwnerWithBuilder:(LynxViewBuilder *)builder { LynxScreenMetrics *screenMetrics = - [[LynxScreenMetrics alloc] initWithScreenSize:builder.screenSize - scale:[UIScreen mainScreen].scale]; + [[LynxScreenMetrics alloc] initWithScreenSize:builder.screenSize scale:builder.screenScale]; _uiOwner = [[LynxUIOwner alloc] initWithContainerView:_containerView componentRegistry:builder.config.componentRegistry screenMetrics:screenMetrics @@ -513,6 +512,18 @@ - (void)updateScreenWidth:(CGFloat)width height:(CGFloat)height { } } +- (void)updateScreenMetrics:(LynxScreenMetrics *)screenMetrics { + if (_uiOwner != nil && _uiOwner.uiContext != nil) { + [_uiOwner.uiContext updateScreenMetrics:screenMetrics]; + } +} + +- (void)updateViewportMetrics:(nullable LynxViewportMetrics *)viewportMetrics { + if (_uiOwner != nil && _uiOwner.uiContext != nil) { + [_uiOwner.uiContext updateViewportMetrics:viewportMetrics]; + } +} + - (void)pauseRootLayoutAnimation { [_uiOwner pauseRootLayoutAnimation]; } diff --git a/platform/darwin/ios/lynx/LynxUIRendererProtocol.h b/platform/darwin/ios/lynx/LynxUIRendererProtocol.h index 0787db914f..a7dfcbe83e 100644 --- a/platform/darwin/ios/lynx/LynxUIRendererProtocol.h +++ b/platform/darwin/ios/lynx/LynxUIRendererProtocol.h @@ -24,6 +24,7 @@ NS_ASSUME_NONNULL_BEGIN @class LynxUIIntersectionObserverManager; @class LynxRootUI; @class LynxScreenMetrics; +@class LynxViewportMetrics; @class LynxGestureArenaManager; @class LynxShadowNodeOwner; @@ -114,6 +115,14 @@ NS_ASSUME_NONNULL_BEGIN - (void)updateScreenWidth:(CGFloat)width height:(CGFloat)height; +@optional + +- (void)updateScreenMetrics:(LynxScreenMetrics *)screenMetrics; + +- (void)updateViewportMetrics:(nullable LynxViewportMetrics *)viewportMetrics; + +@required + - (void)pauseRootLayoutAnimation; - (void)resumeRootLayoutAnimation; diff --git a/platform/darwin/ios/lynx/LynxView.mm b/platform/darwin/ios/lynx/LynxView.mm index 2320d8cc6b..e29995fc64 100644 --- a/platform/darwin/ios/lynx/LynxView.mm +++ b/platform/darwin/ios/lynx/LynxView.mm @@ -451,7 +451,15 @@ - (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event { #pragma mark - View - (void)updateScreenMetricsWithWidth:(CGFloat)width height:(CGFloat)height { - [_templateRender updateScreenMetricsWithWidth:width height:height]; + [_templateRender updateScreenMetricsWithWidth:width height:height scale:0]; +} + +- (void)updateScreenMetricsWithWidth:(CGFloat)width height:(CGFloat)height scale:(CGFloat)scale { + [_templateRender updateScreenMetricsWithWidth:width height:height scale:scale]; +} + +- (void)updateScreenMetrics:(LynxScreenMetrics*)screenMetrics { + [_templateRender updateScreenMetrics:screenMetrics]; } /** diff --git a/platform/darwin/ios/lynx/LynxViewBuilder.mm b/platform/darwin/ios/lynx/LynxViewBuilder.mm index b09d0f8534..74669765f1 100644 --- a/platform/darwin/ios/lynx/LynxViewBuilder.mm +++ b/platform/darwin/ios/lynx/LynxViewBuilder.mm @@ -16,6 +16,8 @@ #import "LynxTraceEventDef.h" #import "LynxUIRendererCreator.h" +#include + @implementation LynxViewBuilder - (LynxConfig *)config { @@ -193,10 +195,33 @@ - (LynxBooleanOption)enableGenericResourceFetcher { } - (CGSize)screenSize { + CGSize screenSize = [super screenSize]; + if (std::isfinite(screenSize.width) && std::isfinite(screenSize.height) && screenSize.width > 0 && + screenSize.height > 0) { + return screenSize; + } if (_lynxViewGroup) { - return _lynxViewGroup.screenSize; + CGSize groupScreenSize = _lynxViewGroup.screenSize; + if (std::isfinite(groupScreenSize.width) && std::isfinite(groupScreenSize.height) && + groupScreenSize.width > 0 && groupScreenSize.height > 0) { + return groupScreenSize; + } + } + return screenSize; +} + +- (CGFloat)screenScale { + CGFloat screenScale = [super screenScale]; + if (std::isfinite(screenScale) && screenScale > 0) { + return screenScale; + } + if (_lynxViewGroup) { + CGFloat groupScreenScale = _lynxViewGroup.screenScale; + if (std::isfinite(groupScreenScale) && groupScreenScale > 0) { + return groupScreenScale; + } } - return [super screenSize]; + return screenScale; } - (BOOL)debuggable { diff --git a/platform/darwin/ios/lynx/LynxViewportMetrics.m b/platform/darwin/ios/lynx/LynxViewportMetrics.m new file mode 100644 index 0000000000..455e974b0f --- /dev/null +++ b/platform/darwin/ios/lynx/LynxViewportMetrics.m @@ -0,0 +1,21 @@ +// Copyright 2026 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +#import + +@implementation LynxViewportMetrics + +- (instancetype)initWithSize:(CGSize)size + widthMode:(LynxViewSizeMode)widthMode + heightMode:(LynxViewSizeMode)heightMode { + self = [super init]; + if (self) { + _size = size; + _widthMode = widthMode; + _heightMode = heightMode; + } + return self; +} + +@end diff --git a/platform/darwin/ios/lynx/event/LynxKeyboardEventDispatcher.m b/platform/darwin/ios/lynx/event/LynxKeyboardEventDispatcher.m index cb9dade57b..2f605d6995 100644 --- a/platform/darwin/ios/lynx/event/LynxKeyboardEventDispatcher.m +++ b/platform/darwin/ios/lynx/event/LynxKeyboardEventDispatcher.m @@ -330,7 +330,26 @@ - (void)keyboardWillShow:(NSNotification *)aNotification { NSValue *aValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey]; keyboardRect = [aValue CGRectValue]; } - int height = keyboardRect.size.height; + id screenCoordinateSpace = _context.getLynxView.window.screen.coordinateSpace; + + CGRect keyboardRectInApp = [_context.getLynxView.window convertRect:keyboardRect + fromCoordinateSpace:screenCoordinateSpace]; + + CGRect overlap = CGRectIntersection(_context.getLynxView.window.bounds, keyboardRectInApp); + + int height = 0; + if (!CGRectIsNull(overlap)) { + if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { + height = overlap.size.height; + } else { + CGFloat gapBetweenAppWindowSizeOverKeyboardWindowSize = + _context.getLynxView.window.bounds.size.height - CGRectGetMaxY(keyboardRect); + height = CGRectGetMaxY(_context.getLynxView.window.bounds) - + CGRectGetMinY(keyboardRectInApp) - gapBetweenAppWindowSizeOverKeyboardWindowSize / 2; + } + } + + // int height = keyboardRect.size.height; LLog(@"keyboard status is on"); LLog(@"keyboard height is %d", height); diff --git a/platform/darwin/ios/lynx/gesture/LynxGestureFlingTrigger.m b/platform/darwin/ios/lynx/gesture/LynxGestureFlingTrigger.m index bac266eca8..ee3429cc04 100644 --- a/platform/darwin/ios/lynx/gesture/LynxGestureFlingTrigger.m +++ b/platform/darwin/ios/lynx/gesture/LynxGestureFlingTrigger.m @@ -35,6 +35,8 @@ - (instancetype)initWithTarget:(nullable id)target action:(nullable SEL)action { selector:@selector(tick:)]; [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; _displayLink.paused = YES; + // TODO(xiamengfei.moonface): [ResizableWindowScale] Check whether this should use window or + // physical screen metrics. _threshold = 0.5f / [[UIScreen mainScreen] scale]; if (@available(iOS 10.3, *)) { _proMotion = UIScreen.mainScreen.maximumFramesPerSecond > 60; diff --git a/platform/darwin/ios/lynx/public/BUILD.gn b/platform/darwin/ios/lynx/public/BUILD.gn index 0bdacf0fdf..eb94300d26 100644 --- a/platform/darwin/ios/lynx/public/BUILD.gn +++ b/platform/darwin/ios/lynx/public/BUILD.gn @@ -27,6 +27,7 @@ public_shared_sources = [ "LynxUIInlineImage.h", "LynxUIRendererCreatorProtocol.h", "LynxURL.h", + "LynxViewportMetrics.h", "LynxWasmFuncRegistry.h", "animation/LynxAnimationDelegate.h", "animation/LynxAnimationInfo.h", diff --git a/platform/darwin/ios/lynx/public/LynxBaseConfigurator.h b/platform/darwin/ios/lynx/public/LynxBaseConfigurator.h index bc3663bfed..4c85d79bfd 100644 --- a/platform/darwin/ios/lynx/public/LynxBaseConfigurator.h +++ b/platform/darwin/ios/lynx/public/LynxBaseConfigurator.h @@ -73,6 +73,13 @@ */ @property(nonatomic, assign) CGSize screenSize; +/** + * You can set a logical device-pixel ratio for this LynxView. + * A non-positive value means not configured. A Builder may inherit its Group value; otherwise the + * system screen scale is used. + */ +@property(nonatomic, assign) CGFloat screenScale; + /** * indicates lynx view can be debug or not * when switch enableDevtool is disabled and diff --git a/platform/darwin/ios/lynx/public/LynxView.h b/platform/darwin/ios/lynx/public/LynxView.h index ed41a3fe1a..fb0184e19a 100644 --- a/platform/darwin/ios/lynx/public/LynxView.h +++ b/platform/darwin/ios/lynx/public/LynxView.h @@ -32,6 +32,7 @@ @class LynxTemplateRender; @class LynxContext; @class LynxElement; +@class LynxScreenMetrics; @protocol LynxBaseInspectorOwner; /** @@ -242,6 +243,17 @@ */ - (void)updateScreenMetricsWithWidth:(CGFloat)width height:(CGFloat)height; +/** + * Updates the Screen Metrics for this LynxView. Each non-positive argument keeps the current + * value. A non-finite argument rejects the whole update. + */ +- (void)updateScreenMetricsWithWidth:(CGFloat)width height:(CGFloat)height scale:(CGFloat)scale; + +/** + * Replaces the Screen Metrics for this LynxView with a complete finite positive snapshot. + */ +- (void)updateScreenMetrics:(nonnull LynxScreenMetrics*)screenMetrics; + /** * @apidoc * @brief Changing the font scaling ratio in client settings diff --git a/platform/darwin/ios/lynx/public/LynxViewportMetrics.h b/platform/darwin/ios/lynx/public/LynxViewportMetrics.h new file mode 100644 index 0000000000..87779a60ab --- /dev/null +++ b/platform/darwin/ios/lynx/public/LynxViewportMetrics.h @@ -0,0 +1,27 @@ +// Copyright 2026 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +#ifndef PLATFORM_DARWIN_IOS_LYNX_PUBLIC_LYNXVIEWPORTMETRICS_H_ +#define PLATFORM_DARWIN_IOS_LYNX_PUBLIC_LYNXVIEWPORTMETRICS_H_ + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface LynxViewportMetrics : NSObject + +@property(nonatomic, assign, readonly) CGSize size; +@property(nonatomic, assign, readonly) LynxViewSizeMode widthMode; +@property(nonatomic, assign, readonly) LynxViewSizeMode heightMode; + +- (instancetype)initWithSize:(CGSize)size + widthMode:(LynxViewSizeMode)widthMode + heightMode:(LynxViewSizeMode)heightMode; + +@end + +NS_ASSUME_NONNULL_END + +#endif // PLATFORM_DARWIN_IOS_LYNX_PUBLIC_LYNXVIEWPORTMETRICS_H_ diff --git a/platform/darwin/ios/lynx/public/base/background/LynxBackgroundRenderer+Internal.h b/platform/darwin/ios/lynx/public/base/background/LynxBackgroundRenderer+Internal.h index 40cd1e58ec..35756e9918 100644 --- a/platform/darwin/ios/lynx/public/base/background/LynxBackgroundRenderer+Internal.h +++ b/platform/darwin/ios/lynx/public/base/background/LynxBackgroundRenderer+Internal.h @@ -154,6 +154,10 @@ static inline CGColorRef LynxCreateDarkenColor(CGColorRef color, bool darken) { static inline CGSize LynxRoundViewSize(CGSize viewSize) { return CGSizeMake( + // TODO(xiamengfei.moonface): [ResizableWindowScale] Check whether this should use window or + // physical screen metrics. round(viewSize.width * [UIScreen mainScreen].scale) / [UIScreen mainScreen].scale, + // TODO(xiamengfei.moonface): [ResizableWindowScale] Check whether this should use window or + // physical screen metrics. round(viewSize.height * [UIScreen mainScreen].scale) / [UIScreen mainScreen].scale); } diff --git a/platform/darwin/ios/lynx/public/ui/LynxUIContext.h b/platform/darwin/ios/lynx/public/ui/LynxUIContext.h index 115bb620d7..c98c4a1fc6 100644 --- a/platform/darwin/ios/lynx/public/ui/LynxUIContext.h +++ b/platform/darwin/ios/lynx/public/ui/LynxUIContext.h @@ -16,6 +16,7 @@ #import #import #import +#import NS_ASSUME_NONNULL_BEGIN @class LynxRootUI; @@ -58,7 +59,8 @@ typedef void (^LynxClaySnapshotCompletion)(UIImage* _Nullable image); @property(nonatomic, weak) id lynxModuleExtraData; @property(nonatomic, assign, readwrite) int64_t shellPtr; @property(nonatomic, strong, nullable) id fetcher; -@property(nonatomic, readwrite) LynxScreenMetrics* screenMetrics; +@property(nonatomic, strong, readwrite) LynxScreenMetrics* screenMetrics; +@property(nonatomic, strong, nullable, readonly) LynxViewportMetrics* viewportMetrics; @property(nonatomic, readonly) LynxUIIntersectionObserverManager* intersectionManager; @property(nonatomic) LynxUIExposure* uiExposure; @property(nonatomic, strong, nullable, readonly) NSDictionary* keyframesDict; diff --git a/platform/darwin/ios/lynx/renderer/LynxTextLayer.mm b/platform/darwin/ios/lynx/renderer/LynxTextLayer.mm index 6790023cd4..2b1d0f9e01 100644 --- a/platform/darwin/ios/lynx/renderer/LynxTextLayer.mm +++ b/platform/darwin/ios/lynx/renderer/LynxTextLayer.mm @@ -13,6 +13,8 @@ - (instancetype)initWithLynxTextRenderer:(LynxTextRenderer *)textRenderer { if (self) { _textRenderer = textRenderer; self.delegate = self; + // TODO(xiamengfei.moonface): [ResizableWindowScale] Check whether this should use window or + // physical screen metrics. self.contentsScale = [UIScreen mainScreen].scale; } return self; diff --git a/platform/darwin/ios/lynx/renderer/LynxTextraLayer.mm b/platform/darwin/ios/lynx/renderer/LynxTextraLayer.mm index 95e178d310..86b1cfee04 100644 --- a/platform/darwin/ios/lynx/renderer/LynxTextraLayer.mm +++ b/platform/darwin/ios/lynx/renderer/LynxTextraLayer.mm @@ -18,6 +18,8 @@ - (instancetype)initWithTextID:(int32_t)textID _textID = textID; _rendererContext = rendererContext; self.delegate = self; + // TODO(xiamengfei.moonface): [ResizableWindowScale] Check whether this should use window or + // physical screen metrics. self.contentsScale = [UIScreen mainScreen].scale; } return self; diff --git a/platform/darwin/ios/lynx/ui/LynxUI.m b/platform/darwin/ios/lynx/ui/LynxUI.m index 8e05d13258..447f58c972 100644 --- a/platform/darwin/ios/lynx/ui/LynxUI.m +++ b/platform/darwin/ios/lynx/ui/LynxUI.m @@ -1652,10 +1652,15 @@ - (void)scrollIntoViewWithSmooth:(BOOL)isSmooth if (invoker) { image = [invoker takeScreenshot:self.view withBackgroundColor:_backgroundManager.backgroundColor + // TODO(xiamengfei.moonface): [ResizableWindowScale] Check whether + // this should use window or physical screen metrics. scale:[UIScreen mainScreen].scale * scale]; } else { - UIGraphicsBeginImageContextWithOptions(self.view.frame.size, NO, - [UIScreen mainScreen].scale * scale); + UIGraphicsBeginImageContextWithOptions( + self.view.frame.size, NO, + // TODO(xiamengfei.moonface): [ResizableWindowScale] Check whether this should use window or + // physical screen metrics. + [UIScreen mainScreen].scale * scale); if (_backgroundManager.backgroundColor && ![_backgroundManager.backgroundColor isEqual:[UIColor clearColor]]) { CGRect rect = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height); diff --git a/platform/darwin/ios/lynx/ui/LynxUIContext+Internal.h b/platform/darwin/ios/lynx/ui/LynxUIContext+Internal.h index afb68d99cf..51fed4406c 100644 --- a/platform/darwin/ios/lynx/ui/LynxUIContext+Internal.h +++ b/platform/darwin/ios/lynx/ui/LynxUIContext+Internal.h @@ -8,12 +8,18 @@ NS_ASSUME_NONNULL_BEGIN +@class LynxError; + @interface LynxUIContext () @property(nonatomic, assign) BOOL imageMonitorEnabled; @property(nonatomic, assign) BOOL devtoolEnabled; @property(nonatomic, assign) BOOL fixNewImageDownSampling; @property(nonatomic, strong, readwrite) LynxScrollFluency *fluencyInnerListener; +- (void)updateScreenMetrics:(LynxScreenMetrics *)screenMetrics; +- (void)updateViewportMetrics:(nullable LynxViewportMetrics *)viewportMetrics; +- (void)updateScreenSize:(CGSize)screenSize; + - (void)setUIConfig:(id)config; - (void)setDefaultOverflowVisible:(BOOL)enable; - (void)setDefaultImplicitAnimation:(BOOL)enable; diff --git a/platform/darwin/ios/lynx/ui/LynxUIContext.mm b/platform/darwin/ios/lynx/ui/LynxUIContext.mm index e127602e3d..7175101708 100644 --- a/platform/darwin/ios/lynx/ui/LynxUIContext.mm +++ b/platform/darwin/ios/lynx/ui/LynxUIContext.mm @@ -53,8 +53,17 @@ - (instancetype)initWithScreenMetrics:(LynxScreenMetrics*)screenMetrics { return self; } +- (void)updateScreenMetrics:(LynxScreenMetrics*)screenMetrics { + _screenMetrics = screenMetrics; +} + +- (void)updateViewportMetrics:(nullable LynxViewportMetrics*)viewportMetrics { + _viewportMetrics = viewportMetrics; +} + - (void)updateScreenSize:(CGSize)screenSize { - [_screenMetrics setLynxScreenSize:screenSize]; + _screenMetrics = [[LynxScreenMetrics alloc] initWithScreenSize:screenSize + scale:_screenMetrics.scale]; } - (void)onGestureRecognized { diff --git a/platform/darwin/ios/lynx/ui/image/LynxImageBlurUtils.m b/platform/darwin/ios/lynx/ui/image/LynxImageBlurUtils.m index 29adfc90df..5d08797163 100644 --- a/platform/darwin/ios/lynx/ui/image/LynxImageBlurUtils.m +++ b/platform/darwin/ios/lynx/ui/image/LynxImageBlurUtils.m @@ -44,6 +44,8 @@ + (UIImage *)blurImage:(UIImage *)inputImage withRadius:(CGFloat)radius { // A description of how to compute the box kernel width from the Gaussian // radius (aka standard deviation) appears in the SVG spec: // https://drafts.fxtf.org/filter-effects/#feGaussianBlurElement + // TODO(xiamengfei.moonface): [ResizableWindowScale] Check whether this should use window or + // physical screen metrics. CGFloat inputRadius = radius * [[UIScreen mainScreen] scale]; uint32_t boxSize = floor(inputRadius * 3 * sqrt(2 * M_PI) / 4 + 0.5); // Force boxSize to be odd @@ -105,6 +107,8 @@ + (UIImage *)ciGaussianBlurImage:(UIImage *)inputImage withRadius:(CGFloat)radiu CIImage *inputCIImage = [[CIImage alloc] initWithImage:inputImage]; CIFilter *filter = [CIFilter filterWithName:@"CIGaussianBlur"]; + // TODO(xiamengfei.moonface): [ResizableWindowScale] Check whether this should use window or + // physical screen metrics. CGFloat inputRadius = radius * [[UIScreen mainScreen] scale]; [filter setValue:inputCIImage forKey:kCIInputImageKey]; [filter setValue:@(inputRadius) forKey:kCIInputRadiusKey]; diff --git a/platform/darwin/ios/lynx/ui/list/LynxUIListLoader.mm b/platform/darwin/ios/lynx/ui/list/LynxUIListLoader.mm index 47440b59d6..bed8895318 100644 --- a/platform/darwin/ios/lynx/ui/list/LynxUIListLoader.mm +++ b/platform/darwin/ios/lynx/ui/list/LynxUIListLoader.mm @@ -393,7 +393,7 @@ - (void)loadCellInfos { // estimatedHeight provided by props must be a number, indicating the height for corresponding // component, with unit as `rpx` the screen width is defined as `750rpx`, compute the scale of // the unit of current iOS device to `rpx` - CGFloat scale = [UIScreen mainScreen].bounds.size.width / kLynxUIListLoaderScreenWidthInRpx; + CGFloat scale = self.context.screenMetrics.screenSize.width / kLynxUIListLoaderScreenWidthInRpx; NSMutableDictionary *estimatedHeights = [[NSMutableDictionary alloc] initWithCapacity:estimatedHeightsRef.size()]; for (std::size_t i = 0; i < estimatedHeightsRef.size(); ++i) { diff --git a/platform/darwin/ios/lynx/ui/list/container/LynxUIListContainer.mm b/platform/darwin/ios/lynx/ui/list/container/LynxUIListContainer.mm index bb7cf157f7..65a854fcad 100644 --- a/platform/darwin/ios/lynx/ui/list/container/LynxUIListContainer.mm +++ b/platform/darwin/ios/lynx/ui/list/container/LynxUIListContainer.mm @@ -1067,6 +1067,8 @@ - (void)updateStickyBottoms { // We can not move less than 1/scale pt in every frame, cause contentOffset // will align to 1/scale. + // TODO(xiamengfei.moonface): [ResizableWindowScale] Check whether this should use window or + // physical screen metrics. while (ABS(rate / preferredFramesPerSecond) < 1.0 / UIScreen.mainScreen.scale) { preferredFramesPerSecond -= 1; if (preferredFramesPerSecond == 0) { diff --git a/platform/darwin/ios/lynx/ui/list/lynx_collection/LynxUICollection+Delegate.m b/platform/darwin/ios/lynx/ui/list/lynx_collection/LynxUICollection+Delegate.m index c957c6a60b..e86f804665 100644 --- a/platform/darwin/ios/lynx/ui/list/lynx_collection/LynxUICollection+Delegate.m +++ b/platform/darwin/ios/lynx/ui/list/lynx_collection/LynxUICollection+Delegate.m @@ -408,6 +408,8 @@ - (NSArray *)visibleCellArray { // We can not move less than 1/scale pt in every frame, cause contentOffset will align to // 1/scale. + // TODO(xiamengfei.moonface): [ResizableWindowScale] Check whether this should use window or + // physical screen metrics. while (ABS(rate / preferredFramesPerSecond) < 1.0 / UIScreen.mainScreen.scale) { preferredFramesPerSecond -= 1; if (preferredFramesPerSecond == 0) { diff --git a/platform/darwin/ios/lynx/ui/list/lynx_collection/LynxUICollection.m b/platform/darwin/ios/lynx/ui/list/lynx_collection/LynxUICollection.m index 969ed9f403..2916bd9b93 100644 --- a/platform/darwin/ios/lynx/ui/list/lynx_collection/LynxUICollection.m +++ b/platform/darwin/ios/lynx/ui/list/lynx_collection/LynxUICollection.m @@ -156,6 +156,9 @@ - (UIView *)createView { if (@available(iOS 11, *)) { collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; } + if (@available(iOS 13.0, *)) { + collectionView.automaticallyAdjustsScrollIndicatorInsets = NO; + } if ([self isAsync]) { // Add feature counter to count when this async-list is set to true diff --git a/platform/darwin/ios/lynx/ui/list/lynx_collection/layout/LynxCollectionViewLayoutModel.m b/platform/darwin/ios/lynx/ui/list/lynx_collection/layout/LynxCollectionViewLayoutModel.m index 25e8734a28..07b547d3a6 100644 --- a/platform/darwin/ios/lynx/ui/list/lynx_collection/layout/LynxCollectionViewLayoutModel.m +++ b/platform/darwin/ios/lynx/ui/list/lynx_collection/layout/LynxCollectionViewLayoutModel.m @@ -4,6 +4,7 @@ #import #import +#import @implementation LynxCollectionViewLayoutModel @@ -35,11 +36,13 @@ + (instancetype)modelWithDefaultSize { } + (CGFloat)defaultHeight { - return [UIScreen mainScreen].bounds.size.height; + return [LynxUIKitAPIAdapter getKeyWindow] ? [LynxUIKitAPIAdapter getKeyWindow].bounds.size.height + : [UIScreen mainScreen].bounds.size.height; } + (CGFloat)defaultWidth { - return [UIScreen mainScreen].bounds.size.width; + return [LynxUIKitAPIAdapter getKeyWindow] ? [LynxUIKitAPIAdapter getKeyWindow].bounds.size.width + : [UIScreen mainScreen].bounds.size.width; } - (id)copyWithZone:(NSZone*)zone { diff --git a/platform/darwin/ios/lynx/ui/scroll_view/LynxUIScrollViewInternal.m b/platform/darwin/ios/lynx/ui/scroll_view/LynxUIScrollViewInternal.m index 618597472e..46aaf887a0 100644 --- a/platform/darwin/ios/lynx/ui/scroll_view/LynxUIScrollViewInternal.m +++ b/platform/darwin/ios/lynx/ui/scroll_view/LynxUIScrollViewInternal.m @@ -292,6 +292,8 @@ - (void)autoScroll:(NSDictionary *)params withResult:(LynxUIMethodCallbackBlock) // We can not move less than 1/scale pt in every frame, cause contentOffset // will align to 1/scale. + // TODO(xiamengfei.moonface): [ResizableWindowScale] Check whether this should use window or + // physical screen metrics. while (ABS(rate / preferredFramesPerSecond) < 1.0 / UIScreen.mainScreen.scale) { preferredFramesPerSecond -= 1; if (preferredFramesPerSecond == 0) { diff --git a/platform/darwin/ios/lynx/ui/scroll_view/LynxUIScroller.m b/platform/darwin/ios/lynx/ui/scroll_view/LynxUIScroller.m index 9d7545ddbe..6be5b448e2 100644 --- a/platform/darwin/ios/lynx/ui/scroll_view/LynxUIScroller.m +++ b/platform/darwin/ios/lynx/ui/scroll_view/LynxUIScroller.m @@ -225,6 +225,9 @@ - (UIView *)createView { if (@available(iOS 11.0, *)) { scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; } + if (@available(iOS 13.0, *)) { + scrollView.automaticallyAdjustsScrollIndicatorInsets = NO; + } scrollView.shouldManualExposure = YES; [[NSNotificationCenter defaultCenter] @@ -1569,6 +1572,8 @@ - (void)flick:(float)velocity direction:(ScrollDirection)direction { __strong FlickParameter *flicker = [[FlickParameter alloc] initFlick:velocity decelerationRate:self.view.decelerationRate + // TODO(xiamengfei.moonface): [ResizableWindowScale] Check whether + // this should use window or physical screen metrics. threshold:0.5 / [[UIScreen mainScreen] scale] oppositeBoundary:oppositeBoundary positiveBoundary:positiveBoundary]; diff --git a/platform/darwin/ios/lynx/ui/scroll_view/base/LynxBaseScrollView+Horizontal.m b/platform/darwin/ios/lynx/ui/scroll_view/base/LynxBaseScrollView+Horizontal.m index 0af642a418..1006aab0f0 100644 --- a/platform/darwin/ios/lynx/ui/scroll_view/base/LynxBaseScrollView+Horizontal.m +++ b/platform/darwin/ios/lynx/ui/scroll_view/base/LynxBaseScrollView+Horizontal.m @@ -57,6 +57,8 @@ - (BOOL)canScrollForwardsHorizontally { CGFloat offset = [self getScrollOffsetHorizontally]; CGFloat scrollRange[2] = {0, 0}; [self getScrollRangeHorizontally:&scrollRange]; + // TODO(xiamengfei.moonface): [ResizableWindowScale] Check whether this should use window or + // physical screen metrics. if (offset >= scrollRange[1] - 1.0 / UIScreen.mainScreen.scale) { return NO; } else { diff --git a/platform/darwin/ios/lynx/ui/scroll_view/base/LynxBaseScrollView+Nested.m b/platform/darwin/ios/lynx/ui/scroll_view/base/LynxBaseScrollView+Nested.m index 91a68d90fb..2d442cfeea 100644 --- a/platform/darwin/ios/lynx/ui/scroll_view/base/LynxBaseScrollView+Nested.m +++ b/platform/darwin/ios/lynx/ui/scroll_view/base/LynxBaseScrollView+Nested.m @@ -112,11 +112,15 @@ + (BOOL)isBouncing:(LynxBaseScrollView *)scrollView { } else { [scrollView getScrollRangeHorizontally:&scrollRange]; } + // TODO(xiamengfei.moonface): [ResizableWindowScale] Check whether this should use window or + // physical screen metrics. CGFloat epsilon = 1.0 / UIScreen.mainScreen.scale; return currentOffset < scrollRange[0] - epsilon || currentOffset > scrollRange[1] + epsilon; } + (LynxNestedScrollDirection)directionFromAxisDelta:(CGFloat)delta { + // TODO(xiamengfei.moonface): [ResizableWindowScale] Check whether this should use window or + // physical screen metrics. CGFloat epsilon = 1.0 / UIScreen.mainScreen.scale; if (delta > epsilon) { return LynxNestedScrollDirectionForwards; diff --git a/platform/darwin/ios/lynx/ui/scroll_view/base/LynxBaseScrollView+Vertical.m b/platform/darwin/ios/lynx/ui/scroll_view/base/LynxBaseScrollView+Vertical.m index ae753bd300..5d094d6bae 100644 --- a/platform/darwin/ios/lynx/ui/scroll_view/base/LynxBaseScrollView+Vertical.m +++ b/platform/darwin/ios/lynx/ui/scroll_view/base/LynxBaseScrollView+Vertical.m @@ -57,6 +57,8 @@ - (BOOL)canScrollForwardsVertically { CGFloat offset = [self getScrollOffsetVertically]; CGFloat scrollRange[2] = {0, 0}; [self getScrollRangeVertically:&scrollRange]; + // TODO(xiamengfei.moonface): [ResizableWindowScale] Check whether this should use window or + // physical screen metrics. if (offset >= scrollRange[1] - 1.0 / UIScreen.mainScreen.scale) { return NO; } else { diff --git a/platform/darwin/ios/lynx/ui/scroll_view/base/LynxBaseScrollView.m b/platform/darwin/ios/lynx/ui/scroll_view/base/LynxBaseScrollView.m index e2c69afeb8..cee4007a43 100644 --- a/platform/darwin/ios/lynx/ui/scroll_view/base/LynxBaseScrollView.m +++ b/platform/darwin/ios/lynx/ui/scroll_view/base/LynxBaseScrollView.m @@ -46,6 +46,9 @@ - (instancetype)initWithVertical:(BOOL)vertical layoutFromEnd:(BOOL)layoutFromEn if (@available(iOS 11.0, *)) { self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; } + if (@available(iOS 13.0, *)) { + self.automaticallyAdjustsScrollIndicatorInsets = NO; + } _vertical = vertical; _layoutFromEnd = layoutFromEnd; _scrollState = LynxBaseScrollViewScrollStateIdle; diff --git a/platform/darwin/ios/lynx/ui/text/LynxUIText.m b/platform/darwin/ios/lynx/ui/text/LynxUIText.m index e1d548dd7d..1872dd951c 100644 --- a/platform/darwin/ios/lynx/ui/text/LynxUIText.m +++ b/platform/darwin/ios/lynx/ui/text/LynxUIText.m @@ -379,6 +379,8 @@ - (void)updateAttachmentsFrame { [self.children enumerateObjectsUsingBlock:^(LynxUI *_Nonnull child, NSUInteger idx, BOOL *_Nonnull stop) { if (child.sign == attachment.sign) { + // TODO(xiamengfei.moonface): [ResizableWindowScale] Check whether this should use + // window or physical screen metrics. CGFloat scale = [UIScreen mainScreen].scale; if (attachment.nativeAttachment) { if (CGRectIsEmpty(attachment.frame) && ![child.view isHidden]) { diff --git a/platform/darwin/ios/lynx/utils/LynxUnitUtilsUnitTest.m b/platform/darwin/ios/lynx/utils/LynxUnitUtilsUnitTest.m index 14abdf694c..217b82aec8 100644 --- a/platform/darwin/ios/lynx/utils/LynxUnitUtilsUnitTest.m +++ b/platform/darwin/ios/lynx/utils/LynxUnitUtilsUnitTest.m @@ -6,6 +6,11 @@ #import #import +@interface LynxScreenMetrics (UnitTestMutable) +- (void)setScreenSize:(CGSize)screenSize; +- (void)setScale:(CGFloat)scale; +@end + @interface LynxUnitUtilsUnitTest : XCTestCase @end @@ -100,11 +105,10 @@ - (void)testToPtFromUnitValueWithHeightWidth { - (void)testToPtFromUnitValueWithScreenMetrics { CGFloat ptValue = 0; - LynxScreenMetrics* screenMertics = [[LynxScreenMetrics alloc] init]; - [screenMertics setScreenSize:CGSizeMake(828, 1338)]; - [screenMertics setScale:3]; + LynxScreenMetrics* screenMetrics = + [[LynxScreenMetrics alloc] initWithScreenSize:CGSizeMake(828, 1338) scale:3]; - ptValue = [LynxUnitUtils toPtWithScreenMetrics:screenMertics + ptValue = [LynxUnitUtils toPtWithScreenMetrics:screenMetrics unitValue:@"375rpx" rootFontSize:0 curFontSize:0 @@ -113,7 +117,7 @@ - (void)testToPtFromUnitValueWithScreenMetrics { withDefaultPt:0]; XCTAssertTrue([self floatEqualFirst:ptValue toSecond:414]); - ptValue = [LynxUnitUtils toPtWithScreenMetrics:screenMertics + ptValue = [LynxUnitUtils toPtWithScreenMetrics:screenMetrics unitValue:@"105.4ppx" rootFontSize:0 curFontSize:0 diff --git a/platform/darwin/ios/lynx_service/image/LynxImageService.m b/platform/darwin/ios/lynx_service/image/LynxImageService.m index 91370b9fac..d93dbba496 100644 --- a/platform/darwin/ios/lynx_service/image/LynxImageService.m +++ b/platform/darwin/ios/lynx_service/image/LynxImageService.m @@ -180,7 +180,11 @@ - (nonnull dispatch_block_t)loadNewImageFromURL:(nonnull LynxURL*)requestUrl NSMutableDictionary* context = [NSMutableDictionary dictionary]; if ([[contextInfo objectForKey:LynxImageFetcherContextKeyDownsampling] boolValue]) { + // TODO(xiamengfei.moonface): [ResizableWindowScale] Check whether this should use window or + // physical screen metrics. CGSize thumbnailSize = CGSizeMake(targetSize.width * [UIScreen mainScreen].scale, + // TODO(xiamengfei.moonface): [ResizableWindowScale] Check + // whether this should use window or physical screen metrics. targetSize.height * [UIScreen mainScreen].scale); context[SDWebImageContextImageThumbnailPixelSize] = [NSValue valueWithCGSize:thumbnailSize]; } diff --git a/platform/darwin/ios/lynx_xelement/animax/LynxUIAnimaX.mm b/platform/darwin/ios/lynx_xelement/animax/LynxUIAnimaX.mm index 094c873718..2740bcdef0 100644 --- a/platform/darwin/ios/lynx_xelement/animax/LynxUIAnimaX.mm +++ b/platform/darwin/ios/lynx_xelement/animax/LynxUIAnimaX.mm @@ -551,7 +551,8 @@ - (void)createPlayer { return; } self.animaxContext.enableMultiThreadAccelerate = self.enableMultiThreadAccelerate; - self.animaxPlayer = [[AnimaXPlayer alloc] initWithContext:self.animaxContext]; + self.animaxPlayer = [[AnimaXPlayer alloc] initWithContext:self.animaxContext + scale:self.context.screenMetrics.scale]; [self.propsPrioritySetter attachToPlayer:self.animaxPlayer]; } diff --git a/platform/darwin/ios/lynx_xelement/input/LynxUIBaseInput.m b/platform/darwin/ios/lynx_xelement/input/LynxUIBaseInput.m index 4cb9e260ae..1aad6cd389 100644 --- a/platform/darwin/ios/lynx_xelement/input/LynxUIBaseInput.m +++ b/platform/darwin/ios/lynx_xelement/input/LynxUIBaseInput.m @@ -782,15 +782,32 @@ - (BOOL)inputViewShouldBeginEditing:(id)input { return !self.readonly; } - - (void)onWillShowKeyboard:(NSNotification *)notification { - CGRect keyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; - CGFloat keyboardHeight = keyboardFrame.size.height; - CGFloat safeAreaBottom = 0; + id screenCoordinateSpace = self.context.lynxContext.getLynxView.window.screen.coordinateSpace; + + CGRect keyboardRectInApp = [self.context.lynxContext.getLynxView.window convertRect:keyboardFrame fromCoordinateSpace:screenCoordinateSpace]; + + CGRect overlap = CGRectIntersection(self.context.lynxContext.getLynxView.window.bounds, keyboardRectInApp); + + + CGFloat keyboardHeight = 0; + if (!CGRectIsNull(overlap)) { + if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { + keyboardHeight = overlap.size.height; + } else { + CGFloat gapBetweenAppWindowSizeOverKeyboardWindowSize = self.context.lynxContext.getLynxView.window.bounds.size.height - CGRectGetMaxY(keyboardFrame); + keyboardHeight = CGRectGetMaxY(self.context.lynxContext.getLynxView.window.bounds) - CGRectGetMinY(keyboardRectInApp) - gapBetweenAppWindowSizeOverKeyboardWindowSize/2; + } + } + +// CGFloat keyboardHeight = keyboardFrame.size.height; + + CGFloat safeAreaBottom = 0; + if (@available(iOS 11.0, *)) { safeAreaBottom = self.view.safeAreaInsets.bottom; } @@ -838,7 +855,7 @@ - (void)updateUISize { if ([node isKindOfClass:LynxUIBaseInputShadowNode.class]) { CGSize preSize = node.uiSize; - CGFloat width = self.view.bounds.size.width ? : UIScreen.mainScreen.bounds.size.width; + CGFloat width = self.view.bounds.size.width ? : self.context.screenMetrics.screenSize.width; [self.view sizeThatFits:CGSizeMake(0, 0)]; CGSize updatedSize = [self.view sizeThatFits:CGSizeMake(width, CGFLOAT_MAX)]; // Measure an empty input as a single text line. UITextView's empty-text size may handle diff --git a/platform/darwin/ios/lynx_xelement/input/LynxUITextArea.m b/platform/darwin/ios/lynx_xelement/input/LynxUITextArea.m index f52bf9c15e..c77cf4e0c9 100644 --- a/platform/darwin/ios/lynx_xelement/input/LynxUITextArea.m +++ b/platform/darwin/ios/lynx_xelement/input/LynxUITextArea.m @@ -207,6 +207,7 @@ - (NSInteger)calcLines:(UITextView*)textView firstRect:(CGRect)firstRect lastRec line = 1; } else { CGFloat lineHeightEnumerator = firstRect.origin.y; + // TODO(xiamengfei.moonface): [ResizableWindowScale] Check whether this should use window or physical screen metrics. while (lastRect.origin.y - lineHeightEnumerator > 1.0 / UIScreen.mainScreen.scale) { line++; lineHeightEnumerator += self.inputParagraphStyle.minimumLineHeight ?: self.font.lineHeight + self.inputParagraphStyle.lineSpacing; @@ -353,7 +354,7 @@ - (NSValue *)getContentSize { - (CGSize)adjustViewSize:(CGSize)viewSize { if (self.placeholder.length) { - CGFloat width = self.placeholderView.bounds.size.width ? : UIScreen.mainScreen.bounds.size.width; + CGFloat width = self.placeholderView.bounds.size.width ? : self.context.screenMetrics.screenSize.width; CGSize placeholderSize = [self.placeholderView sizeThatFits:CGSizeMake(width, CGFLOAT_MAX)]; viewSize.width = MAX(viewSize.width, placeholderSize.width); viewSize.height = MAX(viewSize.height, placeholderSize.height); diff --git a/platform/darwin/ios/lynx_xelement/overlay/LynxOverlayGlobalManager.h b/platform/darwin/ios/lynx_xelement/overlay/LynxOverlayGlobalManager.h index 7b3c03a1f5..5c8df723ee 100644 --- a/platform/darwin/ios/lynx_xelement/overlay/LynxOverlayGlobalManager.h +++ b/platform/darwin/ios/lynx_xelement/overlay/LynxOverlayGlobalManager.h @@ -38,6 +38,9 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)destoryUnattachedOverlay:(UIView *)overlay; ++ (UIView *)getTopViewControllerWithMode:(LynxOverlayMode)mode + customViewController:(UIViewController *)customViewController; + @end NS_ASSUME_NONNULL_END diff --git a/platform/darwin/ios/lynx_xelement/overlay/LynxOverlayGlobalManager.m b/platform/darwin/ios/lynx_xelement/overlay/LynxOverlayGlobalManager.m index c1781f8ccd..dea4a332ca 100644 --- a/platform/darwin/ios/lynx_xelement/overlay/LynxOverlayGlobalManager.m +++ b/platform/darwin/ios/lynx_xelement/overlay/LynxOverlayGlobalManager.m @@ -104,7 +104,7 @@ + (NSMutableArray *)getAllVisibleOverlay { return array; } -- (UIView *)getTopViewControllerWithMode:(LynxOverlayMode)mode ++ (UIView *)getTopViewControllerWithMode:(LynxOverlayMode)mode customViewController:(UIViewController *)customViewController { UIView *topContainer = nil; switch (mode) { @@ -136,8 +136,9 @@ - (void)destroyOverlayView:(UIView *)overlay customViewController:(UIViewController *)customViewController { // Firstly, get the top container by the mode - UIView *topContainer = [self getTopViewControllerWithMode:mode - customViewController:customViewController]; + UIView *topContainer = + [LynxOverlayGlobalManager getTopViewControllerWithMode:mode + customViewController:customViewController]; NSInteger identifier = [self identifierWithModel:mode withContainer:topContainer]; @@ -188,8 +189,9 @@ - (UIView *)showOverlayView:(UIView *)overlay withMode:(LynxOverlayMode)mode customViewController:(UIViewController *)customViewController { // Firstly, get the top container by the mode - UIView *topContainer = [self getTopViewControllerWithMode:mode - customViewController:customViewController]; + UIView *topContainer = + [LynxOverlayGlobalManager getTopViewControllerWithMode:mode + customViewController:customViewController]; // Secondly, get the level container inside the top container with the specific level UIView *levelContainer = [self levelContainerAt:level withModel:mode withContainer:topContainer]; @@ -267,7 +269,7 @@ - (NSInteger)identifierWithModel:(LynxOverlayMode)mode withContainer:(UIView *)c return mode + (uintptr_t)container; } -- (UIView *)modalContainer { ++ (UIView *)modalContainer { UIViewController *topController = [LynxUIKitAPIAdapter getForegroundKeyWindow].rootViewController; while (topController.presentedViewController) { topController = topController.presentedViewController; @@ -275,13 +277,13 @@ - (UIView *)modalContainer { return topController.view; } -- (UIView *)topContainer { ++ (UIView *)topContainer { UIViewController *rootController = [LynxUIKitAPIAdapter getForegroundKeyWindow].rootViewController; return [self topViewControllerForController:rootController].view; } -- (UIViewController *)topViewControllerForController:(UIViewController *)rootViewController { ++ (UIViewController *)topViewControllerForController:(UIViewController *)rootViewController { if ([rootViewController isKindOfClass:[UINavigationController class]]) { UINavigationController *navigationController = (UINavigationController *)rootViewController; return [self topViewControllerForController:[navigationController.viewControllers lastObject]]; @@ -296,7 +298,7 @@ - (UIViewController *)topViewControllerForController:(UIViewController *)rootVie return rootViewController; } -- (UIView *)pageContainer { ++ (UIView *)pageContainer { UIViewController *topController; UIViewController *rootController = [LynxUIKitAPIAdapter getForegroundKeyWindow].rootViewController; @@ -308,7 +310,7 @@ - (UIView *)pageContainer { return topController.view; } -- (UIView *)windowContainer { ++ (UIView *)windowContainer { return [LynxUIKitAPIAdapter getForegroundKeyWindow]; } diff --git a/platform/darwin/ios/lynx_xelement/overlay/LynxUIOverlay.m b/platform/darwin/ios/lynx_xelement/overlay/LynxUIOverlay.m index 025455c391..c5dd1abdcc 100644 --- a/platform/darwin/ios/lynx_xelement/overlay/LynxUIOverlay.m +++ b/platform/darwin/ios/lynx_xelement/overlay/LynxUIOverlay.m @@ -9,6 +9,7 @@ #import #import #import +#import #import #import #import @@ -23,6 +24,12 @@ #import #import +@interface LynxUIOverlayShadowNode () + +@property(atomic, assign) CGRect customRect; + +@end + @implementation LynxUIOverlayShadowNode LYNX_LAZY_REGISTER_SHADOW_NODE("overlay") @@ -31,6 +38,7 @@ - (instancetype)initWithSign:(NSInteger)sign tagName:(NSString *)tagName { if (self = [super initWithSign:sign tagName:tagName]) { // enable custom-layout by default self.hasCustomLayout = YES; + self.customRect = CGRectNull; } return self; } @@ -43,7 +51,7 @@ - (MeasureResult)customMeasureLayoutNode:(nonnull MeasureParam *)param [self.children enumerateObjectsUsingBlock:^(LynxShadowNode *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { if ([obj isKindOfClass:[LynxNativeLayoutNode class]]) { - CGRect bounds = [LynxUIOverlayShadowNode windowBounds]; + CGRect bounds = [self getMeasureBounds]; // Overlay's frame is always equal to the screen, its child should be measured correctly MeasureParam *childParam = [[MeasureParam alloc] initWithWidth:bounds.size.width - obj.style.computedMarginLeft - @@ -75,6 +83,59 @@ - (void)customAlignLayoutNode:(nonnull AlignParam *)param }]; } +- (void)updateCustomRect:(CGRect)customLayout { + if (CGRectEqualToRect(customLayout, self.customRect)) { + return; + } + self.customRect = customLayout; + @synchronized(self) { + [self internalSetNeedsLayoutForce]; + } +} + ++ (UIViewController *)findCustomViewControllerWith:(Class)customViewControllerClass + rootView:(UIView *)rootView { + UIViewController *customViewController = nil; + + UIResponder *responder = rootView; + while (responder && ![responder isKindOfClass:customViewControllerClass]) { + responder = responder.nextResponder; + } + customViewController = (UIViewController *)responder; + + // Can not find target UIViewController, try to find target UIView + if (!customViewController) { + UIView *view = rootView; + while (view && ![view isKindOfClass:customViewControllerClass]) { + view = view.superview; + } + // It's safe to cast from UIView to UIViewController. TODO: (xiamengfei.moonface) adapt to + // UIView + customViewController = (UIViewController *)view; + } + + return customViewController; +} + +- (CGRect)getMeasureBounds { + if (CGRectIsNull(self.customRect)) { + return [LynxUIOverlayShadowNode windowBounds]; + } else { + return self.customRect; + } +} + ++ (CGRect)getBoundsWithMode:(LynxOverlayMode)mode + viewController:(UIViewController *)viewController { + if (mode == LynxOverlayModeWindow) { + return [LynxUIOverlayShadowNode windowBounds]; + } else { + return [LynxOverlayGlobalManager getTopViewControllerWithMode:mode + customViewController:viewController] + .bounds; + } +} + + (CGRect)windowBounds { UIWindow *window = [LynxUIKitAPIAdapter getKeyWindow]; if (window) { @@ -124,6 +185,8 @@ @interface LynxUIOverlay () - (void)updatePlatformEventRootActiveForFragmentLayer:(BOOL)active; +@property(nonatomic, assign) BOOL followModeEdge; + @end @implementation LynxUIOverlay { @@ -169,9 +232,13 @@ - (void)onNodeReady { if (!self.context.rootUI) { return; } + CGRect modeRect = self.followModeEdge + ? [LynxUIOverlayShadowNode getBoundsWithMode:self.mode + viewController:self.customViewController] + : [LynxUIOverlayShadowNode windowBounds]; // Overlay's frame must be equal to UIScreen - self.view.frame = [LynxUIOverlayShadowNode windowBounds]; + self.view.frame = modeRect; // add Overlay's view to global container according to its level and mode UIView *container = @@ -184,10 +251,15 @@ - (void)onNodeReady { CGPoint offset = [[self windowContainer] convertPoint:CGPointZero toView:container]; CGRect rect = {(self.notAdjustLeftMargin ? 0 : offset.x), - (self.notAdjustTopMargin ? 0 : offset.y), - [LynxUIOverlayShadowNode windowBounds].size}; + (self.notAdjustTopMargin ? 0 : offset.y), modeRect.size}; self.view.frame = rect; + if (self.followModeEdge) { + LynxUIOverlayShadowNode *node = + (LynxUIOverlayShadowNode *)[self.context.nodeOwner nodeWithSign:self.sign]; + [node updateCustomRect:rect]; + } + // make sure Overlay is always at the front if (self.willBecomeVisible) { self.willBecomeVisible = NO; @@ -297,6 +369,8 @@ - (BOOL)eventThrough:(CGPoint)point { self.visible = value; } +LYNX_PROP_SETTER("ios-follow-mode-edge", iOSFollowModeEdge, BOOL) { self.followModeEdge = value; } + // legacy API LYNX_PROP_SETTER("allow-pan-gesture", setAllowPanGesture, BOOL) { self.allowPanGesture = value; } @@ -451,23 +525,9 @@ - (UIView *)windowContainer { - (UIViewController *)customViewController { if (![_customViewController isKindOfClass:self.customViewControllerClass]) { - _customViewController = nil; - UIResponder *responder = self.parent.view; - while (responder && ![responder isKindOfClass:self.customViewControllerClass]) { - responder = responder.nextResponder; - } - _customViewController = (UIViewController *)responder; - - // Can not find target UIViewController, try to find target UIView - if (!_customViewController) { - UIView *view = self.parent.view; - while (view && ![view isKindOfClass:self.customViewControllerClass]) { - view = view.superview; - } - // It's safe to cast from UIView to UIViewController. TODO: (xiamengfei.moonface) adapt to - // UIView - _customViewController = (UIViewController *)view; - } + _customViewController = + [LynxUIOverlayShadowNode findCustomViewControllerWith:self.customViewControllerClass + rootView:self.parent.view]; } return _customViewController; } diff --git a/platform/darwin/ios/lynx_xelement/scroll_coordinator/LynxUIScrollCoordinator.m b/platform/darwin/ios/lynx_xelement/scroll_coordinator/LynxUIScrollCoordinator.m index 18850a993f..234b514cec 100644 --- a/platform/darwin/ios/lynx_xelement/scroll_coordinator/LynxUIScrollCoordinator.m +++ b/platform/darwin/ios/lynx_xelement/scroll_coordinator/LynxUIScrollCoordinator.m @@ -63,6 +63,11 @@ - (instancetype)init { if (@available(iOS 11.0, *)) { self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; } + if (@available(iOS 13.0, *)) { + self.automaticallyAdjustsScrollIndicatorInsets = NO; + } + // TODO(xiamengfei.moonface): [ResizableWindowScale] Check whether this should use window or + // physical screen metrics. self.threshold = 1.0 / UIScreen.mainScreen.scale; self.lynxEnableTapGestureSimultaneously = YES; self.forceScrollDetachWhileIdling = YES; diff --git a/platform/darwin/ios/lynx_xelement/svg/LynxSVGRenderer.mm b/platform/darwin/ios/lynx_xelement/svg/LynxSVGRenderer.mm index a9fc85f325..1412c463b5 100644 --- a/platform/darwin/ios/lynx_xelement/svg/LynxSVGRenderer.mm +++ b/platform/darwin/ios/lynx_xelement/svg/LynxSVGRenderer.mm @@ -39,7 +39,7 @@ static CGSize LynxSVGDeviceSize(UIView *host, CGSize hostSize) { static void LynxSVGSetHostImage(UIView *host, UIImage *image) { host.layer.contents = (__bridge id)image.CGImage; - host.layer.contentsScale = UIScreen.mainScreen.scale; + host.layer.contentsScale = host.traitCollection.displayScale; } static dispatch_queue_t LynxSVGRenderQueue(void) { diff --git a/platform/darwin/ios/lynx_xelement/svg/LynxUISVG.m b/platform/darwin/ios/lynx_xelement/svg/LynxUISVG.m index a8f3937032..d099dc6302 100644 --- a/platform/darwin/ios/lynx_xelement/svg/LynxUISVG.m +++ b/platform/darwin/ios/lynx_xelement/svg/LynxUISVG.m @@ -143,8 +143,8 @@ - (void)updateLayoutIfNeed { LynxSVGView *imageView = self.view; // Image inside could be blurry due to the screen resolution after scaling. CGSize devSize = imageView.frame.size; - devSize.width *= [UIScreen mainScreen].scale; - devSize.height *= [UIScreen mainScreen].scale; + devSize.width *= self.context.screenMetrics.scale; + devSize.height *= self.context.screenMetrics.scale; if (devSize.width == 0 || devSize.height == 0) { // Clear the dirty mark before return. [self.view setImage:nil];