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, FlutterViewSizingDelegate> {
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  // Extra size constraints coming from the window positioner.
26 }
27 
28 @property(readonly, nonatomic) NSWindow* window;
29 @property(readonly, nonatomic) FlutterViewController* flutterViewController;
30 @property(readwrite, nonatomic) BOOL closeWhenParentResignsKey;
31 
32 - (instancetype)initWithWindow:(NSWindow*)window
33  flutterViewController:(FlutterViewController*)viewController
34  creationRequest:(const FlutterWindowCreationRequest&)creationRequest;
35 
36 @end
37 
39 
40 - (void)flutterSetContentSize:(FlutterWindowSize)contentSize;
41 - (void)flutterSetConstraints:(FlutterWindowConstraints)constraints;
42 
43 @end
44 
45 @implementation NSWindow (FlutterWindowSizing)
46 - (void)flutterSetContentSize:(FlutterWindowSize)contentSize {
47  [self setContentSize:NSMakeSize(contentSize.width, contentSize.height)];
48 }
49 
50 - (void)flutterSetConstraints:(FlutterWindowConstraints)constraints {
51  NSSize size = [self frameRectForContentRect:self.frame].size;
52  NSSize originalSize = size;
53  [self setContentMinSize:NSMakeSize(constraints.min_width, constraints.min_height)];
54  size.width = std::max(size.width, constraints.min_width);
55  size.height = std::max(size.height, constraints.min_height);
56  if (constraints.max_width > 0 && constraints.max_height > 0) {
57  [self setContentMaxSize:NSMakeSize(constraints.max_width, constraints.max_height)];
58  size.width = std::min(size.width, constraints.max_width);
59  size.height = std::min(size.height, constraints.max_height);
60  } else {
61  [self setContentMaxSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)];
62  }
63  if (!NSEqualSizes(originalSize, size)) {
64  [self setContentSize:size];
65  }
66 }
67 
68 @end
69 
70 @interface FlutterWindowController () {
71  NSMutableArray<FlutterWindowOwner*>* _windows;
73 }
74 
75 - (void)windowDidResignKey:(FlutterWindowOwner*)window;
76 
77 @end
78 
79 @implementation FlutterWindowOwner
80 
81 @synthesize window = _window;
83 
84 - (instancetype)initWithWindow:(NSWindow*)window
85  flutterViewController:(FlutterViewController*)viewController
86  creationRequest:(const FlutterWindowCreationRequest&)creationRequest {
87  if (self = [super init]) {
88  _window = window;
89  _flutterViewController = viewController;
90  _creationRequest = creationRequest;
92  }
93  return self;
94 }
95 
96 - (void)windowDidBecomeKey:(NSNotification*)notification {
97  [_flutterViewController.engine windowDidBecomeKey:_flutterViewController.viewIdentifier];
98 }
99 
100 - (void)windowDidResignKey:(NSNotification*)notification {
101  [_flutterViewController.engine windowDidResignKey:_flutterViewController.viewIdentifier];
102  [[_flutterViewController.engine windowController] windowDidResignKey:self];
103 }
104 
105 - (BOOL)windowShouldClose:(NSWindow*)sender {
106  flutter::IsolateScope isolate_scope(*_isolate);
108  return NO;
109 }
110 
111 - (void)windowWillClose {
113 }
114 
115 - (void)windowDidResize:(NSNotification*)notification {
116  flutter::IsolateScope isolate_scope(*_isolate);
118 }
119 
120 // Miniaturize does not trigger resize event, but for now there
121 // is no other way to get notification about the state change.
122 - (void)windowDidMiniaturize:(NSNotification*)notification {
123  flutter::IsolateScope isolate_scope(*_isolate);
125 }
126 
127 // Deminiaturize does not trigger resize event, but for now there
128 // is no other way to get notification about the state change.
129 - (void)windowDidDeminiaturize:(NSNotification*)notification {
130  flutter::IsolateScope isolate_scope(*_isolate);
132 }
133 
134 - (void)windowWillEnterFullScreen:(NSNotification*)notification {
135  flutter::IsolateScope isolate_scope(*_isolate);
137 }
138 
139 - (void)windowWillExitFullScreen:(NSNotification*)notification {
140  flutter::IsolateScope isolate_scope(*_isolate);
142 }
143 
144 - (std::optional<NSSize>)minimumViewSize:(FlutterView*)view {
146  return NSMakeSize(_creationRequest.constraints.min_width,
148  } else {
149  return std::nullopt;
150  }
151 }
152 
153 - (std::optional<NSSize>)maximumViewSize:(FlutterView*)view {
155  // Window is not sized to contents.
156  return std::nullopt;
157  }
158  NSSize screenSize = self.window.screen.visibleFrame.size;
159  double width = screenSize.width;
160  width = std::min(width, _creationRequest.constraints.max_width);
161  if (_positionerSizeConstraints.width > 0) {
162  width = std::min(width, _positionerSizeConstraints.width);
163  }
164  double height = screenSize.height;
165  height = std::min(height, _creationRequest.constraints.max_height);
166  if (_positionerSizeConstraints.height > 0) {
167  height = std::min(height, _positionerSizeConstraints.height);
168  }
169  return NSMakeSize(width, height);
170 }
171 
172 // Returns the frame that includes all screen. This is used to flip coordinates
173 // of individual screen to match Flutter coordinate system.
174 static NSRect ComputeGlobalScreenFrame() {
175  NSRect frame = NSZeroRect;
176  for (NSScreen* screen in [NSScreen screens]) {
177  NSRect screenFrame = screen.frame;
178  if (NSIsEmptyRect(frame)) {
179  frame = screenFrame;
180  } else {
181  frame = NSUnionRect(frame, screenFrame);
182  }
183  }
184  return frame;
185 }
186 
187 static void FlipRect(NSRect& rect, const NSRect& globalScreenFrame) {
188  // Flip the y coordinate to match Flutter coordinate system.
189  rect.origin.y = (globalScreenFrame.origin.y + globalScreenFrame.size.height) -
190  (rect.origin.y + rect.size.height);
191 }
192 
193 - (void)updatePosition {
194  [self viewDidUpdateContents:self.flutterViewController.flutterView
195  withSize:self.flutterViewController.flutterView.bounds.size];
196 }
197 
198 - (void)viewDidUpdateContents:(FlutterView*)view withSize:(NSSize)newSize {
199  if (_creationRequest.on_get_window_position == nullptr) {
200  // There is no positioner associated with this window.
201  return;
202  }
203 
204  NSRect globalScreenFrame = ComputeGlobalScreenFrame();
205 
206  NSRect parentRect =
207  [self.window.parentWindow contentRectForFrameRect:self.window.parentWindow.frame];
208  FlipRect(parentRect, globalScreenFrame);
209 
210  NSRect screenRect = [self.window.screen visibleFrame];
211  FlipRect(screenRect, globalScreenFrame);
212 
213  flutter::IsolateScope isolate_scope(*_isolate);
216  FlutterWindowRect::fromNSRect(screenRect));
217 
218  NSRect positionRect = position->toNSRect();
219  FlipRect(positionRect, globalScreenFrame);
220 
221  [self.window setFrame:positionRect display:NO animate:NO];
222 
223  free(position);
224 
225  // For windows sized to contents if the positioner size doesn't match actual size
226  // the requested size needs to be passed through constraints.
227  if (view.sizedToContents &&
228  (positionRect.size.width < newSize.width || positionRect.size.height < newSize.height)) {
229  _positionerSizeConstraints = positionRect.size;
230  [view constraintsDidChange];
231  } else {
232  // Only show the window initially if positioner agrees with the size.
233  self.window.alphaValue = 1.0;
234  }
235 }
236 
237 - (void)setConstraints:(FlutterWindowConstraints)constraints {
238  if (_flutterViewController.flutterView.sizedToContents) {
239  self->_creationRequest.constraints = constraints;
240  [_flutterViewController.flutterView constraintsDidChange];
241  } else {
242  [self.window flutterSetConstraints:constraints];
243  }
244 }
245 
246 @end
247 
248 @interface FlutterPopupWindow : NSPanel
249 @end
250 
251 @implementation FlutterPopupWindow
252 
253 - (BOOL)canBecomeKeyWindow {
254  return NO;
255 }
256 
257 - (BOOL)acceptsFirstResponder {
258  return NO;
259 }
260 
261 - (BOOL)canBecomeMainWindow {
262  return NO;
263 }
264 
265 @end
266 
267 @interface _FlutterRunLoopModeFixWindowDelegate : NSObject <NSWindowDelegate>
268 @end
269 
271 
272 - (NSRect)windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)newFrame {
273  // Small frame difference (100px to 110px) to trigger the animation but make it fast.
274  newFrame.size.width = 110;
275  newFrame.size.height = 110;
276  return newFrame;
277 }
278 
279 @end
280 
281 @implementation FlutterWindowController
282 
283 - (instancetype)init {
284  self = [super init];
285  if (self != nil) {
286  _windows = [NSMutableArray array];
287  }
288  return self;
289 }
290 
292  if (_runLoopModeFixApplied) {
293  return;
294  }
295  // When modal sheet is presented, for the duration of the animation the run loop
296  // is running in _NSMoveTimerRunLoopMode. This prevents Flutter content from
297  // being rendered. The solution is to add _NSMoveTimerRunLoopMode to common
298  // run loop modes, but referencing it directly may be flagged as SPI usage.
299  // Instead as a workaround a very short window zoom animation is triggered, which
300  // runs in _NSMoveTimerRunLoopMode adding the mode to CFRunLoop all modes list.
301  // After that it can be queried and added to common modes.
302  NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
303  styleMask:NSWindowStyleMaskResizable
304  backing:NSBackingStoreNonretained
305  defer:NO];
308  window.releasedWhenClosed = NO;
309  window.delegate = delegate;
310  window.ignoresMouseEvents = YES;
311  window.alphaValue = 0;
312  [window orderFront:nil];
313  [window zoom:nil];
314  [window close];
315  NSArray* modes = (__bridge_transfer NSArray*)CFRunLoopCopyAllModes(CFRunLoopGetCurrent());
316  for (NSString* mode in modes) {
317  if ([mode hasSuffix:@"MoveTimerRunLoopMode"]) {
318  CFRunLoopAddCommonMode(CFRunLoopGetCurrent(), (__bridge CFStringRef)mode);
319  }
320  }
321  _runLoopModeFixApplied = YES;
322 }
323 
324 - (FlutterViewIdentifier)createDialogWindow:(const FlutterWindowCreationRequest*)request {
325  FlutterViewController* controller = [[FlutterViewController alloc] initWithEngine:_engine
326  nibName:nil
327  bundle:nil];
328 
329  NSWindow* window = [[NSWindow alloc] init];
330  // If this is not set there will be double free on window close when
331  // using ARC.
332  [window setReleasedWhenClosed:NO];
333 
334  window.contentViewController = controller;
335  window.styleMask =
336  NSWindowStyleMaskResizable | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable;
337  window.collectionBehavior = NSWindowCollectionBehaviorFullScreenAuxiliary;
338  if (request->has_size) {
339  [window flutterSetContentSize:request->size];
340  }
341  if (request->has_constraints) {
342  [window flutterSetConstraints:request->constraints];
343  }
344 
345  FlutterWindowOwner* owner = [[FlutterWindowOwner alloc] initWithWindow:window
346  flutterViewController:controller
347  creationRequest:*request];
348  window.delegate = owner;
349  [_windows addObject:owner];
350 
351  NSWindow* parent = nil;
352 
353  if (request->parent_view_id != 0) {
354  for (FlutterWindowOwner* owner in _windows) {
355  if (owner.flutterViewController.viewIdentifier == request->parent_view_id) {
356  parent = owner.window;
357  break;
358  }
359  }
360  if (parent == nil) {
361  FML_LOG(WARNING) << "Failed to find parent window for ID " << request->parent_view_id;
362  }
363  }
364 
365  if (parent != nil) {
366  dispatch_async(dispatch_get_main_queue(), ^{
367  [self fixMoveRunLoopModeIfNeeded];
368  // beginCriticalSheet blocks with nested run loop until the
369  // sheet animation is finished.
370  [parent beginCriticalSheet:window
371  completionHandler:^(NSModalResponse response){
372  }];
373  });
374 
375  } else {
376  [window setIsVisible:YES];
377  [window makeKeyAndOrderFront:nil];
378  }
379 
380  return controller.viewIdentifier;
381 }
382 
383 - (FlutterViewIdentifier)createTooltipWindow:(const FlutterWindowCreationRequest*)request {
384  FlutterViewController* controller = [[FlutterViewController alloc] initWithEngine:_engine
385  nibName:nil
386  bundle:nil];
387 
388  NSWindow* window = [[NSWindow alloc] init];
389  // If this is not set there will be double free on window close when
390  // using ARC.
391  [window setReleasedWhenClosed:NO];
392 
393  window.contentViewController = controller;
394  window.styleMask = NSWindowStyleMaskBorderless;
395  window.hasShadow = NO;
396  window.opaque = NO;
397  window.backgroundColor = [NSColor clearColor];
398 
399  FlutterWindowOwner* owner = [[FlutterWindowOwner alloc] initWithWindow:window
400  flutterViewController:controller
401  creationRequest:*request];
402 
403  controller.flutterView.sizingDelegate = owner;
404  controller.flutterView.backgroundColor = [NSColor clearColor];
405  // Resend configure event after setting the sizing delegate.
406  [controller.flutterView constraintsDidChange];
407  owner.closeWhenParentResignsKey = YES;
408 
409  window.delegate = owner;
410  [_windows addObject:owner];
411 
412  NSWindow* parent = nil;
413 
414  FML_DCHECK(request->parent_view_id != 0);
415  for (FlutterWindowOwner* owner in _windows) {
416  if (owner.flutterViewController.viewIdentifier == request->parent_view_id) {
417  parent = owner.window;
418  break;
419  }
420  }
421 
422  NSAssert(parent != nil, @"Tooltip window must have a parent window.");
423 
424  window.ignoresMouseEvents = YES;
425  window.collectionBehavior = NSWindowCollectionBehaviorAuxiliary;
426  [parent addChildWindow:window ordered:NSWindowAbove];
427  window.alphaValue = 0.0;
428  return controller.viewIdentifier;
429 }
430 
431 - (FlutterViewIdentifier)createPopupWindow:(const FlutterWindowCreationRequest*)request {
432  FlutterViewController* controller = [[FlutterViewController alloc] initWithEngine:_engine
433  nibName:nil
434  bundle:nil];
435  // By default this is kFlutterMouseTrackingModeInKeyWindow but popup window is never
436  // key window.
437  controller.mouseTrackingMode = kFlutterMouseTrackingModeInActiveApp;
438 
439  NSWindow* window = [[FlutterPopupWindow alloc] init];
440  // If this is not set there will be double free on window close when
441  // using ARC.
442  [window setReleasedWhenClosed:NO];
443 
444  window.contentViewController = controller;
445  window.styleMask = NSWindowStyleMaskBorderless;
446  window.hasShadow = NO;
447  window.opaque = NO;
448  window.backgroundColor = [NSColor clearColor];
449 
450  FlutterWindowOwner* w = [[FlutterWindowOwner alloc] initWithWindow:window
451  flutterViewController:controller
452  creationRequest:*request];
453 
454  controller.flutterView.sizingDelegate = w;
455  [controller.flutterView setBackgroundColor:[NSColor clearColor]];
456  // Resend configure event after setting the sizing delegate.
457  [controller.flutterView constraintsDidChange];
459 
460  window.delegate = w;
461  [_windows addObject:w];
462 
463  NSWindow* parent = nil;
464 
465  FML_DCHECK(request->parent_view_id != 0);
466  for (FlutterWindowOwner* owner in _windows) {
467  if (owner.flutterViewController.viewIdentifier == request->parent_view_id) {
468  parent = owner.window;
469  break;
470  }
471  }
472 
473  NSAssert(parent != nil, @"Popup window must have a parent window.");
474 
475  window.collectionBehavior = NSWindowCollectionBehaviorAuxiliary;
476  [parent addChildWindow:window ordered:NSWindowAbove];
477  window.alphaValue = 0.0;
478  return controller.viewIdentifier;
479 }
480 
481 - (FlutterViewIdentifier)createRegularWindow:(const FlutterWindowCreationRequest*)request {
482  FlutterViewController* controller = [[FlutterViewController alloc] initWithEngine:_engine
483  nibName:nil
484  bundle:nil];
485 
486  NSWindow* window = [[NSWindow alloc] init];
487  // If this is not set there will be double free on window close when
488  // using ARC.
489  [window setReleasedWhenClosed:NO];
490 
491  window.contentViewController = controller;
492  window.styleMask = NSWindowStyleMaskResizable | NSWindowStyleMaskTitled |
493  NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
494  if (request->has_size) {
495  [window flutterSetContentSize:request->size];
496  }
497  if (request->has_constraints) {
498  [window flutterSetConstraints:request->constraints];
499  }
500  [window setIsVisible:YES];
501  [window makeKeyAndOrderFront:nil];
502 
503  FlutterWindowOwner* owner = [[FlutterWindowOwner alloc] initWithWindow:window
504  flutterViewController:controller
505  creationRequest:*request];
506  window.delegate = owner;
507  [_windows addObject:owner];
508 
509  return controller.viewIdentifier;
510 }
511 
512 - (void)destroyWindow:(NSWindow*)window {
513  FlutterWindowOwner* owner = nil;
514  for (FlutterWindowOwner* o in _windows) {
515  if (o.window == window) {
516  owner = o;
517  break;
518  }
519  }
520  if (owner != nil) {
521  [_windows removeObject:owner];
522  for (NSWindow* win in owner.window.sheets) {
523  [self destroyWindow:win];
524  }
525 
526  for (NSWindow* win in owner.window.childWindows) {
527  [self destroyWindow:win];
528  }
529  // Make sure to unregister the controller from the engine and remove the FlutterView
530  // before destroying the window and Flutter NSView.
531  [owner.flutterViewController dispose];
532  owner.window.delegate = nil;
533  [owner.window close];
534  [owner windowWillClose];
535  }
536 }
537 
538 - (void)closeAllWindows {
539  for (FlutterWindowOwner* owner in _windows) {
540  [owner.flutterViewController dispose];
541  [owner.window close];
542  }
543  [_windows removeAllObjects];
544 }
545 
546 static BOOL IsChildAncestor(NSWindow* child, NSWindow* ancestor) {
547  NSWindow* current = child.parentWindow;
548  while (current) {
549  if (current == ancestor) {
550  return YES;
551  }
552  current = current.parentWindow;
553  }
554 
555  return NO;
556 }
557 
558 - (void)windowDidResignKey:(FlutterWindowOwner*)parent {
559  for (FlutterWindowOwner* possibleChild in _windows) {
560  if (possibleChild.closeWhenParentResignsKey &&
561  IsChildAncestor(possibleChild.window, parent.window)) {
562  [possibleChild windowShouldClose:possibleChild.window];
563  }
564  }
565 }
566 
567 @end
568 
569 // NOLINTBEGIN(google-objc-function-naming)
570 
572  int64_t engine_id,
573  const FlutterWindowCreationRequest* request) {
574  FlutterEngine* engine = [FlutterEngine engineForIdentifier:engine_id];
575  [engine enableMultiView];
576  return [engine.windowController createRegularWindow:request];
577 }
578 
580  int64_t engine_id,
581  const FlutterWindowCreationRequest* request) {
582  FlutterEngine* engine = [FlutterEngine engineForIdentifier:engine_id];
583  [engine enableMultiView];
584  return [engine.windowController createDialogWindow:request];
585 }
586 
588  int64_t engine_id,
589  const FlutterWindowCreationRequest* request) {
590  FlutterEngine* engine = [FlutterEngine engineForIdentifier:engine_id];
591  [engine enableMultiView];
592  return [engine.windowController createTooltipWindow:request];
593 }
594 
596  int64_t engine_id,
597  const FlutterWindowCreationRequest* request) {
598  FlutterEngine* engine = [FlutterEngine engineForIdentifier:engine_id];
599  [engine enableMultiView];
600  return [engine.windowController createPopupWindow:request];
601 }
602 
603 void InternalFlutter_Window_Destroy(int64_t engine_id, void* window) {
604  NSWindow* w = (__bridge NSWindow*)window;
605  FlutterEngine* engine = [FlutterEngine engineForIdentifier:engine_id];
606  [engine.windowController destroyWindow:w];
607 }
608 
609 void* InternalFlutter_Window_GetHandle(int64_t engine_id, FlutterViewIdentifier view_id) {
610  FlutterEngine* engine = [FlutterEngine engineForIdentifier:engine_id];
611  FlutterViewController* controller = [engine viewControllerForIdentifier:view_id];
612  return (__bridge void*)controller.view.window;
613 }
614 
616  NSWindow* w = (__bridge NSWindow*)window;
617  NSRect contentRect = [w contentRectForFrameRect:w.frame];
618  return {
619  .width = contentRect.size.width,
620  .height = contentRect.size.height,
621  };
622 }
623 
625  NSWindow* w = (__bridge NSWindow*)window;
626  [w flutterSetContentSize:*size];
627 }
628 
631  const FlutterWindowConstraints* constraints) {
632  NSWindow* w = (__bridge NSWindow*)window;
633  FlutterWindowOwner* owner = (FlutterWindowOwner*)w.delegate;
634  [owner setConstraints:*constraints];
635 }
636 
637 void InternalFlutter_Window_SetTitle(void* window, const char* title) {
638  NSWindow* w = (__bridge NSWindow*)window;
639  w.title = [NSString stringWithUTF8String:title];
640 }
641 
642 void InternalFlutter_Window_SetMaximized(void* window, bool maximized) {
643  NSWindow* w = (__bridge NSWindow*)window;
644  if (maximized & !w.isZoomed) {
645  [w zoom:nil];
646  } else if (!maximized && w.isZoomed) {
647  [w zoom:nil];
648  }
649 }
650 
652  NSWindow* w = (__bridge NSWindow*)window;
653  return w.isZoomed;
654 }
655 
657  NSWindow* w = (__bridge NSWindow*)window;
658  [w miniaturize:nil];
659 }
660 
662  NSWindow* w = (__bridge NSWindow*)window;
663  [w deminiaturize:nil];
664 }
665 
667  NSWindow* w = (__bridge NSWindow*)window;
668  return w.isMiniaturized;
669 }
670 
671 void InternalFlutter_Window_SetFullScreen(void* window, bool fullScreen) {
672  NSWindow* w = (__bridge NSWindow*)window;
673  bool isFullScreen = (w.styleMask & NSWindowStyleMaskFullScreen) != 0;
674  if (fullScreen && !isFullScreen) {
675  [w toggleFullScreen:nil];
676  } else if (!fullScreen && isFullScreen) {
677  [w toggleFullScreen:nil];
678  }
679 }
680 
682  NSWindow* w = (__bridge NSWindow*)window;
683  return (w.styleMask & NSWindowStyleMaskFullScreen) != 0;
684 }
685 
687  NSWindow* w = (__bridge NSWindow*)window;
688  [NSApplication.sharedApplication activateIgnoringOtherApps:YES];
689  [w makeKeyAndOrderFront:nil];
690 }
691 
692 char* InternalFlutter_Window_GetTitle(void* window) {
693  NSWindow* w = (__bridge NSWindow*)window;
694  return strdup(w.title.UTF8String);
695 }
696 
698  NSWindow* w = (__bridge NSWindow*)window;
699  return w.isKeyWindow;
700 }
701 
703  NSWindow* w = (__bridge NSWindow*)window;
704  FlutterWindowOwner* owner = (FlutterWindowOwner*)w.delegate;
705  [owner updatePosition];
706 }
707 
709  NSWindow* w = (__bridge NSWindow*)window;
710  NSWindow* parent = w.parentWindow;
711  if (!parent) {
712  return {0, 0};
713  }
714  NSRect globalScreenFrame = ComputeGlobalScreenFrame();
715 
716  NSRect parentRect = [parent contentRectForFrameRect:parent.frame];
717  FlipRect(parentRect, globalScreenFrame);
718 
719  NSRect childRect = w.frame;
720  FlipRect(childRect, globalScreenFrame);
721 
722  return {
723  .x = childRect.origin.x - parentRect.origin.x,
724  .y = childRect.origin.y - parentRect.origin.y,
725  };
726 }
727 
728 // 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)
int64_t InternalFlutter_WindowController_CreateTooltipWindow(int64_t engine_id, const FlutterWindowCreationRequest *request)
int64_t InternalFlutter_WindowController_CreatePopupWindow(int64_t engine_id, const FlutterWindowCreationRequest *request)
FLUTTER_DARWIN_EXPORT void InternalFlutter_Window_SetConstraints(void *window, const FlutterWindowConstraints *constraints)
bool InternalFlutter_Window_IsActivated(void *window)
int64_t InternalFlutter_WindowController_CreateDialogWindow(int64_t engine_id, const FlutterWindowCreationRequest *request)
FlutterWindowOffset InternalFlutter_Window_GetOffsetInParent(void *window)
int64_t InternalFlutter_WindowController_CreateRegularWindow(int64_t engine_id, const FlutterWindowCreationRequest *request)
void InternalFlutter_Window_SetFullScreen(void *window, bool fullScreen)
void InternalFlutter_Window_UpdatePosition(void *window)
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
FlutterMouseTrackingMode mouseTrackingMode
FlutterViewIdentifier viewIdentifier
BOOL sizedToContents
Definition: FlutterView.h:121
void constraintsDidChange()
Definition: FlutterView.mm:200
FlutterViewController * _flutterViewController
FlutterViewController * flutterViewController
std::optional< flutter::Isolate > _isolate
FlutterWindowCreationRequest _creationRequest
struct FlutterWindowConstraints constraints
FlutterWindowRect *(* on_get_window_position)(const FlutterWindowSize &child_size, const FlutterWindowRect &parent_rect, const FlutterWindowRect &output_rect)
static FlutterWindowRect fromNSRect(const NSRect &rect)
static FlutterWindowSize fromNSSize(const NSSize &size)