Flutter macOS Embedder
FlutterView.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 
6 
7 #import <QuartzCore/QuartzCore.h>
8 
9 #import "flutter/shell/platform/darwin/common/InternalFlutterSwiftCommon/InternalFlutterSwiftCommon.h"
10 #import "flutter/shell/platform/darwin/macos/InternalFlutterSwift/InternalFlutterSwift.h"
12 
15  __weak id<FlutterViewDelegate> _viewDelegate;
16  FlutterResizeSynchronizer* _resizeSynchronizer;
18  NSCursor* _lastCursor;
19 }
20 
21 @end
22 
23 @implementation FlutterView
24 
25 - (instancetype)initWithMTLDevice:(id<MTLDevice>)device
26  commandQueue:(id<MTLCommandQueue>)commandQueue
27  delegate:(id<FlutterViewDelegate>)delegate
28  viewIdentifier:(FlutterViewIdentifier)viewIdentifier {
29  self = [super initWithFrame:NSZeroRect];
30  if (self) {
31  [self setWantsLayer:YES];
32  [self setBackgroundColor:[NSColor blackColor]];
33  [self setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawDuringViewResize];
34  _viewIdentifier = viewIdentifier;
35  _viewDelegate = delegate;
36  _surfaceManager = [[FlutterSurfaceManager alloc] initWithDevice:device
37  commandQueue:commandQueue
38  layer:self.layer
39  delegate:self];
40  _resizeSynchronizer = [[FlutterResizeSynchronizer alloc] init];
41  }
42  return self;
43 }
44 
45 - (void)onPresent:(CGSize)frameSize withBlock:(dispatch_block_t)block delay:(NSTimeInterval)delay {
46  [_resizeSynchronizer performCommitForSize:frameSize afterDelay:delay notify:block];
47 }
48 
50  return _surfaceManager;
51 }
52 
53 - (void)shutDown {
54  [_resizeSynchronizer shutDown];
55 }
56 
57 - (void)setBackgroundColor:(NSColor*)color {
58  self.layer.backgroundColor = color.CGColor;
59 }
60 
61 #pragma mark - NSView overrides
62 
63 - (void)setFrameSize:(NSSize)newSize {
64  [super setFrameSize:newSize];
65  CGSize scaledSize = [self convertSizeToBacking:self.bounds.size];
66  [_resizeSynchronizer beginResizeForSize:scaledSize
67  notify:^{
68  [_viewDelegate viewDidReshape:self];
69  }
70  onTimeout:^{
71  [FlutterLogger logError:@"Resize timed out"];
72  }];
73 }
74 
75 /**
76  * Declares that the view uses a flipped coordinate system, consistent with Flutter conventions.
77  */
78 - (BOOL)isFlipped {
79  return YES;
80 }
81 
82 - (BOOL)isOpaque {
83  return YES;
84 }
85 
86 /**
87  * Declares that the initial mouse-down when the view is not in focus will send an event to the
88  * view.
89  */
90 - (BOOL)acceptsFirstMouse:(NSEvent*)event {
91  return YES;
92 }
93 
94 - (BOOL)acceptsFirstResponder {
95  // This is to ensure that FlutterView does not take first responder status from TextInputPlugin
96  // on mouse clicks.
97  return [_viewDelegate viewShouldAcceptFirstResponder:self];
98 }
99 
100 - (void)didUpdateMouseCursor:(NSCursor*)cursor {
101  _lastCursor = cursor;
102 }
103 
104 // Restores mouse cursor. There are few cases when this is needed and framework will not handle
105 // this automatically:
106 // - When mouse cursor leaves subview of FlutterView (technically still within bound of
107 // FlutterView tracking area so the framework won't be notified)
108 // - When context menu above FlutterView is closed. Context menu will change current cursor to
109 // arrow and will not restore it back.
110 - (void)cursorUpdate:(NSEvent*)event {
111  // Make sure to not override cursor when over a platform view.
112  NSPoint mouseLocation = [[self superview] convertPoint:event.locationInWindow fromView:nil];
113  NSView* hitTestView = [self hitTest:mouseLocation];
114  if (hitTestView != self) {
115  return;
116  }
117  [_lastCursor set];
118  // It is possible that there is a platform view with NSTrackingArea below flutter content.
119  // This could override the mouse cursor as a result of mouse move event. There is no good way
120  // to prevent that short of swizzling [NSCursor set], so as a workaround force flutter cursor
121  // in next runloop turn. This is not ideal, as it may cause the cursor flicker a bit.
122  [[NSRunLoop currentRunLoop] performBlock:^{
123  [_lastCursor set];
124  }];
125 }
126 
127 - (void)viewDidChangeBackingProperties {
128  [super viewDidChangeBackingProperties];
129  // Force redraw
130  [_viewDelegate viewDidReshape:self];
131 }
132 
133 - (BOOL)layer:(CALayer*)layer
134  shouldInheritContentsScale:(CGFloat)newScale
135  fromWindow:(NSWindow*)window {
136  return YES;
137 }
138 
139 #pragma mark - NSAccessibility overrides
140 
141 - (BOOL)isAccessibilityElement {
142  return YES;
143 }
144 
145 - (NSAccessibilityRole)accessibilityRole {
146  return NSAccessibilityGroupRole;
147 }
148 
149 - (NSString*)accessibilityLabel {
150  // TODO(chunhtai): Provides a way to let developer customize the accessibility
151  // label.
152  // https://github.com/flutter/flutter/issues/75446
153  NSString* applicationName =
154  [NSBundle.mainBundle objectForInfoDictionaryKey:@"CFBundleDisplayName"];
155  if (!applicationName) {
156  applicationName = [NSBundle.mainBundle objectForInfoDictionaryKey:@"CFBundleName"];
157  }
158  return applicationName;
159 }
160 
161 @end
int64_t FlutterViewIdentifier
__weak id< FlutterViewDelegate > _viewDelegate
Definition: FlutterView.mm:15
NSCursor * _lastCursor
Definition: FlutterView.mm:18
FlutterSurfaceManager * _surfaceManager
Definition: FlutterView.mm:17
FlutterViewIdentifier _viewIdentifier
Definition: FlutterView.mm:14
FlutterResizeSynchronizer * _resizeSynchronizer
Definition: FlutterView.mm:16
void shutDown()
Definition: FlutterView.mm:53
FlutterSurfaceManager * surfaceManager
Definition: FlutterView.h:56