mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[macOS] Top-left origin for PlatformView container (flutter/engine#42523)
For consistency with Flutter (and all other platforms), Flutter views in the macOS embedder set `isFlipped` to ensure a co-ordinate system with the origin in the top-left, with y co-ordinates increasing towards the bottom edge of the view. Previously, we were using a stock NSView as the container, which uses a bottom-left origin by default. Instead we extract the PlatformView container view as its own class with `isFlipped` true. This doesn't correct the issue of the view anchorpoint/position but does correct rotation direction. This also applies the transform back to origin prior to other transforms when adjusting the platformview position rather than after. Issue: https://github.com/flutter/flutter/issues/124490 [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
This commit is contained in:
parent
ce41b91c3b
commit
49e25a2dee
@ -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;
|
||||
|
||||
@ -26,6 +26,21 @@
|
||||
|
||||
@end
|
||||
|
||||
/// Superview container for platform views, to which sublayer transforms are applied.
|
||||
@interface FlutterPlatformViewContainer : NSView
|
||||
@end
|
||||
|
||||
@implementation FlutterPlatformViewContainer
|
||||
|
||||
- (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;
|
||||
}
|
||||
|
||||
@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.
|
||||
@ -43,6 +58,9 @@
|
||||
}
|
||||
|
||||
- (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;
|
||||
}
|
||||
|
||||
@ -400,7 +418,7 @@ NSMutableArray* RoundedRectClipsFromMutations(CGRect master_clip, const Mutation
|
||||
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;
|
||||
}
|
||||
|
||||
@ -409,14 +427,15 @@ NSMutableArray* RoundedRectClipsFromMutations(CGRect master_clip, const Mutation
|
||||
[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.
|
||||
|
||||
@ -301,6 +301,45 @@ TEST(FlutterMutatorViewTest, RoundRectClipsToSimpleRectangle) {
|
||||
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];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user