// 15 august 2015 #import "uipriv_darwin.h" #define defaultStyleMask (NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask) struct uiWindow { uiDarwinControl c; NSWindow *window; uiControl *child; int margined; int (*onClosing)(uiWindow *, void *); void *onClosingData; struct singleChildConstraints constraints; void (*onContentSizeChanged)(uiWindow *, void *); void *onContentSizeChangedData; BOOL suppressSizeChanged; int fullscreen; int borderless; }; @interface windowDelegateClass : NSObject { struct mapTable *windows; } - (BOOL)windowShouldClose:(id)sender; - (void)windowDidResize:(NSNotification *)note; - (void)windowDidEnterFullScreen:(NSNotification *)note; - (void)windowDidExitFullScreen:(NSNotification *)note; - (void)registerWindow:(uiWindow *)w; - (void)unregisterWindow:(uiWindow *)w; - (uiWindow *)lookupWindow:(NSWindow *)w; @end @implementation windowDelegateClass - (id)init { self = [super init]; if (self) self->windows = newMap(); return self; } - (void)dealloc { mapDestroy(self->windows); [super dealloc]; } - (BOOL)windowShouldClose:(id)sender { uiWindow *w; w = [self lookupWindow:((NSWindow *) sender)]; // w should not be NULL; we are only the delegate of registered windows if ((*(w->onClosing))(w, w->onClosingData)) uiControlDestroy(uiControl(w)); return NO; } - (void)windowDidResize:(NSNotification *)note { uiWindow *w; w = [self lookupWindow:((NSWindow *) [note object])]; if (!w->suppressSizeChanged) (*(w->onContentSizeChanged))(w, w->onContentSizeChangedData); } - (void)windowDidEnterFullScreen:(NSNotification *)note { uiWindow *w; w = [self lookupWindow:((NSWindow *) [note object])]; if (!w->suppressSizeChanged) w->fullscreen = 1; } - (void)windowDidExitFullScreen:(NSNotification *)note { uiWindow *w; w = [self lookupWindow:((NSWindow *) [note object])]; if (!w->suppressSizeChanged) w->fullscreen = 0; } - (void)registerWindow:(uiWindow *)w { mapSet(self->windows, w->window, w); [w->window setDelegate:self]; } - (void)unregisterWindow:(uiWindow *)w { [w->window setDelegate:nil]; mapDelete(self->windows, w->window); } - (uiWindow *)lookupWindow:(NSWindow *)w { uiWindow *v; v = uiWindow(mapGet(self->windows, w)); // this CAN (and IS ALLOWED TO) return NULL, just in case we're called with some OS X-provided window as the key window return v; } @end static windowDelegateClass *windowDelegate = nil; static void removeConstraints(uiWindow *w) { NSView *cv; cv = [w->window contentView]; singleChildConstraintsRemove(&(w->constraints), cv); } static void uiWindowDestroy(uiControl *c) { uiWindow *w = uiWindow(c); // hide the window [w->window orderOut:w->window]; removeConstraints(w); if (w->child != NULL) { uiControlSetParent(w->child, NULL); uiDarwinControlSetSuperview(uiDarwinControl(w->child), nil); uiControlDestroy(w->child); } [windowDelegate unregisterWindow:w]; [w->window release]; uiFreeControl(uiControl(w)); } uiDarwinControlDefaultHandle(uiWindow, window) uiControl *uiWindowParent(uiControl *c) { return NULL; } void uiWindowSetParent(uiControl *c, uiControl *parent) { uiUserBugCannotSetParentOnToplevel("uiWindow"); } static int uiWindowToplevel(uiControl *c) { return 1; } static int uiWindowVisible(uiControl *c) { uiWindow *w = uiWindow(c); return [w->window isVisible]; } static void uiWindowShow(uiControl *c) { uiWindow *w = (uiWindow *) c; [w->window makeKeyAndOrderFront:w->window]; } static void uiWindowHide(uiControl *c) { uiWindow *w = (uiWindow *) c; [w->window orderOut:w->window]; } uiDarwinControlDefaultEnabled(uiWindow, window) uiDarwinControlDefaultEnable(uiWindow, window) uiDarwinControlDefaultDisable(uiWindow, window) static void uiWindowSyncEnableState(uiDarwinControl *c, int enabled) { uiWindow *w = uiWindow(c); if (uiDarwinShouldStopSyncEnableState(uiDarwinControl(w), enabled)) return; if (w->child != NULL) uiDarwinControlSyncEnableState(uiDarwinControl(w->child), enabled); } static void uiWindowSetSuperview(uiDarwinControl *c, NSView *superview) { // TODO } static void windowRelayout(uiWindow *w) { NSView *childView; NSView *contentView; removeConstraints(w); if (w->child == NULL) return; childView = (NSView *) uiControlHandle(w->child); contentView = [w->window contentView]; singleChildConstraintsEstablish(&(w->constraints), contentView, childView, uiDarwinControlHugsTrailingEdge(uiDarwinControl(w->child)), uiDarwinControlHugsBottom(uiDarwinControl(w->child)), w->margined, @"uiWindow"); } uiDarwinControlDefaultHugsTrailingEdge(uiWindow, window) uiDarwinControlDefaultHugsBottom(uiWindow, window) static void uiWindowChildEdgeHuggingChanged(uiDarwinControl *c) { uiWindow *w = uiWindow(c); windowRelayout(w); } // TODO uiDarwinControlDefaultHuggingPriority(uiWindow, window) uiDarwinControlDefaultSetHuggingPriority(uiWindow, window) // end TODO static void uiWindowChildVisibilityChanged(uiDarwinControl *c) { uiWindow *w = uiWindow(c); windowRelayout(w); } char *uiWindowTitle(uiWindow *w) { return uiDarwinNSStringToText([w->window title]); } void uiWindowSetTitle(uiWindow *w, const char *title) { [w->window setTitle:toNSString(title)]; } void uiWindowContentSize(uiWindow *w, int *width, int *height) { NSRect r; r = [w->window contentRectForFrameRect:[w->window frame]]; *width = r.size.width; *height = r.size.height; } void uiWindowSetContentSize(uiWindow *w, int width, int height) { w->suppressSizeChanged = YES; [w->window setContentSize:NSMakeSize(width, height)]; w->suppressSizeChanged = NO; } int uiWindowFullscreen(uiWindow *w) { return w->fullscreen; } void uiWindowSetFullscreen(uiWindow *w, int fullscreen) { if (w->fullscreen && fullscreen) return; if (!w->fullscreen && !fullscreen) return; w->fullscreen = fullscreen; if (w->fullscreen && w->borderless) // borderless doesn't play nice with fullscreen; don't toggle while borderless return; w->suppressSizeChanged = YES; [w->window toggleFullScreen:w->window]; w->suppressSizeChanged = NO; if (!w->fullscreen && w->borderless) // borderless doesn't play nice with fullscreen; restore borderless after removing [w->window setStyleMask:NSBorderlessWindowMask]; } void uiWindowOnContentSizeChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) { w->onContentSizeChanged = f; w->onContentSizeChangedData = data; } void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *, void *), void *data) { w->onClosing = f; w->onClosingData = data; } int uiWindowBorderless(uiWindow *w) { return w->borderless; } void uiWindowSetBorderless(uiWindow *w, int borderless) { w->borderless = borderless; if (w->borderless) { // borderless doesn't play nice with fullscreen; wait for later if (!w->fullscreen) [w->window setStyleMask:NSBorderlessWindowMask]; } else { [w->window setStyleMask:defaultStyleMask]; // borderless doesn't play nice with fullscreen; restore state if (w->fullscreen) { w->suppressSizeChanged = YES; [w->window toggleFullScreen:w->window]; w->suppressSizeChanged = NO; } } } void uiWindowSetChild(uiWindow *w, uiControl *child) { NSView *childView; if (w->child != NULL) { childView = (NSView *) uiControlHandle(w->child); [childView removeFromSuperview]; uiControlSetParent(w->child, NULL); } w->child = child; if (w->child != NULL) { uiControlSetParent(w->child, uiControl(w)); childView = (NSView *) uiControlHandle(w->child); uiDarwinControlSetSuperview(uiDarwinControl(w->child), [w->window contentView]); uiDarwinControlSyncEnableState(uiDarwinControl(w->child), uiControlEnabledToUser(uiControl(w))); } windowRelayout(w); } int uiWindowMargined(uiWindow *w) { return w->margined; } void uiWindowSetMargined(uiWindow *w, int margined) { w->margined = margined; singleChildConstraintsSetMargined(&(w->constraints), w->margined); } static int defaultOnClosing(uiWindow *w, void *data) { return 0; } static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data) { // do nothing } uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) { uiWindow *w; finalizeMenus(); uiDarwinNewControl(uiWindow, w); w->window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, (CGFloat) width, (CGFloat) height) styleMask:defaultStyleMask backing:NSBackingStoreBuffered defer:YES]; [w->window setTitle:toNSString(title)]; // do NOT release when closed // we manually do this in uiWindowDestroy() above [w->window setReleasedWhenClosed:NO]; if (windowDelegate == nil) { windowDelegate = [[windowDelegateClass new] autorelease]; [delegates addObject:windowDelegate]; } [windowDelegate registerWindow:w]; uiWindowOnClosing(w, defaultOnClosing, NULL); uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL); return w; } // utility function for menus uiWindow *windowFromNSWindow(NSWindow *w) { if (w == nil) return NULL; if (windowDelegate == nil) // no windows were created yet; we're called with some OS X-provided window return NULL; return [windowDelegate lookupWindow:w]; }