6 #include <Foundation/Foundation.h>
28 @property(readonly, nonatomic) NSWindow* window;
30 @property(readwrite, nonatomic) BOOL closeWhenParentResignsKey;
32 - (instancetype)initWithWindow:(NSWindow*)window
47 [
self setContentSize:NSMakeSize(contentSize.width, contentSize.height)];
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);
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);
61 [
self setContentMaxSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)];
63 if (!NSEqualSizes(originalSize, size)) {
64 [
self setContentSize:size];
84 - (instancetype)initWithWindow:(NSWindow*)window
87 if (
self = [super init]) {
96 - (void)windowDidBecomeKey:(NSNotification*)notification {
97 [_flutterViewController.engine windowDidBecomeKey:_flutterViewController.viewIdentifier];
100 - (void)windowDidResignKey:(NSNotification*)notification {
101 [_flutterViewController.engine windowDidResignKey:_flutterViewController.viewIdentifier];
102 [[_flutterViewController.engine windowController] windowDidResignKey:self];
105 - (BOOL)windowShouldClose:(NSWindow*)sender {
111 - (void)windowWillClose {
115 - (void)windowDidResize:(NSNotification*)notification {
122 - (void)windowDidMiniaturize:(NSNotification*)notification {
129 - (void)windowDidDeminiaturize:(NSNotification*)notification {
134 - (void)windowWillEnterFullScreen:(NSNotification*)notification {
139 - (void)windowWillExitFullScreen:(NSNotification*)notification {
144 - (std::optional<NSSize>)minimumViewSize:(
FlutterView*)view {
153 - (std::optional<NSSize>)maximumViewSize:(
FlutterView*)view {
158 NSSize screenSize =
self.window.screen.visibleFrame.size;
159 double width = screenSize.width;
164 double height = screenSize.height;
169 return NSMakeSize(width, height);
174 static NSRect ComputeGlobalScreenFrame() {
175 NSRect frame = NSZeroRect;
176 for (NSScreen* screen in [NSScreen screens]) {
177 NSRect screenFrame = screen.frame;
178 if (NSIsEmptyRect(frame)) {
181 frame = NSUnionRect(frame, screenFrame);
187 static void FlipRect(NSRect& rect,
const NSRect& globalScreenFrame) {
189 rect.origin.y = (globalScreenFrame.origin.y + globalScreenFrame.size.height) -
190 (rect.origin.y + rect.size.height);
193 - (void)updatePosition {
194 [
self viewDidUpdateContents:self.flutterViewController.flutterView
195 withSize:self.flutterViewController.flutterView.bounds.size];
198 - (void)viewDidUpdateContents:(
FlutterView*)view withSize:(NSSize)newSize {
204 NSRect globalScreenFrame = ComputeGlobalScreenFrame();
207 [
self.window.parentWindow contentRectForFrameRect:self.window.parentWindow.frame];
208 FlipRect(parentRect, globalScreenFrame);
210 NSRect screenRect = [
self.window.screen visibleFrame];
211 FlipRect(screenRect, globalScreenFrame);
218 NSRect positionRect = position->
toNSRect();
219 FlipRect(positionRect, globalScreenFrame);
221 [
self.window setFrame:positionRect display:NO animate:NO];
228 (positionRect.size.width < newSize.width || positionRect.size.height < newSize.height)) {
233 self.window.alphaValue = 1.0;
239 self->_creationRequest.constraints = constraints;
240 [_flutterViewController.flutterView constraintsDidChange];
242 [
self.window flutterSetConstraints:constraints];
253 - (BOOL)canBecomeKeyWindow {
257 - (BOOL)acceptsFirstResponder {
261 - (BOOL)canBecomeMainWindow {
272 - (NSRect)windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)newFrame {
274 newFrame.size.width = 110;
275 newFrame.size.height = 110;
283 - (instancetype)init {
286 _windows = [NSMutableArray array];
292 if (_runLoopModeFixApplied) {
302 NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
303 styleMask:NSWindowStyleMaskResizable
304 backing:NSBackingStoreNonretained
308 window.releasedWhenClosed = NO;
309 window.delegate = delegate;
310 window.ignoresMouseEvents = YES;
311 window.alphaValue = 0;
312 [window orderFront:nil];
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);
321 _runLoopModeFixApplied = YES;
329 NSWindow* window = [[NSWindow alloc] init];
332 [window setReleasedWhenClosed:NO];
334 window.contentViewController = controller;
336 NSWindowStyleMaskResizable | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable;
337 window.collectionBehavior = NSWindowCollectionBehaviorFullScreenAuxiliary;
338 if (request->has_size) {
339 [window flutterSetContentSize:request->size];
341 if (request->has_constraints) {
342 [window flutterSetConstraints:request->constraints];
346 flutterViewController:controller
347 creationRequest:*request];
348 window.delegate = owner;
349 [_windows addObject:owner];
351 NSWindow* parent = nil;
353 if (request->parent_view_id != 0) {
361 FML_LOG(WARNING) <<
"Failed to find parent window for ID " << request->parent_view_id;
366 dispatch_async(dispatch_get_main_queue(), ^{
367 [
self fixMoveRunLoopModeIfNeeded];
370 [parent beginCriticalSheet:window
371 completionHandler:^(NSModalResponse response){
376 [window setIsVisible:YES];
377 [window makeKeyAndOrderFront:nil];
388 NSWindow* window = [[NSWindow alloc] init];
391 [window setReleasedWhenClosed:NO];
393 window.contentViewController = controller;
394 window.styleMask = NSWindowStyleMaskBorderless;
395 window.hasShadow = NO;
400 flutterViewController:controller
401 creationRequest:*request];
403 controller.flutterView.sizingDelegate = owner;
406 [controller.flutterView constraintsDidChange];
409 window.delegate = owner;
410 [_windows addObject:owner];
412 NSWindow* parent = nil;
414 FML_DCHECK(request->parent_view_id != 0);
422 NSAssert(parent != nil,
@"Tooltip window must have a parent window.");
424 window.ignoresMouseEvents = YES;
425 window.collectionBehavior = NSWindowCollectionBehaviorAuxiliary;
426 [parent addChildWindow:window ordered:NSWindowAbove];
427 window.alphaValue = 0.0;
442 [window setReleasedWhenClosed:NO];
444 window.contentViewController = controller;
445 window.styleMask = NSWindowStyleMaskBorderless;
446 window.hasShadow = NO;
451 flutterViewController:controller
452 creationRequest:*request];
454 controller.flutterView.sizingDelegate = w;
455 [controller.flutterView setBackgroundColor:[NSColor clearColor]];
457 [controller.flutterView constraintsDidChange];
461 [_windows addObject:w];
463 NSWindow* parent = nil;
465 FML_DCHECK(request->parent_view_id != 0);
467 if (owner.flutterViewController.viewIdentifier == request->parent_view_id) {
468 parent = owner.window;
473 NSAssert(parent != nil,
@"Popup window must have a parent window.");
475 window.collectionBehavior = NSWindowCollectionBehaviorAuxiliary;
476 [parent addChildWindow:window ordered:NSWindowAbove];
477 window.alphaValue = 0.0;
486 NSWindow* window = [[NSWindow alloc] init];
489 [window setReleasedWhenClosed:NO];
491 window.contentViewController = controller;
492 window.styleMask = NSWindowStyleMaskResizable | NSWindowStyleMaskTitled |
493 NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
494 if (request->has_size) {
495 [window flutterSetContentSize:request->size];
497 if (request->has_constraints) {
498 [window flutterSetConstraints:request->constraints];
500 [window setIsVisible:YES];
501 [window makeKeyAndOrderFront:nil];
504 flutterViewController:controller
505 creationRequest:*request];
506 window.delegate = owner;
507 [_windows addObject:owner];
512 - (void)destroyWindow:(NSWindow*)window {
515 if (o.window == window) {
521 [_windows removeObject:owner];
522 for (NSWindow* win in owner.
window.sheets) {
523 [
self destroyWindow:win];
526 for (NSWindow* win in owner.
window.childWindows) {
527 [
self destroyWindow:win];
531 [owner.flutterViewController dispose];
532 owner.
window.delegate = nil;
533 [owner.window close];
534 [owner windowWillClose];
540 [owner.flutterViewController dispose];
541 [owner.window close];
543 [_windows removeAllObjects];
546 static BOOL IsChildAncestor(NSWindow* child, NSWindow* ancestor) {
547 NSWindow* current = child.parentWindow;
549 if (current == ancestor) {
552 current = current.parentWindow;
560 if (possibleChild.closeWhenParentResignsKey &&
561 IsChildAncestor(possibleChild.window, parent.window)) {
562 [possibleChild windowShouldClose:possibleChild.window];
575 [engine enableMultiView];
576 return [engine.windowController createRegularWindow:request];
583 [engine enableMultiView];
584 return [engine.windowController createDialogWindow:request];
591 [engine enableMultiView];
592 return [engine.windowController createTooltipWindow:request];
599 [engine enableMultiView];
600 return [engine.windowController createPopupWindow:request];
604 NSWindow* w = (__bridge NSWindow*)window;
606 [engine.windowController destroyWindow:w];
612 return (__bridge
void*)controller.view.window;
616 NSWindow* w = (__bridge NSWindow*)window;
617 NSRect contentRect = [w contentRectForFrameRect:w.frame];
619 .
width = contentRect.size.width,
620 .height = contentRect.size.height,
625 NSWindow* w = (__bridge NSWindow*)window;
626 [w flutterSetContentSize:*size];
632 NSWindow* w = (__bridge NSWindow*)window;
634 [owner setConstraints:*constraints];
638 NSWindow* w = (__bridge NSWindow*)window;
639 w.title = [NSString stringWithUTF8String:title];
643 NSWindow* w = (__bridge NSWindow*)window;
644 if (maximized & !w.isZoomed) {
646 }
else if (!maximized && w.isZoomed) {
652 NSWindow* w = (__bridge NSWindow*)window;
657 NSWindow* w = (__bridge NSWindow*)window;
662 NSWindow* w = (__bridge NSWindow*)window;
663 [w deminiaturize:nil];
667 NSWindow* w = (__bridge NSWindow*)window;
668 return w.isMiniaturized;
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];
682 NSWindow* w = (__bridge NSWindow*)window;
683 return (w.styleMask & NSWindowStyleMaskFullScreen) != 0;
687 NSWindow* w = (__bridge NSWindow*)window;
688 [NSApplication.sharedApplication activateIgnoringOtherApps:YES];
689 [w makeKeyAndOrderFront:nil];
693 NSWindow* w = (__bridge NSWindow*)window;
694 return strdup(w.title.UTF8String);
698 NSWindow* w = (__bridge NSWindow*)window;
699 return w.isKeyWindow;
703 NSWindow* w = (__bridge NSWindow*)window;
705 [owner updatePosition];
709 NSWindow* w = (__bridge NSWindow*)window;
710 NSWindow* parent = w.parentWindow;
714 NSRect globalScreenFrame = ComputeGlobalScreenFrame();
716 NSRect parentRect = [parent contentRectForFrameRect:parent.frame];
717 FlipRect(parentRect, globalScreenFrame);
719 NSRect childRect = w.frame;
720 FlipRect(childRect, globalScreenFrame);
723 .x = childRect.origin.x - parentRect.origin.x,
724 .y = childRect.origin.y - parentRect.origin.y,
#define FLUTTER_DARWIN_EXPORT
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
BOOL _runLoopModeFixApplied
NSColor * backgroundColor
FlutterMouseTrackingMode mouseTrackingMode
FlutterViewIdentifier viewIdentifier
void constraintsDidChange()
void fixMoveRunLoopModeIfNeeded()
FlutterViewController * _flutterViewController
FlutterViewController * flutterViewController
std::optional< flutter::Isolate > _isolate
CGSize _positionerSizeConstraints
FlutterWindowCreationRequest _creationRequest
BOOL closeWhenParentResignsKey
void(* on_should_close)()
void(* notify_listeners)()
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)