Flutter Engine
The Flutter Engine
FlutterMutatorView.mm
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMutatorView.h"
6#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h"
7
8#include <QuartzCore/QuartzCore.h>
9
10#include "flutter/fml/logging.h"
11#include "flutter/shell/platform/embedder/embedder.h"
12
13#import "flutter/shell/platform/darwin/macos/framework/Source/NSView+ClipsToBounds.h"
14
15namespace flutter {
18 const auto* platform_view = layer->platform_view;
19 identifier_ = platform_view->identifier;
20 for (size_t i = 0; i < platform_view->mutations_count; i++) {
21 mutations_.push_back(*platform_view->mutations[i]);
22 }
23 offset_ = layer->offset;
24 size_ = layer->size;
25}
27 const std::vector<FlutterPlatformViewMutation>& mutations,
30 : identifier_(identifier), mutations_(mutations), offset_(offset), size_(size) {}
31} // namespace flutter
32
33@implementation FlutterCursorCoordinator {
37}
38
39- (FlutterCursorCoordinator*)initWithFlutterView:(FlutterView*)flutterView {
40 if (self = [super init]) {
41 _flutterView = flutterView;
42 }
43 return self;
44}
45
46- (void)frameCleanup {
49}
50
51- (BOOL)cleanupScheduled {
52 return _cleanupScheduled;
53}
54
55// Processes the mouse event from given mutator view. This is called for each mutator view, in
56// z-order, from the top most down.
57- (void)processMouseMoveEvent:(NSEvent*)event
58 forMutatorView:(FlutterMutatorView*)view
59 overlayRegion:(const std::vector<CGRect>&)region {
60 // [self frameCleanup] will be called once after current run loop turn.
61 if (!_cleanupScheduled) {
62 [[NSRunLoop mainRunLoop] performBlock:^{
63 [self frameCleanup];
64 }];
66 }
67
68 // Mouse move was already handled by a mutator view above.
70 return;
71 }
72
73 NSPoint point = [view convertPoint:event.locationInWindow fromView:nil];
74
75 // If the mouse is above overlay region restore current Flutter cursor.
76 for (const auto& r : region) {
77 if (CGRectContainsPoint(r, point)) {
78 [_flutterView cursorUpdate:event];
80 return;
81 }
82 }
83 NSView* platformView = view.platformView;
84 // It is possible that Flutter changed mouse cursor while the mouse was inside
85 // cursor rect. Unfocused NSTextField uses legacy cursor rects for changing
86 // its cursor.
87 [platformView.window invalidateCursorRectsForView:platformView];
89}
90@end
91
92@interface FlutterMutatorView () {
93 // Each of these views clips to a CGPathRef. These views, if present,
94 // are nested (first is child of FlutterMutatorView and last is parent of
95 // _platformView).
96 NSMutableArray* _pathClipViews;
97
98 // View right above the platform view. Used to apply the final transform
99 // (sans the translation) to the platform view.
101
103
105
106 // Container view that hosts the tracking area. Must be above platform view
107 // so that it gets the mouseMove event first.
109
110 // Tracking area used to update cursor when moving over overlay region.
111 NSTrackingArea* _trackingArea;
112
113 // Region of the overlay that should be ignored for hit testing.
114 std::vector<CGRect> _hitTestIgnoreRegion;
115}
116
117@end
118
119/// Superview container for platform views, to which sublayer transforms are applied.
121@end
122
123@implementation FlutterPlatformViewContainer
124
125- (NSView*)hitTest:(NSPoint)point {
126 NSView* res = [super hitTest:point];
127 return res != self ? res : nil;
128}
129
130- (BOOL)isFlipped {
131 // Flutter transforms assume a coordinate system with an upper-left corner origin, with y
132 // coordinate values increasing downwards. This affects the view, view transforms, and
133 // sublayerTransforms.
134 return YES;
135}
136
137@end
138
139/// View that clips that content to a specific CGPathRef.
140/// Clipping is done through a CAShapeLayer mask, which avoids the need to
141/// rasterize the mask.
142@interface FlutterPathClipView : NSView
143
144@end
145
146@implementation FlutterPathClipView
147
148- (instancetype)initWithFrame:(NSRect)frameRect {
149 if (self = [super initWithFrame:frameRect]) {
150 self.wantsLayer = YES;
151 }
152 return self;
153}
154
155- (BOOL)isFlipped {
156 // Flutter transforms assume a coordinate system with an upper-left corner origin, with y
157 // coordinate values increasing downwards. This affects the view, view transforms, and
158 // sublayerTransforms.
159 return YES;
160}
161
162- (NSView*)hitTest:(NSPoint)point {
163 NSView* res = [super hitTest:point];
164 return res != self ? res : nil;
165}
166
167/// Clip the view to the given path. Offset top left corner of platform view
168/// in global logical coordinates.
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;
174 }
175 maskLayer.path = path;
176 maskLayer.transform = CATransform3DMakeTranslation(-origin.x, -origin.y, 0);
177}
178
179@end
180
181namespace {
182CATransform3D ToCATransform3D(const FlutterTransformation& t) {
183 CATransform3D transform = CATransform3DIdentity;
184 transform.m11 = t.scaleX;
185 transform.m21 = t.skewX;
186 transform.m41 = t.transX;
187 transform.m14 = t.pers0;
188 transform.m12 = t.skewY;
189 transform.m22 = t.scaleY;
190 transform.m42 = t.transY;
191 transform.m24 = t.pers1;
192 return transform;
193}
194
195bool AffineTransformIsOnlyScaleOrTranslate(const CGAffineTransform& transform) {
196 return transform.b == 0 && transform.c == 0;
197}
198
199bool IsZeroSize(const FlutterSize size) {
200 return size.width == 0 && size.height == 0;
201}
202
203CGRect FromFlutterRect(const FlutterRect& rect) {
204 return CGRectMake(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
205}
206
207FlutterRect ToFlutterRect(const CGRect& rect) {
208 return FlutterRect{
209 .left = rect.origin.x,
210 .top = rect.origin.y,
211 .right = rect.origin.x + rect.size.width,
212 .bottom = rect.origin.y + rect.size.height,
213
214 };
215}
216
217/// Returns whether the point is inside ellipse with given radius (centered at 0, 0).
218bool PointInsideEllipse(const CGPoint& point, const FlutterSize& radius) {
219 return (point.x * point.x) / (radius.width * radius.width) +
220 (point.y * point.y) / (radius.height * radius.height) <
221 1.0;
222}
223
224bool RoundRectCornerIntersects(const FlutterRoundedRect& roundRect, const FlutterRect& rect) {
225 // Inner coordinate of the top left corner of the round rect.
226 CGPoint inner_top_left =
227 CGPointMake(roundRect.rect.left + roundRect.upper_left_corner_radius.width,
228 roundRect.rect.top + roundRect.upper_left_corner_radius.height);
229
230 // Position of `rect` corner relative to inner_top_left.
231 CGPoint relative_top_left =
232 CGPointMake(rect.left - inner_top_left.x, rect.top - inner_top_left.y);
233
234 // `relative_top_left` is in upper left quadrant.
235 if (relative_top_left.x < 0 && relative_top_left.y < 0) {
236 if (!PointInsideEllipse(relative_top_left, roundRect.upper_left_corner_radius)) {
237 return true;
238 }
239 }
240
241 // Inner coordinate of the top right corner of the round rect.
242 CGPoint inner_top_right =
243 CGPointMake(roundRect.rect.right - roundRect.upper_right_corner_radius.width,
244 roundRect.rect.top + roundRect.upper_right_corner_radius.height);
245
246 // Positon of `rect` corner relative to inner_top_right.
247 CGPoint relative_top_right =
248 CGPointMake(rect.right - inner_top_right.x, rect.top - inner_top_right.y);
249
250 // `relative_top_right` is in top right quadrant.
251 if (relative_top_right.x > 0 && relative_top_right.y < 0) {
252 if (!PointInsideEllipse(relative_top_right, roundRect.upper_right_corner_radius)) {
253 return true;
254 }
255 }
256
257 // Inner coordinate of the bottom left corner of the round rect.
258 CGPoint inner_bottom_left =
259 CGPointMake(roundRect.rect.left + roundRect.lower_left_corner_radius.width,
260 roundRect.rect.bottom - roundRect.lower_left_corner_radius.height);
261
262 // Position of `rect` corner relative to inner_bottom_left.
263 CGPoint relative_bottom_left =
264 CGPointMake(rect.left - inner_bottom_left.x, rect.bottom - inner_bottom_left.y);
265
266 // `relative_bottom_left` is in bottom left quadrant.
267 if (relative_bottom_left.x < 0 && relative_bottom_left.y > 0) {
268 if (!PointInsideEllipse(relative_bottom_left, roundRect.lower_left_corner_radius)) {
269 return true;
270 }
271 }
272
273 // Inner coordinate of the bottom right corner of the round rect.
274 CGPoint inner_bottom_right =
275 CGPointMake(roundRect.rect.right - roundRect.lower_right_corner_radius.width,
276 roundRect.rect.bottom - roundRect.lower_right_corner_radius.height);
277
278 // Position of `rect` corner relative to inner_bottom_right.
279 CGPoint relative_bottom_right =
280 CGPointMake(rect.right - inner_bottom_right.x, rect.bottom - inner_bottom_right.y);
281
282 // `relative_bottom_right` is in bottom right quadrant.
283 if (relative_bottom_right.x > 0 && relative_bottom_right.y > 0) {
284 if (!PointInsideEllipse(relative_bottom_right, roundRect.lower_right_corner_radius)) {
285 return true;
286 }
287 }
288
289 return false;
290}
291
292CGPathRef PathFromRoundedRect(const FlutterRoundedRect& roundedRect) {
293 if (IsZeroSize(roundedRect.lower_left_corner_radius) &&
294 IsZeroSize(roundedRect.lower_right_corner_radius) &&
295 IsZeroSize(roundedRect.upper_left_corner_radius) &&
296 IsZeroSize(roundedRect.upper_right_corner_radius)) {
297 return CGPathCreateWithRect(FromFlutterRect(roundedRect.rect), nullptr);
298 }
299
300 CGMutablePathRef path = CGPathCreateMutable();
301
302 const auto& rect = roundedRect.rect;
303 const auto& topLeft = roundedRect.upper_left_corner_radius;
304 const auto& topRight = roundedRect.upper_right_corner_radius;
305 const auto& bottomLeft = roundedRect.lower_left_corner_radius;
306 const auto& bottomRight = roundedRect.lower_right_corner_radius;
307
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);
316 CGPathAddCurveToPoint(path, nullptr, rect.left, rect.bottom, rect.left,
317 rect.bottom - bottomLeft.height, rect.left,
318 rect.bottom - bottomLeft.height);
319 CGPathAddLineToPoint(path, nullptr, rect.left, rect.top + topLeft.height);
320 CGPathAddCurveToPoint(path, nullptr, rect.left, rect.top, rect.left + topLeft.width, rect.top,
321 rect.left + topLeft.width, rect.top);
322 CGPathCloseSubpath(path);
323 return path;
324}
325
326using MutationVector = std::vector<FlutterPlatformViewMutation>;
327
328/// Returns a vector of FlutterPlatformViewMutation object pointers associated with a platform view.
329/// The transforms sent from the engine include a transform from logical to physical coordinates.
330/// Since Cocoa deals only in logical points, this function prepends a scale transform that scales
331/// back from physical to logical coordinates to compensate.
332MutationVector MutationsForPlatformView(const MutationVector& mutationsIn, float scale) {
333 MutationVector mutations(mutationsIn);
334
335 mutations.insert(mutations.begin(), {
336 .type = kFlutterPlatformViewMutationTypeTransformation,
337 .transformation{
338 .scaleX = 1.0 / scale,
339 .scaleY = 1.0 / scale,
340 },
341 });
342 return mutations;
343}
344
345/// Returns the composition of all transformation mutations in the mutations vector.
346CATransform3D CATransformFromMutations(const MutationVector& mutations) {
347 CATransform3D transform = CATransform3DIdentity;
348 for (auto mutation : mutations) {
349 switch (mutation.type) {
351 CATransform3D mutationTransform = ToCATransform3D(mutation.transformation);
352 transform = CATransform3DConcat(mutationTransform, transform);
353 break;
354 }
358 break;
359 }
360 }
361 return transform;
362}
363
364/// Returns the opacity for all opacity mutations in the mutations vector.
365float OpacityFromMutations(const MutationVector& mutations) {
366 float opacity = 1.0;
367 for (auto mutation : mutations) {
368 switch (mutation.type) {
370 opacity *= mutation.opacity;
371 break;
375 break;
376 }
377 }
378 return opacity;
379}
380
381/// Returns the clip rect generated by the intersection of clips in the mutations vector.
382CGRect MasterClipFromMutations(CGRect bounds, const MutationVector& mutations) {
383 // Master clip in global logical coordinates. This is intersection of all clip rectangles
384 // present in mutators.
385 CGRect master_clip = bounds;
386
387 // Create the initial transform.
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);
395 break;
396 }
398 CGAffineTransform affineTransform = CATransform3DGetAffineTransform(transform);
399 CGRect rect = CGRectApplyAffineTransform(FromFlutterRect(mutation.clip_rounded_rect.rect),
400 affineTransform);
401 master_clip = CGRectIntersection(rect, master_clip);
402 break;
403 }
405 transform = CATransform3DConcat(ToCATransform3D(mutation.transformation), transform);
406 break;
408 break;
409 }
410 }
411 return master_clip;
412}
413
414/// A rounded rectangle and transform associated with it.
415typedef struct {
417 CGAffineTransform transform;
418} ClipRoundedRect;
419
420/// Returns the set of all rounded rect paths generated by clips in the mutations vector.
421NSMutableArray* ClipPathFromMutations(CGRect master_clip, const MutationVector& mutations) {
422 std::vector<ClipRoundedRect> rounded_rects;
423
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});
430 break;
431 }
433 transform = CATransform3DConcat(ToCATransform3D(mutation.transformation), transform);
434 break;
436 CGAffineTransform affineTransform = CATransform3DGetAffineTransform(transform);
437 // Shearing or rotation requires path clipping.
438 if (!AffineTransformIsOnlyScaleOrTranslate(affineTransform)) {
439 rounded_rects.push_back(
440 {FlutterRoundedRect{mutation.clip_rect, FlutterSize{0, 0}, FlutterSize{0, 0},
441 FlutterSize{0, 0}, FlutterSize{0, 0}},
442 affineTransform});
443 }
444 break;
445 }
447 break;
448 }
449 }
450
451 NSMutableArray* paths = [NSMutableArray array];
452 for (const auto& r : rounded_rects) {
453 bool requiresPath = !AffineTransformIsOnlyScaleOrTranslate(r.transform);
454 if (!requiresPath) {
455 CGAffineTransform inverse = CGAffineTransformInvert(r.transform);
456 // Transform master clip to clip rect coordinates and check if this view intersects one of the
457 // corners, which means we need to use path clipping.
458 CGRect localMasterClip = CGRectApplyAffineTransform(master_clip, inverse);
459 requiresPath = RoundRectCornerIntersects(r.rrect, ToFlutterRect(localMasterClip));
460 }
461
462 // Only clip to rounded rectangle path if the view intersects some of the round corners. If
463 // not, clipping to masterClip is enough.
464 if (requiresPath) {
465 CGPathRef path = PathFromRoundedRect(r.rrect);
466 CGPathRef transformedPath = CGPathCreateCopyByTransformingPath(path, &r.transform);
467 [paths addObject:(__bridge id)transformedPath];
468 CGPathRelease(transformedPath);
469 CGPathRelease(path);
470 }
471 }
472 return paths;
473}
474} // namespace
475
477@end
478
479@implementation FlutterTrackingAreaContainer
480- (NSView*)hitTest:(NSPoint)point {
481 return nil;
482}
483@end
484
485@implementation FlutterMutatorView
486
487- (NSView*)platformView {
488 return _platformView;
489}
490
491- (NSMutableArray*)pathClipViews {
492 return _pathClipViews;
493}
494
495- (NSView*)platformViewContainer {
497}
498
499- (instancetype)initWithPlatformView:(NSView*)platformView {
500 return [self initWithPlatformView:platformView cursorCoordiator:nil];
501}
502
503- (instancetype)initWithPlatformView:(NSView*)platformView
504 cursorCoordiator:(FlutterCursorCoordinator*)coordinator {
505 if (self = [super initWithFrame:NSZeroRect]) {
507 _pathClipViews = [NSMutableArray array];
508 _cursorCoordinator = coordinator;
509 self.wantsLayer = YES;
510 self.clipsToBounds = YES;
511
512 _trackingAreaContainer = [[FlutterTrackingAreaContainer alloc] initWithFrame:NSZeroRect];
513 [self addSubview:_trackingAreaContainer];
514
515 NSTrackingAreaOptions options = NSTrackingMouseMoved | NSTrackingInVisibleRect |
516 NSTrackingEnabledDuringMouseDrag | NSTrackingActiveAlways;
517 _trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect
518 options:options
519 owner:self
520 userInfo:nil];
521 [_trackingAreaContainer addTrackingArea:_trackingArea];
522 }
523 return self;
524}
525
526- (void)resetHitTestRegion {
527 self->_hitTestIgnoreRegion.clear();
528}
529
530- (void)addHitTestIgnoreRegion:(CGRect)region {
531 self->_hitTestIgnoreRegion.push_back(region);
532}
533
534- (void)mouseMoved:(NSEvent*)event {
535 [_cursorCoordinator processMouseMoveEvent:event
536 forMutatorView:self
537 overlayRegion:_hitTestIgnoreRegion];
538}
539
540- (NSView*)hitTest:(NSPoint)point {
541 CGPoint localPoint = point;
542 localPoint.x -= self.frame.origin.x;
543 localPoint.y -= self.frame.origin.y;
544 for (const auto& region : _hitTestIgnoreRegion) {
545 if (CGRectContainsPoint(region, localPoint)) {
546 return nil;
547 }
548 }
549 return [super hitTest:point];
550}
551
552- (BOOL)isFlipped {
553 return YES;
554}
555
556/// Returns the scale factor to translate logical pixels to physical pixels for this view.
557- (CGFloat)contentsScale {
558 return self.superview != nil ? self.superview.layer.contentsScale : 1.0;
559}
560
561/// Updates the nested stack of clip views that host the platform view.
562- (void)updatePathClipViewsWithPaths:(NSArray*)paths {
563 // Remove path clip views depending on the number of paths.
564 while (_pathClipViews.count > paths.count) {
565 NSView* view = _pathClipViews.lastObject;
566 [view removeFromSuperview];
567 [_pathClipViews removeLastObject];
568 }
569 // Otherwise, add path clip views to the end.
570 for (size_t i = _pathClipViews.count; i < paths.count; ++i) {
571 NSView* superView = _pathClipViews.count == 0 ? self : _pathClipViews.lastObject;
572 FlutterPathClipView* pathClipView = [[FlutterPathClipView alloc] initWithFrame:self.bounds];
573 [_pathClipViews addObject:pathClipView];
574 [superView addSubview:pathClipView];
575 }
576 // Update bounds and apply clip paths.
577 for (size_t i = 0; i < _pathClipViews.count; ++i) {
578 FlutterPathClipView* pathClipView = _pathClipViews[i];
579 pathClipView.frame = self.bounds;
580 [pathClipView maskToPath:(__bridge CGPathRef)[paths objectAtIndex:i]
581 withOrigin:self.frame.origin];
582 }
583}
584
585/// Updates the PlatformView and PlatformView container views.
586///
587/// Re-nests _platformViewContainer in the innermost clip view, applies transforms to the underlying
588/// CALayer, adds the platform view as a subview of the container, and sets the axis-aligned clip
589/// rect around the tranformed view.
590- (void)updatePlatformViewWithBounds:(CGRect)untransformedBounds
591 transformedBounds:(CGRect)transformedBounds
592 transform:(CATransform3D)transform
593 clipRect:(CGRect)clipRect {
594 // Create the PlatformViewContainer view if necessary.
595 if (_platformViewContainer == nil) {
596 _platformViewContainer = [[FlutterPlatformViewContainer alloc] initWithFrame:self.bounds];
597 _platformViewContainer.wantsLayer = YES;
598 }
599
600 // Nest the PlatformViewContainer view in the innermost path clip view.
601 NSView* containerSuperview = _pathClipViews.count == 0 ? self : _pathClipViews.lastObject;
602 [containerSuperview addSubview:_platformViewContainer];
603 _platformViewContainer.frame = self.bounds;
604
605 // Nest the platform view in the PlatformViewContainer, but only if the view doesn't have a
606 // superview yet. Sometimes the platform view reparents itself (WKWebView entering FullScreen)
607 // in which case do not forcefully move it back.
608 if (_platformView.superview == nil) {
609 [_platformViewContainer addSubview:_platformView];
610 }
611
612 // Originally first subview would be the _platformView. However during WKWebView full screen
613 // the platform view gets replaced with a placeholder. Given that _platformViewContainer does
614 // not contain any other views it is safe to assume that any subview found can be treated
615 // as the platform view.
616 _platformViewContainer.subviews.firstObject.frame = untransformedBounds;
617
618 // Transform for the platform view is finalTransform adjusted for bounding rect origin.
619 CATransform3D translation =
620 CATransform3DMakeTranslation(-transformedBounds.origin.x, -transformedBounds.origin.y, 0);
621 transform = CATransform3DConcat(transform, translation);
622 _platformViewContainer.layer.sublayerTransform = transform;
623
624 // By default NSView clips children to frame. If masterClip is tighter than mutator view frame,
625 // the frame is set to masterClip and child offset adjusted to compensate for the difference.
626 if (!CGRectEqualToRect(clipRect, transformedBounds)) {
627 NSMutableArray<NSView*>* subviews = [NSMutableArray arrayWithArray:self.subviews];
628 [subviews removeObject:_trackingAreaContainer];
629 FML_DCHECK(subviews.count == 1);
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);
635 self.frame = clipRect;
636 }
637}
638
639/// Whenever possible view will be clipped using layer bounds.
640/// If clipping to path is needed, CAShapeLayer(s) will be used as mask.
641/// Clipping to round rect only clips to path if round corners are intersected.
642- (void)applyFlutterLayer:(const flutter::PlatformViewLayer*)layer {
643 // Compute the untransformed bounding rect for the platform view in logical pixels.
644 // FlutterLayer.size is in physical pixels but Cocoa uses logical points.
645 CGFloat scale = [self contentsScale];
646 MutationVector mutations = MutationsForPlatformView(layer->mutations(), scale);
647
648 CATransform3D finalTransform = CATransformFromMutations(mutations);
649
650 // Compute the untransformed bounding rect for the platform view in logical pixels.
651 // FlutterLayer.size is in physical pixels but Cocoa uses logical points.
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;
657
658 // Compute the layer opacity.
659 self.layer.opacity = OpacityFromMutations(mutations);
660
661 // Compute the master clip in global logical coordinates.
662 CGRect masterClip = MasterClipFromMutations(finalBoundingRect, mutations);
663 if (CGRectIsNull(masterClip)) {
664 self.hidden = YES;
665 return;
666 }
667 self.hidden = NO;
668
669 /// Paths in global logical coordinates that need to be clipped to.
670 NSMutableArray* paths = ClipPathFromMutations(masterClip, mutations);
671 [self updatePathClipViewsWithPaths:paths];
672
673 /// Update PlatformViewContainer, PlatformView, and apply transforms and axis-aligned clip rect.
674 [self updatePlatformViewWithBounds:untransformedBoundingRect
675 transformedBounds:finalBoundingRect
676 transform:finalTransform
677 clipRect:masterClip];
678
679 [self addSubview:_trackingAreaContainer positioned:(NSWindowAbove)relativeTo:nil];
680 _trackingAreaContainer.frame = self.bounds;
681}
682
683@end
const char * options
std::unique_ptr< flutter::PlatformViewIOS > platform_view
BOOL _mouseMoveHandled
BOOL _cleanupScheduled
NSMutableArray< NSView * > * pathClipViews
Represents a platform view layer, including all mutations.
PlatformViewLayer(const SkPoint &offset, const SkSize &size, int64_t view_id)
static SkString identifier(const FontFamilyDesc &family, const FontDesc &font)
int64_t FlutterPlatformViewIdentifier
Definition: embedder.h:1188
@ kFlutterLayerContentTypePlatformView
Indicates that the contents of this layer are determined by the embedder.
Definition: embedder.h:1795
@ kFlutterPlatformViewMutationTypeClipRoundedRect
Definition: embedder.h:1697
@ kFlutterPlatformViewMutationTypeClipRect
Definition: embedder.h:1694
@ kFlutterPlatformViewMutationTypeTransformation
Definition: embedder.h:1700
@ kFlutterPlatformViewMutationTypeOpacity
Definition: embedder.h:1691
#define FML_CHECK(condition)
Definition: logging.h:85
#define FML_DCHECK(condition)
Definition: logging.h:103
NSTrackingArea * _trackingArea
NSView * platformView
Returns wrapped platform view.
FlutterCursorCoordinator * _cursorCoordinator
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
NSMutableArray * _pathClipViews
void maskToPath:withOrigin:(CGPathRef path, [withOrigin] CGPoint origin)
Superview container for platform views, to which sublayer transforms are applied.
instancetype initWithFrame
fml::scoped_nsobject< FlutterView > _flutterView
static bool init()
Optional< SkRect > bounds
Definition: SkRecords.h:189
clipRect(r.rect, r.opAA.op(), r.opAA.aa())) template<> void Draw
ClipOpAndAA opAA SkRegion region
Definition: SkRecords.h:238
SkRRect rrect
Definition: SkRecords.h:232
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
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
Definition: switches.h:57
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
Definition: switches.h:259
Definition: ref_ptr.h:256
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition: p3.cpp:47
const Scalar scale
SeparatedVector2 offset
FlutterPoint offset
Definition: embedder.h:1835
FlutterLayerContentType type
Definition: embedder.h:1824
const FlutterPlatformView * platform_view
Definition: embedder.h:1831
FlutterSize size
The size of the layer (in physical pixels).
Definition: embedder.h:1837
A structure to represent a 2D point.
Definition: embedder.h:445
A structure to represent a rectangle.
Definition: embedder.h:437
double bottom
Definition: embedder.h:441
double top
Definition: embedder.h:439
double left
Definition: embedder.h:438
double right
Definition: embedder.h:440
A structure to represent a rounded rectangle.
Definition: embedder.h:451
FlutterRect rect
Definition: embedder.h:452
FlutterSize upper_left_corner_radius
Definition: embedder.h:453
FlutterSize lower_left_corner_radius
Definition: embedder.h:456
FlutterSize upper_right_corner_radius
Definition: embedder.h:454
FlutterSize lower_right_corner_radius
Definition: embedder.h:455
A structure to represent the width and height.
Definition: embedder.h:423
double height
Definition: embedder.h:425
double width
Definition: embedder.h:424
double transY
vertical translation
Definition: embedder.h:289
double skewX
horizontal skew factor
Definition: embedder.h:281
double pers0
input x-axis perspective factor
Definition: embedder.h:291
double scaleX
horizontal scale factor
Definition: embedder.h:279
double skewY
vertical skew factor
Definition: embedder.h:285
double scaleY
vertical scale factor
Definition: embedder.h:287
double pers1
input y-axis perspective factor
Definition: embedder.h:293
double transX
horizontal translation
Definition: embedder.h:283
int BOOL
Definition: windows_types.h:37