Flutter macOS Embedder
FlutterWindowController.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 #include <Foundation/Foundation.h>
7 
12 
14 
15 // A delegate for a Flutter managed window.
16 @interface FlutterWindowOwner : NSObject <NSWindowDelegate> {
17  // Strong reference to the window. This is the only strong reference to the
18  // window.
19  NSWindow* _window;
21  std::optional<flutter::Isolate> _isolate;
23 }
24 
25 @property(readonly, nonatomic) NSWindow* window;
26 @property(readonly, nonatomic) FlutterViewController* flutterViewController;
27 
28 - (instancetype)initWithWindow:(NSWindow*)window
29  flutterViewController:(FlutterViewController*)viewController
30  creationRequest:(const FlutterWindowCreationRequest&)creationRequest;
31 
32 @end
33 
35 
36 - (void)flutterSetContentSize:(FlutterWindowSizing)contentSize;
37 
38 @end
39 
40 @implementation NSWindow (FlutterWindowSizing)
41 - (void)flutterSetContentSize:(FlutterWindowSizing)contentSize {
42  if (contentSize.has_size) {
43  [self setContentSize:NSMakeSize(contentSize.width, contentSize.height)];
44  }
45  if (contentSize.has_constraints) {
46  [self setContentMinSize:NSMakeSize(contentSize.min_width, contentSize.min_height)];
47  if (contentSize.max_width > 0 && contentSize.max_height > 0) {
48  [self setContentMaxSize:NSMakeSize(contentSize.max_width, contentSize.max_height)];
49  } else {
50  [self setContentMaxSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)];
51  }
52  }
53 }
54 
55 @end
56 
57 @implementation FlutterWindowOwner
58 
59 @synthesize window = _window;
61 
62 - (instancetype)initWithWindow:(NSWindow*)window
63  flutterViewController:(FlutterViewController*)viewController
64  creationRequest:(const FlutterWindowCreationRequest&)creationRequest {
65  if (self = [super init]) {
66  _window = window;
67  _flutterViewController = viewController;
68  _creationRequest = creationRequest;
70  }
71  return self;
72 }
73 
74 - (void)windowDidBecomeKey:(NSNotification*)notification {
75  [_flutterViewController.engine windowDidBecomeKey:_flutterViewController.viewIdentifier];
76 }
77 
78 - (void)windowDidResignKey:(NSNotification*)notification {
79  [_flutterViewController.engine windowDidResignKey:_flutterViewController.viewIdentifier];
80 }
81 
82 - (BOOL)windowShouldClose:(NSWindow*)sender {
83  flutter::IsolateScope isolate_scope(*_isolate);
85  return NO;
86 }
87 
88 - (void)windowDidResize:(NSNotification*)notification {
89  flutter::IsolateScope isolate_scope(*_isolate);
91 }
92 
93 // Miniaturize does not trigger resize event, but for now there
94 // is no other way to get notification about the state change.
95 - (void)windowDidMiniaturize:(NSNotification*)notification {
96  flutter::IsolateScope isolate_scope(*_isolate);
98 }
99 
100 // Deminiaturize does not trigger resize event, but for now there
101 // is no other way to get notification about the state change.
102 - (void)windowDidDeminiaturize:(NSNotification*)notification {
103  flutter::IsolateScope isolate_scope(*_isolate);
105 }
106 
107 @end
108 
109 @interface FlutterWindowController () {
110  NSMutableArray<FlutterWindowOwner*>* _windows;
111 }
112 
113 @end
114 
115 @implementation FlutterWindowController
116 
117 - (instancetype)init {
118  self = [super init];
119  if (self != nil) {
120  _windows = [NSMutableArray array];
121  }
122  return self;
123 }
124 
125 - (FlutterViewIdentifier)createRegularWindow:(const FlutterWindowCreationRequest*)request {
126  FlutterViewController* c = [[FlutterViewController alloc] initWithEngine:_engine
127  nibName:nil
128  bundle:nil];
129 
130  NSWindow* window = [[NSWindow alloc] init];
131  // If this is not set there will be double free on window close when
132  // using ARC.
133  [window setReleasedWhenClosed:NO];
134 
135  window.contentViewController = c;
136  window.styleMask = NSWindowStyleMaskResizable | NSWindowStyleMaskTitled |
137  NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
138  [window flutterSetContentSize:request->contentSize];
139  [window setIsVisible:YES];
140  [window makeKeyAndOrderFront:nil];
141 
142  FlutterWindowOwner* w = [[FlutterWindowOwner alloc] initWithWindow:window
143  flutterViewController:c
144  creationRequest:*request];
145  window.delegate = w;
146  [_windows addObject:w];
147 
148  return c.viewIdentifier;
149 }
150 
151 - (void)destroyWindow:(NSWindow*)window {
152  FlutterWindowOwner* owner = nil;
153  for (FlutterWindowOwner* o in _windows) {
154  if (o.window == window) {
155  owner = o;
156  break;
157  }
158  }
159  if (owner != nil) {
160  [_windows removeObject:owner];
161  // Make sure to unregister the controller from the engine and remove the FlutterView
162  // before destroying the window and Flutter NSView.
163  [owner.flutterViewController dispose];
164  owner.window.delegate = nil;
165  [owner.window close];
166  }
167 }
168 
169 - (void)closeAllWindows {
170  for (FlutterWindowOwner* owner in _windows) {
171  [owner.flutterViewController dispose];
172  [owner.window close];
173  }
174  [_windows removeAllObjects];
175 }
176 
177 @end
178 
179 // NOLINTBEGIN(google-objc-function-naming)
180 
182  int64_t engine_id,
183  const FlutterWindowCreationRequest* request) {
184  FlutterEngine* engine = [FlutterEngine engineForIdentifier:engine_id];
185  [engine enableMultiView];
186  return [engine.windowController createRegularWindow:request];
187 }
188 
189 void InternalFlutter_Window_Destroy(int64_t engine_id, void* window) {
190  NSWindow* w = (__bridge NSWindow*)window;
191  FlutterEngine* engine = [FlutterEngine engineForIdentifier:engine_id];
192  [engine.windowController destroyWindow:w];
193 }
194 
195 void* InternalFlutter_Window_GetHandle(int64_t engine_id, FlutterViewIdentifier view_id) {
196  FlutterEngine* engine = [FlutterEngine engineForIdentifier:engine_id];
197  FlutterViewController* controller = [engine viewControllerForIdentifier:view_id];
198  return (__bridge void*)controller.view.window;
199 }
200 
202  NSWindow* w = (__bridge NSWindow*)window;
203  return {
204  .width = w.frame.size.width,
205  .height = w.frame.size.height,
206  };
207 }
208 
210  NSWindow* w = (__bridge NSWindow*)window;
211  [w flutterSetContentSize:*size];
212 }
213 
214 void InternalFlutter_Window_SetTitle(void* window, const char* title) {
215  NSWindow* w = (__bridge NSWindow*)window;
216  w.title = [NSString stringWithUTF8String:title];
217 }
218 
219 void InternalFlutter_Window_SetMaximized(void* window, bool maximized) {
220  NSWindow* w = (__bridge NSWindow*)window;
221  if (maximized & !w.isZoomed) {
222  [w zoom:nil];
223  } else if (!maximized && w.isZoomed) {
224  [w zoom:nil];
225  }
226 }
227 
229  NSWindow* w = (__bridge NSWindow*)window;
230  return w.isZoomed;
231 }
232 
234  NSWindow* w = (__bridge NSWindow*)window;
235  [w miniaturize:nil];
236 }
237 
239  NSWindow* w = (__bridge NSWindow*)window;
240  [w deminiaturize:nil];
241 }
242 
244  NSWindow* w = (__bridge NSWindow*)window;
245  return w.isMiniaturized;
246 }
247 
248 void InternalFlutter_Window_SetFullScreen(void* window, bool fullScreen) {
249  NSWindow* w = (__bridge NSWindow*)window;
250  bool isFullScreen = (w.styleMask & NSWindowStyleMaskFullScreen) != 0;
251  if (fullScreen && !isFullScreen) {
252  [w toggleFullScreen:nil];
253  } else if (!fullScreen && isFullScreen) {
254  [w toggleFullScreen:nil];
255  }
256 }
257 
259  NSWindow* w = (__bridge NSWindow*)window;
260  return (w.styleMask & NSWindowStyleMaskFullScreen) != 0;
261 }
262 
264  NSWindow* w = (__bridge NSWindow*)window;
265  [NSApplication.sharedApplication activateIgnoringOtherApps:YES];
266  [w makeKeyAndOrderFront:nil];
267 }
268 
269 // NOLINTEND(google-objc-function-naming)
int64_t FlutterViewIdentifier
void InternalFlutter_Window_Activate(void *window)
void * InternalFlutter_Window_GetHandle(int64_t engine_id, FlutterViewIdentifier view_id)
bool InternalFlutter_Window_IsMaximized(void *window)
void InternalFlutter_Window_SetContentSize(void *window, const FlutterWindowSizing *size)
int64_t InternalFlutter_WindowController_CreateRegularWindow(int64_t engine_id, const FlutterWindowCreationRequest *request)
void InternalFlutter_Window_SetFullScreen(void *window, bool fullScreen)
bool InternalFlutter_Window_IsFullScreen(void *window)
void InternalFlutter_Window_Minimize(void *window)
void InternalFlutter_Window_SetTitle(void *window, const char *title)
bool InternalFlutter_Window_IsMinimized(void *window)
void InternalFlutter_Window_Destroy(int64_t engine_id, void *window)
void InternalFlutter_Window_SetMaximized(void *window, bool maximized)
FlutterWindowSize InternalFlutter_Window_GetContentSize(void *window)
void InternalFlutter_Window_Unminimize(void *window)
NSMutableArray< FlutterWindowOwner * > * _windows
static Isolate Current()
Definition: isolate_scope.cc:9
FlutterViewIdentifier viewIdentifier
FlutterViewController * _flutterViewController
FlutterViewController * flutterViewController
std::optional< flutter::Isolate > _isolate
FlutterWindowCreationRequest _creationRequest