5#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMutatorView.h"
6#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h"
8#include <QuartzCore/QuartzCore.h>
10#include "flutter/fml/logging.h"
11#include "flutter/shell/platform/embedder/embedder.h"
13#import "flutter/shell/platform/darwin/macos/framework/Source/NSView+ClipsToBounds.h"
27 const std::vector<FlutterPlatformViewMutation>& mutations,
51- (
BOOL)cleanupScheduled {
57- (void)processMouseMoveEvent:(NSEvent*)event
59 overlayRegion:(const
std::vector<CGRect>&)region {
62 [[NSRunLoop mainRunLoop] performBlock:^{
73 NSPoint point = [view convertPoint:event.locationInWindow fromView:nil];
76 for (
const auto& r :
region) {
77 if (CGRectContainsPoint(r, point)) {
78 [_flutterView cursorUpdate:event];
87 [platformView.window invalidateCursorRectsForView:platformView];
125- (NSView*)hitTest:(NSPoint)point {
126 NSView* res = [
super hitTest:point];
127 return res !=
self ? res : nil;
150 self.wantsLayer = YES;
162- (NSView*)hitTest:(NSPoint)point {
163 NSView* res = [
super hitTest:point];
164 return res !=
self ? res : nil;
169- (void)maskToPath:(CGPathRef)
path withOrigin:(CGPoint)origin {
170 CAShapeLayer* maskLayer =
self.layer.mask;
171 if (maskLayer == nil) {
172 maskLayer = [CAShapeLayer layer];
173 self.layer.mask = maskLayer;
175 maskLayer.path =
path;
176 maskLayer.transform = CATransform3DMakeTranslation(-origin.x, -origin.y, 0);
183 CATransform3D
transform = CATransform3DIdentity;
195bool AffineTransformIsOnlyScaleOrTranslate(
const CGAffineTransform&
transform) {
200 return size.width == 0 &&
size.height == 0;
210 .top =
rect.origin.y,
211 .right =
rect.origin.x +
rect.size.width,
212 .bottom =
rect.origin.y +
rect.size.height,
218bool PointInsideEllipse(
const CGPoint& point,
const FlutterSize& radius) {
219 return (point.x * point.x) / (radius.
width * radius.
width) +
226 CGPoint inner_top_left =
231 CGPoint relative_top_left =
232 CGPointMake(
rect.left - inner_top_left.x,
rect.top - inner_top_left.y);
235 if (relative_top_left.x < 0 && relative_top_left.y < 0) {
242 CGPoint inner_top_right =
247 CGPoint relative_top_right =
248 CGPointMake(
rect.right - inner_top_right.x,
rect.top - inner_top_right.y);
251 if (relative_top_right.x > 0 && relative_top_right.y < 0) {
258 CGPoint inner_bottom_left =
263 CGPoint relative_bottom_left =
264 CGPointMake(
rect.left - inner_bottom_left.x,
rect.bottom - inner_bottom_left.y);
267 if (relative_bottom_left.x < 0 && relative_bottom_left.y > 0) {
274 CGPoint inner_bottom_right =
279 CGPoint relative_bottom_right =
280 CGPointMake(
rect.right - inner_bottom_right.x,
rect.bottom - inner_bottom_right.y);
283 if (relative_bottom_right.x > 0 && relative_bottom_right.y > 0) {
297 return CGPathCreateWithRect(FromFlutterRect(roundedRect.
rect),
nullptr);
300 CGMutablePathRef
path = CGPathCreateMutable();
302 const auto&
rect = roundedRect.
rect;
308 CGPathMoveToPoint(
path,
nullptr,
rect.left + topLeft.width,
rect.top);
309 CGPathAddLineToPoint(
path,
nullptr,
rect.right - topRight.width,
rect.top);
310 CGPathAddCurveToPoint(
path,
nullptr,
rect.right,
rect.top,
rect.right,
rect.top + topRight.height,
311 rect.right,
rect.top + topRight.height);
312 CGPathAddLineToPoint(
path,
nullptr,
rect.right,
rect.bottom - bottomRight.height);
313 CGPathAddCurveToPoint(
path,
nullptr,
rect.right,
rect.bottom,
rect.right - bottomRight.width,
314 rect.bottom,
rect.right - bottomRight.width,
rect.bottom);
315 CGPathAddLineToPoint(
path,
nullptr,
rect.left + bottomLeft.width,
rect.bottom);
317 rect.bottom - bottomLeft.height,
rect.left,
318 rect.bottom - bottomLeft.height);
319 CGPathAddLineToPoint(
path,
nullptr,
rect.left,
rect.top + topLeft.height);
321 rect.left + topLeft.width,
rect.top);
322 CGPathCloseSubpath(
path);
326using MutationVector = std::vector<FlutterPlatformViewMutation>;
332MutationVector MutationsForPlatformView(
const MutationVector& mutationsIn,
float scale) {
333 MutationVector mutations(mutationsIn);
335 mutations.insert(mutations.begin(), {
336 .type = kFlutterPlatformViewMutationTypeTransformation,
338 .scaleX = 1.0 / scale,
339 .scaleY = 1.0 / scale,
346CATransform3D CATransformFromMutations(
const MutationVector& mutations) {
347 CATransform3D
transform = CATransform3DIdentity;
348 for (
auto mutation : mutations) {
349 switch (mutation.type) {
351 CATransform3D mutationTransform = ToCATransform3D(mutation.transformation);
365float OpacityFromMutations(
const MutationVector& mutations) {
367 for (
auto mutation : mutations) {
368 switch (mutation.type) {
370 opacity *= mutation.opacity;
382CGRect MasterClipFromMutations(CGRect
bounds,
const MutationVector& mutations) {
385 CGRect master_clip =
bounds;
388 CATransform3D
transform = CATransform3DIdentity;
389 for (
auto mutation : mutations) {
390 switch (mutation.type) {
392 CGRect
rect = CGRectApplyAffineTransform(FromFlutterRect(mutation.clip_rect),
393 CATransform3DGetAffineTransform(
transform));
394 master_clip = CGRectIntersection(
rect, master_clip);
398 CGAffineTransform affineTransform = CATransform3DGetAffineTransform(
transform);
399 CGRect
rect = CGRectApplyAffineTransform(FromFlutterRect(mutation.clip_rounded_rect.rect),
401 master_clip = CGRectIntersection(
rect, master_clip);
421NSMutableArray* ClipPathFromMutations(CGRect master_clip,
const MutationVector& mutations) {
422 std::vector<ClipRoundedRect> rounded_rects;
424 CATransform3D
transform = CATransform3DIdentity;
425 for (
auto mutation : mutations) {
426 switch (mutation.type) {
428 CGAffineTransform affineTransform = CATransform3DGetAffineTransform(
transform);
429 rounded_rects.push_back({mutation.clip_rounded_rect, affineTransform});
436 CGAffineTransform affineTransform = CATransform3DGetAffineTransform(
transform);
438 if (!AffineTransformIsOnlyScaleOrTranslate(affineTransform)) {
439 rounded_rects.push_back(
451 NSMutableArray* paths = [NSMutableArray array];
452 for (
const auto& r : rounded_rects) {
453 bool requiresPath = !AffineTransformIsOnlyScaleOrTranslate(r.transform);
455 CGAffineTransform inverse = CGAffineTransformInvert(r.transform);
458 CGRect localMasterClip = CGRectApplyAffineTransform(master_clip, inverse);
459 requiresPath = RoundRectCornerIntersects(r.rrect, ToFlutterRect(localMasterClip));
465 CGPathRef
path = PathFromRoundedRect(r.rrect);
466 CGPathRef transformedPath = CGPathCreateCopyByTransformingPath(
path, &r.transform);
467 [paths addObject:(__bridge id)transformedPath];
468 CGPathRelease(transformedPath);
480- (NSView*)hitTest:(NSPoint)point {
499- (instancetype)initWithPlatformView:(NSView*)
platformView {
500 return [
self initWithPlatformView:platformView cursorCoordiator:nil];
503- (instancetype)initWithPlatformView:(NSView*)
platformView
509 self.wantsLayer = YES;
510 self.clipsToBounds = YES;
513 [
self addSubview:_trackingAreaContainer];
515 NSTrackingAreaOptions
options = NSTrackingMouseMoved | NSTrackingInVisibleRect |
516 NSTrackingEnabledDuringMouseDrag | NSTrackingActiveAlways;
517 _trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect
521 [_trackingAreaContainer addTrackingArea:_trackingArea];
527 self->_hitTestIgnoreRegion.clear();
530- (void)addHitTestIgnoreRegion:(CGRect)
region {
534- (void)mouseMoved:(NSEvent*)
event {
535 [_cursorCoordinator processMouseMoveEvent:event
537 overlayRegion:_hitTestIgnoreRegion];
540- (NSView*)hitTest:(NSPoint)point {
541 CGPoint localPoint = point;
542 localPoint.x -=
self.frame.origin.x;
543 localPoint.y -=
self.frame.origin.y;
545 if (CGRectContainsPoint(
region, localPoint)) {
549 return [
super hitTest:point];
558 return self.superview != nil ?
self.superview.layer.contentsScale : 1.0;
562- (void)updatePathClipViewsWithPaths:(NSArray*)paths {
566 [view removeFromSuperview];
567 [_pathClipViews removeLastObject];
573 [_pathClipViews addObject:pathClipView];
574 [superView addSubview:pathClipView];
579 pathClipView.frame =
self.bounds;
580 [pathClipView
maskToPath:(__bridge CGPathRef)[paths objectAtIndex:i]
590- (void)updatePlatformViewWithBounds:(CGRect)untransformedBounds
591 transformedBounds:(CGRect)transformedBounds
602 [containerSuperview addSubview:_platformViewContainer];
609 [_platformViewContainer addSubview:_platformView];
619 CATransform3D translation =
620 CATransform3DMakeTranslation(-transformedBounds.origin.x, -transformedBounds.origin.y, 0);
626 if (!CGRectEqualToRect(
clipRect, transformedBounds)) {
627 NSMutableArray<NSView*>* subviews = [NSMutableArray arrayWithArray:self.subviews];
628 [subviews removeObject:_trackingAreaContainer];
630 auto subview = subviews.firstObject;
631 FML_DCHECK(subview.frame.origin.x == 0 && subview.frame.origin.y == 0);
632 subview.frame = CGRectMake(transformedBounds.origin.x -
clipRect.origin.x,
633 transformedBounds.origin.y -
clipRect.origin.y,
634 subview.frame.size.width, subview.frame.size.height);
645 CGFloat
scale = [
self contentsScale];
646 MutationVector mutations = MutationsForPlatformView(layer->mutations(),
scale);
648 CATransform3D finalTransform = CATransformFromMutations(mutations);
652 CGRect untransformedBoundingRect =
653 CGRectMake(0, 0, layer->size().width /
scale, layer->size().height /
scale);
654 CGRect finalBoundingRect = CGRectApplyAffineTransform(
655 untransformedBoundingRect, CATransform3DGetAffineTransform(finalTransform));
656 self.frame = finalBoundingRect;
659 self.layer.opacity = OpacityFromMutations(mutations);
662 CGRect masterClip = MasterClipFromMutations(finalBoundingRect, mutations);
663 if (CGRectIsNull(masterClip)) {
670 NSMutableArray* paths = ClipPathFromMutations(masterClip, mutations);
671 [
self updatePathClipViewsWithPaths:paths];
674 [
self updatePlatformViewWithBounds:untransformedBoundingRect
675 transformedBounds:finalBoundingRect
676 transform:finalTransform
677 clipRect:masterClip];
679 [
self addSubview:_trackingAreaContainer positioned:(NSWindowAbove)relativeTo:nil];
NSView * platformViewContainer
NSMutableArray< NSView * > * pathClipViews
static SkString identifier(const FontFamilyDesc &family, const FontDesc &font)
int64_t FlutterPlatformViewIdentifier
@ kFlutterLayerContentTypePlatformView
Indicates that the contents of this layer are determined by the embedder.
@ kFlutterPlatformViewMutationTypeClipRoundedRect
@ kFlutterPlatformViewMutationTypeClipRect
@ kFlutterPlatformViewMutationTypeTransformation
@ kFlutterPlatformViewMutationTypeOpacity
#define FML_CHECK(condition)
#define FML_DCHECK(condition)
NSTrackingArea * _trackingArea
NSView * platformView
Returns wrapped platform view.
FlutterCursorCoordinator * _cursorCoordinator
NSView * _platformViewContainer
void resetHitTestRegion()
Resets hit hit testing region for this mutator view.
CGFloat contentsScale()
Returns the scale factor to translate logical pixels to physical pixels for this view.
std::vector< CGRect > _hitTestIgnoreRegion
NSView * _trackingAreaContainer
NSMutableArray * _pathClipViews
void maskToPath:withOrigin:(CGPathRef path, [withOrigin] CGPoint origin)
instancetype initWithFrame
fml::scoped_nsobject< FlutterView > _flutterView
Optional< SkRect > bounds
clipRect(r.rect, r.opAA.op(), r.opAA.aa())) template<> void Draw
ClipOpAndAA opAA SkRegion region
sk_sp< SkBlender > blender SkRect rect
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
FlutterLayerContentType type
const FlutterPlatformView * platform_view
FlutterSize size
The size of the layer (in physical pixels).
A structure to represent a 2D point.
A structure to represent a rectangle.
A structure to represent a rounded rectangle.
FlutterSize upper_left_corner_radius
FlutterSize lower_left_corner_radius
FlutterSize upper_right_corner_radius
FlutterSize lower_right_corner_radius
A structure to represent the width and height.