Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,6 @@ targets:
jazzy_version: "0.14.1"
runtime_versions: >-
[
"ios-13-0",
"ios-16-0_14a5294e"
]
timeout: 75
Expand Down
63 changes: 47 additions & 16 deletions shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,52 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
}
@end

// Determines if the final `clipBounds` from a clipRect/clipRRect/clipPath mutator contains the
// Determines if the `clip_rect` from a clipRect mutator contains the
// `platformview_boundingrect`.
//
// `clip_bounds` is the bounding rect of the rect/rrect/path in the clipRect/clipRRect/clipPath
// mutator. This rect is in its own coordinate space. The rect needs to be transformed by
// `clip_rect` is in its own coordinate space. The rect needs to be transformed by
// `transform_matrix` to be in the coordinate space where the PlatformView is displayed.
//
// `platformview_boundingrect` is the final bounding rect of the PlatformView in the coordinate
// space where the PlatformView is displayed.
static bool ClipBoundsContainsPlatformViewBoundingRect(const SkRect& clip_bounds,
const SkRect& platformview_boundingrect,
const SkMatrix& transform_matrix) {
SkRect transforme_clip_bounds = transform_matrix.mapRect(clip_bounds);
return transforme_clip_bounds.contains(platformview_boundingrect);
static bool ClipRectContainsPlatformViewBoundingRect(const SkRect& clip_rect,
const SkRect& platformview_boundingrect,
const SkMatrix& transform_matrix) {
SkRect transformed_rect = transform_matrix.mapRect(clip_rect);
return transformed_rect.contains(platformview_boundingrect);
}

// Determines if the `clipRRect` from a clipRRect mutator contains the
// `platformview_boundingrect`.
//
// `clip_rrect` is in its own coordinate space. The rrect needs to be transformed by
// `transform_matrix` to be in the coordinate space where the PlatformView is displayed.
//
// `platformview_boundingrect` is the final bounding rect of the PlatformView in the coordinate
// space where the PlatformView is displayed.
static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect,
const SkRect& platformview_boundingrect,
const SkMatrix& transform_matrix) {
SkVector upper_left = clip_rrect.radii(SkRRect::Corner::kUpperLeft_Corner);
SkVector upper_right = clip_rrect.radii(SkRRect::Corner::kUpperRight_Corner);
SkVector lower_right = clip_rrect.radii(SkRRect::Corner::kLowerRight_Corner);
SkVector lower_left = clip_rrect.radii(SkRRect::Corner::kLowerLeft_Corner);
SkScalar transformed_upper_left_x = transform_matrix.mapRadius(upper_left.x());
SkScalar transformed_upper_left_y = transform_matrix.mapRadius(upper_left.y());
SkScalar transformed_upper_right_x = transform_matrix.mapRadius(upper_right.x());
SkScalar transformed_upper_right_y = transform_matrix.mapRadius(upper_right.y());
SkScalar transformed_lower_right_x = transform_matrix.mapRadius(lower_right.x());
SkScalar transformed_lower_right_y = transform_matrix.mapRadius(lower_right.y());
SkScalar transformed_lower_left_x = transform_matrix.mapRadius(lower_left.x());
SkScalar transformed_lower_left_y = transform_matrix.mapRadius(lower_left.y());
SkRect transformed_clip_rect = transform_matrix.mapRect(clip_rrect.rect());
SkRRect transformed_rrect;
SkVector corners[] = {{transformed_upper_left_x, transformed_upper_left_y},
{transformed_upper_right_x, transformed_upper_right_y},
{transformed_lower_right_x, transformed_lower_right_y},
{transformed_lower_left_x, transformed_lower_left_y}};
transformed_rrect.setRectRadii(transformed_clip_rect, corners);
return transformed_rrect.contains(platformview_boundingrect);
}

namespace flutter {
Expand Down Expand Up @@ -450,28 +482,27 @@ static bool ClipBoundsContainsPlatformViewBoundingRect(const SkRect& clip_bounds
break;
}
case kClipRect: {
if (ClipBoundsContainsPlatformViewBoundingRect((*iter)->GetRect(), bounding_rect,
transformMatrix)) {
if (ClipRectContainsPlatformViewBoundingRect((*iter)->GetRect(), bounding_rect,
transformMatrix)) {
break;
}
[maskView clipRect:(*iter)->GetRect() matrix:transformMatrix];
clipView.maskView = maskView;
break;
}
case kClipRRect: {
if (ClipBoundsContainsPlatformViewBoundingRect((*iter)->GetRRect().getBounds(),
bounding_rect, transformMatrix)) {
if (ClipRRectContainsPlatformViewBoundingRect((*iter)->GetRRect(), bounding_rect,
transformMatrix)) {
break;
}
[maskView clipRRect:(*iter)->GetRRect() matrix:transformMatrix];
clipView.maskView = maskView;
break;
}
case kClipPath: {
if (ClipBoundsContainsPlatformViewBoundingRect((*iter)->GetPath().getBounds(),
bounding_rect, transformMatrix)) {
break;
}
// TODO(cyanglaz): Find a way to pre-determine if path contains the PlatformView boudning
// rect. See `ClipRRectContainsPlatformViewBoundingRect`.
// https://github.com/flutter/flutter/issues/118650
[maskView clipPath:(*iter)->GetPath() matrix:transformMatrix];
clipView.maskView = maskView;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1510,25 +1510,23 @@ - (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView {

UIView* mockFlutterView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)] autorelease];
flutterPlatformViewsController->SetFlutterView(mockFlutterView);
// Create embedded view params
// Create embedded view params.
flutter::MutatorsStack stack;
// Layer tree always pushes a screen scale factor to the stack
// Layer tree always pushes a screen scale factor to the stack.
SkMatrix screenScaleMatrix =
SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
stack.PushTransform(screenScaleMatrix);
SkMatrix translateMatrix = SkMatrix::Translate(5, 5);
// The platform view's rect for this test will be (5, 5, 10, 10)
// The platform view's rect for this test will be (5, 5, 10, 10).
stack.PushTransform(translateMatrix);
// Push a clip rect, big enough to contain the entire platform view bound
// Push a clip rect, big enough to contain the entire platform view bound.
SkRect rect = SkRect::MakeXYWH(0, 0, 25, 25);
stack.PushClipRect(rect);
// Push a clip rrect, big enough to contain the entire platform view bound
SkRect rect_for_rrect = SkRect::MakeXYWH(0, 0, 24, 24);
// Push a clip rrect, big enough to contain the entire platform view bound without clipping it.
// Make the origin (-1, -1) so that the top left rounded corner isn't clipping the PlatformView.
SkRect rect_for_rrect = SkRect::MakeXYWH(-1, -1, 25, 25);
SkRRect rrect = SkRRect::MakeRectXY(rect_for_rrect, 1, 1);
stack.PushClipRRect(rrect);
// Push a clip path, big enough to contain the entire platform view bound
SkPath path = SkPath::RRect(SkRect::MakeXYWH(0, 0, 23, 23), 1, 1);
stack.PushClipPath(path);

auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
SkMatrix::Concat(screenScaleMatrix, translateMatrix), SkSize::Make(5, 5), stack);
Expand All @@ -1542,10 +1540,73 @@ - (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView {

[mockFlutterView setNeedsLayout];
[mockFlutterView layoutIfNeeded];

XCTAssertNil(childClippingView.maskView);
}

- (void)testClipRRectOnlyHasCornersInterceptWithPlatformViewShouldAddMaskView {
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
/*platform=*/thread_task_runner,
/*raster=*/thread_task_runner,
/*ui=*/thread_task_runner,
/*io=*/thread_task_runner);
auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
/*delegate=*/mock_delegate,
/*rendering_api=*/flutter::IOSRenderingAPI::kSoftware,
/*platform_views_controller=*/flutterPlatformViewsController,
/*task_runners=*/runners);

FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
[[FlutterPlatformViewsTestMockFlutterPlatformFactory new] autorelease];
flutterPlatformViewsController->RegisterViewFactory(
factory, @"MockFlutterPlatformView",
FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
FlutterResult result = ^(id result) {
};
flutterPlatformViewsController->OnMethodCall(
[FlutterMethodCall
methodCallWithMethodName:@"create"
arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
result);

XCTAssertNotNil(gMockPlatformView);

UIView* mockFlutterView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)] autorelease];
flutterPlatformViewsController->SetFlutterView(mockFlutterView);
// Create embedded view params
flutter::MutatorsStack stack;
// Layer tree always pushes a screen scale factor to the stack.
SkMatrix screenScaleMatrix =
SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
stack.PushTransform(screenScaleMatrix);
SkMatrix translateMatrix = SkMatrix::Translate(5, 5);
// The platform view's rect for this test will be (5, 5, 10, 10).
stack.PushTransform(translateMatrix);

// Push a clip rrect, the rect of the rrect is the same as the PlatformView of the corner should.
// clip the PlatformView.
SkRect rect_for_rrect = SkRect::MakeXYWH(0, 0, 10, 10);
SkRRect rrect = SkRRect::MakeRectXY(rect_for_rrect, 1, 1);
stack.PushClipRRect(rrect);

auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
SkMatrix::Concat(screenScaleMatrix, translateMatrix), SkSize::Make(5, 5), stack);

flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
flutterPlatformViewsController->CompositeEmbeddedView(2);
gMockPlatformView.backgroundColor = UIColor.redColor;
XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
[mockFlutterView addSubview:childClippingView];

[mockFlutterView setNeedsLayout];
[mockFlutterView layoutIfNeeded];

XCTAssertNotNil(childClippingView.maskView);
}

- (void)testClipRect {
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
Expand Down
2 changes: 1 addition & 1 deletion testing/scenario_app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Build the `ios_debug_sim_unopt` engine variant, and run
in your shell.

To run or debug in Xcode, open the xcodeproj file located in
`<engine_out_dir>/ios_debug_sim_unopt/scenario_app/Scenarios/Scenaios.xcodeproj`.
`<engine_out_dir>/ios_debug_sim_unopt/scenario_app/Scenarios/Scenarios.xcodeproj`.

### iOS Platform View Tests

Expand Down
Loading