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:(FlutterWindowSize)contentSize;
37 - (void)flutterSetConstraints:(FlutterWindowConstraints)constraints;
38 
39 @end
40 
41 @implementation NSWindow (FlutterWindowSizing)
42 - (void)flutterSetContentSize:(FlutterWindowSize)contentSize {
43  [self setContentSize:NSMakeSize(contentSize.width, contentSize.height)];
44 }
45 
46 - (void)flutterSetConstraints:(FlutterWindowConstraints)constraints {
47  NSSize size = [self frameRectForContentRect:self.frame].size;
48  NSSize originalSize = size;
49  [self setContentMinSize:NSMakeSize(constraints.min_width, constraints.min_height)];
50  size.width = std::max(size.width, constraints.min_width);
51  size.height = std::max(size.height, constraints.min_height);
52  if (constraints.max_width > 0 && constraints.max_height > 0) {
53  [self setContentMaxSize:NSMakeSize(constraints.max_width, constraints.max_height)];
54  size.width = std::min(size.width, constraints.max_width);
55  size.height = std::min(size.height, constraints.max_height);
56  } else {
57  [self setContentMaxSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)];
58  }
59  if (!NSEqualSizes(originalSize, size)) {
60  [self setContentSize:size];
61  }
62 }
63 
64 @end
65 
66 @implementation FlutterWindowOwner
67 
68 @synthesize window = _window;
70 
71 - (instancetype)initWithWindow:(NSWindow*)window
72  flutterViewController:(FlutterViewController*)viewController
73  creationRequest:(const FlutterWindowCreationRequest&)creationRequest {
74  if (self = [super init]) {
75  _window = window;
76  _flutterViewController = viewController;
77  _creationRequest = creationRequest;
79  }
80  return self;
81 }
82 
83 - (void)windowDidBecomeKey:(NSNotification*)notification {
84  [_flutterViewController.engine windowDidBecomeKey:_flutterViewController.viewIdentifier];
85 }
86 
87 - (void)windowDidResignKey:(NSNotification*)notification {
88  [_flutterViewController.engine windowDidResignKey:_flutterViewController.viewIdentifier];
89 }
90 
91 - (BOOL)windowShouldClose:(NSWindow*)sender {
92  flutter::IsolateScope isolate_scope(*_isolate);
94  return NO;
95 }
96 
97 - (void)windowDidResize:(NSNotification*)notification {
98  flutter::IsolateScope isolate_scope(*_isolate);
100 }
101 
102 // Miniaturize does not trigger resize event, but for now there
103 // is no other way to get notification about the state change.
104 - (void)windowDidMiniaturize:(NSNotification*)notification {
105  flutter::IsolateScope isolate_scope(*_isolate);
107 }
108 
109 // Deminiaturize does not trigger resize event, but for now there
110 // is no other way to get notification about the state change.
111 - (void)windowDidDeminiaturize:(NSNotification*)notification {
112  flutter::IsolateScope isolate_scope(*_isolate);
114 }
115 
116 - (void)windowWillEnterFullScreen:(NSNotification*)notification {
117  flutter::IsolateScope isolate_scope(*_isolate);
119 }
120 
121 - (void)windowWillExitFullScreen:(NSNotification*)notification {
122  flutter::IsolateScope isolate_scope(*_isolate);
124 }
125 
126 @end
127 
128 @interface FlutterWindowController () {
129  NSMutableArray<FlutterWindowOwner*>* _windows;
130 }
131 
132 @end
133 
134 @implementation FlutterWindowController
135 
136 - (instancetype)init {
137  self = [super init];
138  if (self != nil) {
139  _windows = [NSMutableArray array];
140  }
141  return self;
142 }
143 
144 - (FlutterViewIdentifier)createRegularWindow:(const FlutterWindowCreationRequest*)request {
145  FlutterViewController* c = [[FlutterViewController alloc] initWithEngine:_engine
146  nibName:nil
147  bundle:nil];
148 
149  NSWindow* window = [[NSWindow alloc] init];
150  // If this is not set there will be double free on window close when
151  // using ARC.
152  [window setReleasedWhenClosed:NO];
153 
154  window.contentViewController = c;
155  window.styleMask = NSWindowStyleMaskResizable | NSWindowStyleMaskTitled |
156  NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
157  if (request->has_size) {
158  [window flutterSetContentSize:request->size];
159  }
160  if (request->has_constraints) {
161  [window flutterSetConstraints:request->constraints];
162  }
163  [window setIsVisible:YES];
164  [window makeKeyAndOrderFront:nil];
165 
166  FlutterWindowOwner* w = [[FlutterWindowOwner alloc] initWithWindow:window
167  flutterViewController:c
168  creationRequest:*request];
169  window.delegate = w;
170  [_windows addObject:w];
171 
172  return c.viewIdentifier;
173 }
174 
175 - (void)destroyWindow:(NSWindow*)window {
176  FlutterWindowOwner* owner = nil;
177  for (FlutterWindowOwner* o in _windows) {
178  if (o.window == window) {
179  owner = o;
180  break;
181  }
182  }
183  if (owner != nil) {
184  [_windows removeObject:owner];
185  // Make sure to unregister the controller from the engine and remove the FlutterView
186  // before destroying the window and Flutter NSView.
187  [owner.flutterViewController dispose];
188  owner.window.delegate = nil;
189  [owner.window close];
190  }
191 }
192 
193 - (void)closeAllWindows {
194  for (FlutterWindowOwner* owner in _windows) {
195  [owner.flutterViewController dispose];
196  [owner.window close];
197  }
198  [_windows removeAllObjects];
199 }
200 
201 @end
202 
203 // NOLINTBEGIN(google-objc-function-naming)
204 
206  int64_t engine_id,
207  const FlutterWindowCreationRequest* request) {
208  FlutterEngine* engine = [FlutterEngine engineForIdentifier:engine_id];
209  [engine enableMultiView];
210  return [engine.windowController createRegularWindow:request];
211 }
212 
213 void InternalFlutter_Window_Destroy(int64_t engine_id, void* window) {
214  NSWindow* w = (__bridge NSWindow*)window;
215  FlutterEngine* engine = [FlutterEngine engineForIdentifier:engine_id];
216  [engine.windowController destroyWindow:w];
217 }
218 
219 void* InternalFlutter_Window_GetHandle(int64_t engine_id, FlutterViewIdentifier view_id) {
220  FlutterEngine* engine = [FlutterEngine engineForIdentifier:engine_id];
221  FlutterViewController* controller = [engine viewControllerForIdentifier:view_id];
222  return (__bridge void*)controller.view.window;
223 }
224 
226  NSWindow* w = (__bridge NSWindow*)window;
227  NSRect contentRect = [w contentRectForFrameRect:w.frame];
228  return {
229  .width = contentRect.size.width,
230  .height = contentRect.size.height,
231  };
232 }
233 
235  NSWindow* w = (__bridge NSWindow*)window;
236  [w flutterSetContentSize:*size];
237 }
238 
241  const FlutterWindowConstraints* constraints) {
242  NSWindow* w = (__bridge NSWindow*)window;
243  [w flutterSetConstraints:*constraints];
244 }
245 
246 void InternalFlutter_Window_SetTitle(void* window, const char* title) {
247  NSWindow* w = (__bridge NSWindow*)window;
248  w.title = [NSString stringWithUTF8String:title];
249 }
250 
251 void InternalFlutter_Window_SetMaximized(void* window, bool maximized) {
252  NSWindow* w = (__bridge NSWindow*)window;
253  if (maximized & !w.isZoomed) {
254  [w zoom:nil];
255  } else if (!maximized && w.isZoomed) {
256  [w zoom:nil];
257  }
258 }
259 
261  NSWindow* w = (__bridge NSWindow*)window;
262  return w.isZoomed;
263 }
264 
266  NSWindow* w = (__bridge NSWindow*)window;
267  [w miniaturize:nil];
268 }
269 
271  NSWindow* w = (__bridge NSWindow*)window;
272  [w deminiaturize:nil];
273 }
274 
276  NSWindow* w = (__bridge NSWindow*)window;
277  return w.isMiniaturized;
278 }
279 
280 void InternalFlutter_Window_SetFullScreen(void* window, bool fullScreen) {
281  NSWindow* w = (__bridge NSWindow*)window;
282  bool isFullScreen = (w.styleMask & NSWindowStyleMaskFullScreen) != 0;
283  if (fullScreen && !isFullScreen) {
284  [w toggleFullScreen:nil];
285  } else if (!fullScreen && isFullScreen) {
286  [w toggleFullScreen:nil];
287  }
288 }
289 
291  NSWindow* w = (__bridge NSWindow*)window;
292  return (w.styleMask & NSWindowStyleMaskFullScreen) != 0;
293 }
294 
296  NSWindow* w = (__bridge NSWindow*)window;
297  [NSApplication.sharedApplication activateIgnoringOtherApps:YES];
298  [w makeKeyAndOrderFront:nil];
299 }
300 
301 char* InternalFlutter_Window_GetTitle(void* window) {
302  NSWindow* w = (__bridge NSWindow*)window;
303  return strdup(w.title.UTF8String);
304 }
305 
307  NSWindow* w = (__bridge NSWindow*)window;
308  return w.isKeyWindow;
309 }
310 
311 // NOLINTEND(google-objc-function-naming)
#define FLUTTER_DARWIN_EXPORT
Definition: FlutterMacros.h:14
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)
FLUTTER_DARWIN_EXPORT void InternalFlutter_Window_SetConstraints(void *window, const FlutterWindowConstraints *constraints)
bool InternalFlutter_Window_IsActivated(void *window)
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_SetContentSize(void *window, const FlutterWindowSize *size)
void InternalFlutter_Window_Minimize(void *window)
char * InternalFlutter_Window_GetTitle(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