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
33 changes: 26 additions & 7 deletions shell/platform/darwin/macos/framework/Source/FlutterMutatorView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
#include "flutter/shell/platform/embedder/embedder.h"

@interface FlutterMutatorView () {
/// Each of these views clips to a CGPathRef. These views, if present,
/// are nested (first is child of FlutterMutatorView and last is parent of
// Each of these views clips to a CGPathRef. These views, if present,
// are nested (first is child of FlutterMutatorView and last is parent of
// _platformView).
NSMutableArray* _pathClipViews;

Expand All @@ -26,6 +26,21 @@ @interface FlutterMutatorView () {

@end

/// Superview container for platform views, to which sublayer transforms are applied.
@interface FlutterPlatformViewContainer : NSView
@end

@implementation FlutterPlatformViewContainer

- (BOOL)isFlipped {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe isYFlipped or isVerticallyFlipped? Someday there might be a system where the origin is in the lower right...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is Cocoa method that needs to be overriden to switch into a sane coordinate system...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, OK, so no choice there then.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Someday there might be a system where the origin is in the lower right...

Please don't give them any suggestions...

// Flutter transforms assume a coordinate system with an upper-left corner origin, with y
// coordinate values increasing downwards. This affects the view, view transforms, and
// sublayerTransforms.
return YES;
}

@end

/// View that clips that content to a specific CGPathRef.
/// Clipping is done through a CAShapeLayer mask, which avoids the need to
/// rasterize the mask.
Expand All @@ -43,6 +58,9 @@ - (instancetype)initWithFrame:(NSRect)frameRect {
}

- (BOOL)isFlipped {
// Flutter transforms assume a coordinate system with an upper-left corner origin, with y
// coordinate values increasing downwards. This affects the view, view transforms, and
// sublayerTransforms.
return YES;
}

Expand Down Expand Up @@ -400,7 +418,7 @@ - (void)updatePlatformViewWithBounds:(CGRect)untransformedBounds
clipRect:(CGRect)clipRect {
// Create the PlatformViewContainer view if necessary.
if (_platformViewContainer == nil) {
_platformViewContainer = [[NSView alloc] initWithFrame:self.bounds];
_platformViewContainer = [[FlutterPlatformViewContainer alloc] initWithFrame:self.bounds];
_platformViewContainer.wantsLayer = YES;
}

Expand All @@ -409,14 +427,15 @@ - (void)updatePlatformViewWithBounds:(CGRect)untransformedBounds
[containerSuperview addSubview:_platformViewContainer];
_platformViewContainer.frame = self.bounds;

// Add the
// Nest the platform view in the PlatformViewContainer.
[_platformViewContainer addSubview:_platformView];
_platformView.frame = untransformedBounds;

// Transform for the platform view is finalTransform adjusted for bounding rect origin.
_platformViewContainer.layer.sublayerTransform =
CATransform3DTranslate(transform, -transformedBounds.origin.x / transform.m11 /* scaleX */,
-transformedBounds.origin.y / transform.m22 /* scaleY */, 0);
CATransform3D translation =
CATransform3DMakeTranslation(-transformedBounds.origin.x, -transformedBounds.origin.y, 0);
transform = CATransform3DConcat(transform, translation);
_platformViewContainer.layer.sublayerTransform = transform;

// By default NSView clips children to frame. If masterClip is tighter than mutator view frame,
// the frame is set to masterClip and child offset adjusted to compensate for the difference.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,45 @@ void ExpectTransform3DEqual(const CATransform3D& t, const CATransform3D& u) {
EXPECT_EQ(mutatorView.pathClipViews.count, 0ul);
}

// Ensure that the mutator view, clip views, and container all use a flipped y axis. The transforms
// sent from the framework assume this, and so aside from the consistency with every other embedder,
// we can avoid a lot of extra math.
TEST(FlutterMutatorViewTest, ViewsSetIsFlipped) {
NSView* platformView = [[NSView alloc] init];
FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView];

std::vector<FlutterPlatformViewMutation> mutations{
{
.type = kFlutterPlatformViewMutationTypeClipRoundedRect,
.clip_rounded_rect =
FlutterRoundedRect{
.rect = FlutterRect{110, 60, 150, 150},
.upper_left_corner_radius = FlutterSize{10, 10},
.upper_right_corner_radius = FlutterSize{10, 10},
.lower_right_corner_radius = FlutterSize{10, 10},
.lower_left_corner_radius = FlutterSize{10, 10},
},
},
{
.type = kFlutterPlatformViewMutationTypeTransformation,
.transformation =
FlutterTransformation{
.scaleX = 1,
.transX = 100,
.scaleY = 1,
.transY = 50,
},
},
};

ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);

EXPECT_TRUE(mutatorView.isFlipped);
ASSERT_EQ(mutatorView.pathClipViews.count, 1ul);
EXPECT_TRUE(mutatorView.pathClipViews.firstObject.isFlipped);
EXPECT_TRUE(mutatorView.platformViewContainer.isFlipped);
}

TEST(FlutterMutatorViewTest, RoundRectClipsToPath) {
NSView* platformView = [[NSView alloc] init];
FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView];
Expand Down