| 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
5 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h"
6 #include <Foundation/Foundation.h>
7
8 #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h"
9 #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h"
10 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h"
11 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h"
12
13 #include "flutter/shell/platform/common/isolate_scope.h"
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;
20 FlutterViewController* _flutterViewController;
21 std::optional<flutter::Isolate> _isolate;
22 FlutterWindowCreationRequest _creationRequest;
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
34 @interface NSWindow (FlutterWindowSizing)
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;
69 @synthesize flutterViewController = _flutterViewController;
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;
78 _isolate = flutter::Isolate::Current();
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);
93 _creationRequest.on_should_close();
94 return NO;
95 }
96
97 - (void)windowWillClose {
98 _creationRequest.on_will_close();
99 }
100
101 - (void)windowDidResize:(NSNotification*)notification {
102 flutter::IsolateScope isolate_scope(*_isolate);
103 _creationRequest.notify_listeners();
104 }
105
106 // Miniaturize does not trigger resize event, but for now there
107 // is no other way to get notification about the state change.
108 - (void)windowDidMiniaturize:(NSNotification*)notification {
109 flutter::IsolateScope isolate_scope(*_isolate);
110 _creationRequest.notify_listeners();
111 }
112
113 // Deminiaturize does not trigger resize event, but for now there
114 // is no other way to get notification about the state change.
115 - (void)windowDidDeminiaturize:(NSNotification*)notification {
116 flutter::IsolateScope isolate_scope(*_isolate);
117 _creationRequest.notify_listeners();
118 }
119
120 - (void)windowWillEnterFullScreen:(NSNotification*)notification {
121 flutter::IsolateScope isolate_scope(*_isolate);
122 _creationRequest.notify_listeners();
123 }
124
125 - (void)windowWillExitFullScreen:(NSNotification*)notification {
126 flutter::IsolateScope isolate_scope(*_isolate);
127 _creationRequest.notify_listeners();
128 }
129
130 @end
131
132 @interface FlutterWindowController () {
133 NSMutableArray<FlutterWindowOwner*>* _windows;
134 }
135
136 @end
137
138 @implementation FlutterWindowController
139
140 - (instancetype)init {
141 self = [super init];
142 if (self != nil) {
143 _windows = [NSMutableArray array];
144 }
145 return self;
146 }
147
148 - (FlutterViewIdentifier)createDialogWindow:(const FlutterWindowCreationRequest*)request {
149 FlutterViewController* c = [[FlutterViewController alloc] initWithEngine:_engine
150 nibName:nil
151 bundle:nil];
152
153 NSWindow* window = [[NSWindow alloc] init];
154 // If this is not set there will be double free on window close when
155 // using ARC.
156 [window setReleasedWhenClosed:NO];
157
158 window.contentViewController = c;
159 window.styleMask =
160 NSWindowStyleMaskResizable | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable;
161 window.collectionBehavior = NSWindowCollectionBehaviorFullScreenAuxiliary;
162 if (request->has_size) {
163 [window flutterSetContentSize:request->size];
164 }
165 if (request->has_constraints) {
166 [window flutterSetConstraints:request->constraints];
167 }
168
169 FlutterWindowOwner* w = [[FlutterWindowOwner alloc] initWithWindow:window
170 flutterViewController:c
171 creationRequest:*request];
172 window.delegate = w;
173 [_windows addObject:w];
174
175 NSWindow* parent = nil;
176
177 if (request->parent_view_id != 0) {
178 for (FlutterWindowOwner* owner in _windows) {
179 if (owner.flutterViewController.viewIdentifier == request->parent_view_id) {
180 parent = owner.window;
181 break;
182 }
183 }
184 if (parent == nil) {
185 FML_LOG(WARNING) << "Failed to find parent window for ID " << request->parent_view_id;
186 }
187 }
188
189 if (parent != nil) {
190 [parent beginCriticalSheet:window
191 completionHandler:^(NSModalResponse response){
192 }];
193 } else {
194 [window setIsVisible:YES];
195 [window makeKeyAndOrderFront:nil];
196 }
197
198 return c.viewIdentifier;
199 }
200
201 - (FlutterViewIdentifier)createRegularWindow:(const FlutterWindowCreationRequest*)request {
202 FlutterViewController* c = [[FlutterViewController alloc] initWithEngine:_engine
203 nibName:nil
204 bundle:nil];
205
206 NSWindow* window = [[NSWindow alloc] init];
207 // If this is not set there will be double free on window close when
208 // using ARC.
209 [window setReleasedWhenClosed:NO];
210
211 window.contentViewController = c;
212 window.styleMask = NSWindowStyleMaskResizable | NSWindowStyleMaskTitled |
213 NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
214 if (request->has_size) {
215 [window flutterSetContentSize:request->size];
216 }
217 if (request->has_constraints) {
218 [window flutterSetConstraints:request->constraints];
219 }
220 [window setIsVisible:YES];
221 [window makeKeyAndOrderFront:nil];
222
223 FlutterWindowOwner* w = [[FlutterWindowOwner alloc] initWithWindow:window
224 flutterViewController:c
225 creationRequest:*request];
226 window.delegate = w;
227 [_windows addObject:w];
228
229 return c.viewIdentifier;
230 }
231
232 - (void)destroyWindow:(NSWindow*)window {
233 FlutterWindowOwner* owner = nil;
234 for (FlutterWindowOwner* o in _windows) {
235 if (o.window == window) {
236 owner = o;
237 break;
238 }
239 }
240 if (owner != nil) {
241 [_windows removeObject:owner];
242
243 for (NSWindow* win in owner.window.sheets) {
244 [self destroyWindow:win];
245 }
246
247 for (NSWindow* win in owner.window.childWindows) {
248 [self destroyWindow:win];
249 }
250
251 // Make sure to unregister the controller from the engine and remove the FlutterView
252 // before destroying the window and Flutter NSView.
253 [owner.flutterViewController dispose];
254 owner.window.delegate = nil;
255 [owner.window close];
256 [owner windowWillClose];
257 }
258 }
259
260 - (void)closeAllWindows {
261 for (FlutterWindowOwner* owner in _windows) {
262 [owner.flutterViewController dispose];
263 [owner.window close];
264 }
265 [_windows removeAllObjects];
266 }
267
268 @end
269
270 // NOLINTBEGIN(google-objc-function-naming)
271
272 int64_t InternalFlutter_WindowController_CreateRegularWindow(
273 int64_t engine_id,
274 const FlutterWindowCreationRequest* request) {
275 FlutterEngine* engine = [FlutterEngine engineForIdentifier:engine_id];
276 [engine enableMultiView];
277 return [engine.windowController createRegularWindow:request];
278 }
279
280 int64_t InternalFlutter_WindowController_CreateDialogWindow(
281 int64_t engine_id,
282 const FlutterWindowCreationRequest* request) {
283 FlutterEngine* engine = [FlutterEngine engineForIdentifier:engine_id];
284 [engine enableMultiView];
285 return [engine.windowController createDialogWindow:request];
286 }
287
288 void InternalFlutter_Window_Destroy(int64_t engine_id, void* window) {
289 NSWindow* w = (__bridge NSWindow*)window;
290 FlutterEngine* engine = [FlutterEngine engineForIdentifier:engine_id];
291 [engine.windowController destroyWindow:w];
292 }
293
294 void* InternalFlutter_Window_GetHandle(int64_t engine_id, FlutterViewIdentifier view_id) {
295 FlutterEngine* engine = [FlutterEngine engineForIdentifier:engine_id];
296 FlutterViewController* controller = [engine viewControllerForIdentifier:view_id];
297 return (__bridge void*)controller.view.window;
298 }
299
300 FlutterWindowSize InternalFlutter_Window_GetContentSize(void* window) {
301 NSWindow* w = (__bridge NSWindow*)window;
302 NSRect contentRect = [w contentRectForFrameRect:w.frame];
303 return {
304 .width = contentRect.size.width,
305 .height = contentRect.size.height,
306 };
307 }
308
309 void InternalFlutter_Window_SetContentSize(void* window, const FlutterWindowSize* size) {
310 NSWindow* w = (__bridge NSWindow*)window;
311 [w flutterSetContentSize:*size];
312 }
313
314 FLUTTER_DARWIN_EXPORT
315 void InternalFlutter_Window_SetConstraints(void* window,
316 const FlutterWindowConstraints* constraints) {
317 NSWindow* w = (__bridge NSWindow*)window;
318 [w flutterSetConstraints:*constraints];
319 }
320
321 void InternalFlutter_Window_SetTitle(void* window, const char* title) {
322 NSWindow* w = (__bridge NSWindow*)window;
323 w.title = [NSString stringWithUTF8String:title];
324 }
325
326 void InternalFlutter_Window_SetMaximized(void* window, bool maximized) {
327 NSWindow* w = (__bridge NSWindow*)window;
328 if (maximized & !w.isZoomed) {
329 [w zoom:nil];
330 } else if (!maximized && w.isZoomed) {
331 [w zoom:nil];
332 }
333 }
334
335 bool InternalFlutter_Window_IsMaximized(void* window) {
336 NSWindow* w = (__bridge NSWindow*)window;
337 return w.isZoomed;
338 }
339
340 void InternalFlutter_Window_Minimize(void* window) {
341 NSWindow* w = (__bridge NSWindow*)window;
342 [w miniaturize:nil];
343 }
344
345 void InternalFlutter_Window_Unminimize(void* window) {
346 NSWindow* w = (__bridge NSWindow*)window;
347 [w deminiaturize:nil];
348 }
349
350 bool InternalFlutter_Window_IsMinimized(void* window) {
351 NSWindow* w = (__bridge NSWindow*)window;
352 return w.isMiniaturized;
353 }
354
355 void InternalFlutter_Window_SetFullScreen(void* window, bool fullScreen) {
356 NSWindow* w = (__bridge NSWindow*)window;
357 bool isFullScreen = (w.styleMask & NSWindowStyleMaskFullScreen) != 0;
358 if (fullScreen && !isFullScreen) {
359 [w toggleFullScreen:nil];
360 } else if (!fullScreen && isFullScreen) {
361 [w toggleFullScreen:nil];
362 }
363 }
364
365 bool InternalFlutter_Window_IsFullScreen(void* window) {
366 NSWindow* w = (__bridge NSWindow*)window;
367 return (w.styleMask & NSWindowStyleMaskFullScreen) != 0;
368 }
369
370 void InternalFlutter_Window_Activate(void* window) {
371 NSWindow* w = (__bridge NSWindow*)window;
372 [NSApplication.sharedApplication activateIgnoringOtherApps:YES];
373 [w makeKeyAndOrderFront:nil];
374 }
375
376 char* InternalFlutter_Window_GetTitle(void* window) {
377 NSWindow* w = (__bridge NSWindow*)window;
378 return strdup(w.title.UTF8String);
379 }
380
381 bool InternalFlutter_Window_IsActivated(void* window) {
382 NSWindow* w = (__bridge NSWindow*)window;
383 return w.isKeyWindow;
384 }
385
386 // NOLINTEND(google-objc-function-naming)
FlutterChannels.h
FlutterCodecs.h
FlutterEngine_Internal.h
FLUTTER_DARWIN_EXPORT
#define FLUTTER_DARWIN_EXPORT
Definition: FlutterMacros.h:14
FlutterViewIdentifier
int64_t FlutterViewIdentifier
Definition: FlutterViewController.h:21
FlutterViewController_Internal.h
FlutterWindowController.h
InternalFlutter_Window_Activate
void InternalFlutter_Window_Activate(void *window)
Definition: FlutterWindowController.mm:370
InternalFlutter_Window_GetHandle
void * InternalFlutter_Window_GetHandle(int64_t engine_id, FlutterViewIdentifier view_id)
Definition: FlutterWindowController.mm:294
InternalFlutter_Window_IsMaximized
bool InternalFlutter_Window_IsMaximized(void *window)
Definition: FlutterWindowController.mm:335
InternalFlutter_Window_SetConstraints
FLUTTER_DARWIN_EXPORT void InternalFlutter_Window_SetConstraints(void *window, const FlutterWindowConstraints *constraints)
Definition: FlutterWindowController.mm:315
InternalFlutter_Window_IsActivated
bool InternalFlutter_Window_IsActivated(void *window)
Definition: FlutterWindowController.mm:381
InternalFlutter_WindowController_CreateDialogWindow
int64_t InternalFlutter_WindowController_CreateDialogWindow(int64_t engine_id, const FlutterWindowCreationRequest *request)
Definition: FlutterWindowController.mm:280
InternalFlutter_WindowController_CreateRegularWindow
int64_t InternalFlutter_WindowController_CreateRegularWindow(int64_t engine_id, const FlutterWindowCreationRequest *request)
Definition: FlutterWindowController.mm:272
InternalFlutter_Window_SetFullScreen
void InternalFlutter_Window_SetFullScreen(void *window, bool fullScreen)
Definition: FlutterWindowController.mm:355
InternalFlutter_Window_IsFullScreen
bool InternalFlutter_Window_IsFullScreen(void *window)
Definition: FlutterWindowController.mm:365
InternalFlutter_Window_SetContentSize
void InternalFlutter_Window_SetContentSize(void *window, const FlutterWindowSize *size)
Definition: FlutterWindowController.mm:309
InternalFlutter_Window_Minimize
void InternalFlutter_Window_Minimize(void *window)
Definition: FlutterWindowController.mm:340
InternalFlutter_Window_GetTitle
char * InternalFlutter_Window_GetTitle(void *window)
Definition: FlutterWindowController.mm:376
InternalFlutter_Window_SetTitle
void InternalFlutter_Window_SetTitle(void *window, const char *title)
Definition: FlutterWindowController.mm:321
InternalFlutter_Window_IsMinimized
bool InternalFlutter_Window_IsMinimized(void *window)
Definition: FlutterWindowController.mm:350
InternalFlutter_Window_Destroy
void InternalFlutter_Window_Destroy(int64_t engine_id, void *window)
Definition: FlutterWindowController.mm:288
InternalFlutter_Window_SetMaximized
void InternalFlutter_Window_SetMaximized(void *window, bool maximized)
Definition: FlutterWindowController.mm:326
InternalFlutter_Window_GetContentSize
FlutterWindowSize InternalFlutter_Window_GetContentSize(void *window)
Definition: FlutterWindowController.mm:300
InternalFlutter_Window_Unminimize
void InternalFlutter_Window_Unminimize(void *window)
Definition: FlutterWindowController.mm:345
FlutterWindowController()::_windows
NSMutableArray< FlutterWindowOwner * > * _windows
Definition: FlutterWindowController.mm:133
-[FlutterWindowController(Testing) closeAllWindows]
void closeAllWindows()
NSWindow(FlutterWindowSizing)
Definition: FlutterWindowController.mm:34
flutter::Isolate::Current
static Isolate Current()
Definition: isolate_scope.cc:9
flutter::IsolateScope
Definition: isolate_scope.h:33
FlutterEngine
Definition: FlutterEngine.h:32
FlutterViewController
Definition: FlutterViewController.h:73
FlutterViewController::viewIdentifier
FlutterViewIdentifier viewIdentifier
Definition: FlutterViewController.h:130
FlutterWindowController
Definition: FlutterWindowController.h:16
FlutterWindowController::engine
FlutterEngine * engine
Definition: FlutterWindowController.h:17
FlutterWindowOwner
Definition: FlutterWindowController.mm:16
FlutterWindowOwner::_window
NSWindow * _window
Definition: FlutterWindowController.mm:19
FlutterWindowOwner::_flutterViewController
FlutterViewController * _flutterViewController
Definition: FlutterWindowController.mm:20
FlutterWindowOwner::window
NSWindow * window
Definition: FlutterWindowController.mm:25
FlutterWindowOwner::flutterViewController
FlutterViewController * flutterViewController
Definition: FlutterWindowController.mm:26
FlutterWindowOwner::_isolate
std::optional< flutter::Isolate > _isolate
Definition: FlutterWindowController.mm:21
FlutterWindowOwner::_creationRequest
FlutterWindowCreationRequest _creationRequest
Definition: FlutterWindowController.mm:22
isolate_scope.h
FlutterWindowConstraints
Definition: FlutterWindowController.h:32
FlutterWindowConstraints::max_width
double max_width
Definition: FlutterWindowController.h:35
FlutterWindowConstraints::min_height
double min_height
Definition: FlutterWindowController.h:34
FlutterWindowConstraints::max_height
double max_height
Definition: FlutterWindowController.h:36
FlutterWindowConstraints::min_width
double min_width
Definition: FlutterWindowController.h:33
FlutterWindowCreationRequest
Definition: FlutterWindowController.h:39
FlutterWindowCreationRequest::on_should_close
void(* on_should_close)()
Definition: FlutterWindowController.h:45
FlutterWindowCreationRequest::notify_listeners
void(* notify_listeners)()
Definition: FlutterWindowController.h:47
FlutterWindowCreationRequest::has_constraints
bool has_constraints
Definition: FlutterWindowController.h:42
FlutterWindowCreationRequest::has_size
bool has_size
Definition: FlutterWindowController.h:40
FlutterWindowCreationRequest::parent_view_id
int64_t parent_view_id
Definition: FlutterWindowController.h:44
FlutterWindowCreationRequest::on_will_close
void(* on_will_close)()
Definition: FlutterWindowController.h:46
FlutterWindowSize
Definition: FlutterWindowController.h:27