diff --git a/app/os_macos.go b/app/os_macos.go index f133ab10..d62bb645 100644 --- a/app/os_macos.go +++ b/app/os_macos.go @@ -109,6 +109,14 @@ static void makeKeyAndOrderFront(CFTypeRef windowRef) { } } +static void makeFirstResponder(CFTypeRef windowRef, CFTypeRef viewRef) { + @autoreleasepool { + NSWindow *window = (__bridge NSWindow *)windowRef; + NSView *view = (__bridge NSView *)viewRef; + [window makeFirstResponder:view]; + } +} + static void toggleFullScreen(CFTypeRef windowRef) { @autoreleasepool { NSWindow *window = (__bridge NSWindow *)windowRef; @@ -238,6 +246,13 @@ static int isWindowZoomed(CFTypeRef windowRef) { } } +static int isWindowMiniaturized(CFTypeRef windowRef) { + @autoreleasepool { + NSWindow *window = (__bridge NSWindow *)windowRef; + return window.miniaturized ? 1 : 0; + } +} + static void zoomWindow(CFTypeRef windowRef) { @autoreleasepool { NSWindow *window = (__bridge NSWindow *)windowRef; @@ -318,7 +333,6 @@ type window struct { view C.CFTypeRef w *callbacks anim bool - visible bool displayLink *displayLink // redraw is a single entry channel for making sure only one // display link redraw request is in flight. @@ -367,11 +381,23 @@ func (w *window) WriteClipboard(mime string, s []byte) { } func (w *window) updateWindowMode() { + w.scale = float32(C.getViewBackingScale(w.view)) + wf, hf := float32(C.viewWidth(w.view)), float32(C.viewHeight(w.view)) + w.config.Size = image.Point{ + X: int(wf*w.scale + .5), + Y: int(hf*w.scale + .5), + } + w.config.Mode = Windowed + window := C.windowForView(w.view) + if window == 0 { + return + } style := int(C.getWindowStyleMask(C.windowForView(w.view))) - if style&C.NSWindowStyleMaskFullScreen != 0 { + switch { + case style&C.NSWindowStyleMaskFullScreen != 0: w.config.Mode = Fullscreen - } else { - w.config.Mode = Windowed + case C.isWindowZoomed(window) != 0: + w.config.Mode = Maximized } w.config.Decorated = style&C.NSWindowStyleMaskFullSizeContentView == 0 } @@ -379,105 +405,82 @@ func (w *window) updateWindowMode() { func (w *window) Configure(options []Option) { screenScale := float32(C.getScreenBackingScale()) cfg := configFor(screenScale) - prev := w.config - w.updateWindowMode() cnf := w.config cnf.apply(cfg, options) window := C.windowForView(w.view) + mask := C.getWindowStyleMask(window) + fullscreen := mask&C.NSWindowStyleMaskFullScreen != 0 switch cnf.Mode { case Fullscreen: - switch prev.Mode { - case Fullscreen: - case Minimized: + if C.isWindowMiniaturized(window) != 0 { C.unhideWindow(window) - fallthrough - default: - w.config.Mode = Fullscreen + } + if !fullscreen { C.toggleFullScreen(window) } case Minimized: - switch prev.Mode { - case Minimized, Fullscreen: - default: - w.config.Mode = Minimized - C.hideWindow(window) - } + C.hideWindow(window) case Maximized: - switch prev.Mode { - case Fullscreen: - case Minimized: + if C.isWindowMiniaturized(window) != 0 { C.unhideWindow(window) - fallthrough - default: - w.config.Mode = Maximized - w.setTitle(prev, cnf) - if C.isWindowZoomed(window) == 0 { - C.zoomWindow(window) - } + } + if fullscreen { + C.toggleFullScreen(window) + } + w.setTitle(cnf.Title) + if C.isWindowZoomed(window) == 0 { + C.zoomWindow(window) } case Windowed: - switch prev.Mode { - case Fullscreen: - C.toggleFullScreen(window) - case Minimized: + if C.isWindowMiniaturized(window) != 0 { C.unhideWindow(window) - case Maximized: - if C.isWindowZoomed(window) != 0 { - C.zoomWindow(window) - } } - w.config.Mode = Windowed - w.setTitle(prev, cnf) - if prev.Size != cnf.Size { - w.config.Size = cnf.Size - cnf.Size = cnf.Size.Div(int(screenScale)) - C.setSize(window, C.CGFloat(cnf.Size.X), C.CGFloat(cnf.Size.Y)) + if fullscreen { + C.toggleFullScreen(window) } - if prev.MinSize != cnf.MinSize { - w.config.MinSize = cnf.MinSize - cnf.MinSize = cnf.MinSize.Div(int(screenScale)) - C.setMinSize(window, C.CGFloat(cnf.MinSize.X), C.CGFloat(cnf.MinSize.Y)) - } - if prev.MaxSize != cnf.MaxSize { - w.config.MaxSize = cnf.MaxSize - cnf.MaxSize = cnf.MaxSize.Div(int(screenScale)) + w.setTitle(cnf.Title) + w.config.Size = cnf.Size + cnf.Size = cnf.Size.Div(int(screenScale)) + C.setSize(window, C.CGFloat(cnf.Size.X), C.CGFloat(cnf.Size.Y)) + w.config.MinSize = cnf.MinSize + cnf.MinSize = cnf.MinSize.Div(int(screenScale)) + C.setMinSize(window, C.CGFloat(cnf.MinSize.X), C.CGFloat(cnf.MinSize.Y)) + w.config.MaxSize = cnf.MaxSize + cnf.MaxSize = cnf.MaxSize.Div(int(screenScale)) + if cnf.MaxSize != (image.Point{}) { C.setMaxSize(window, C.CGFloat(cnf.MaxSize.X), C.CGFloat(cnf.MaxSize.Y)) } - } - if cnf.Decorated != prev.Decorated { - w.config.Decorated = cnf.Decorated - mask := C.getWindowStyleMask(window) - style := C.NSWindowStyleMask(C.NSWindowStyleMaskTitled | C.NSWindowStyleMaskResizable | C.NSWindowStyleMaskMiniaturizable | C.NSWindowStyleMaskClosable) - style = C.NSWindowStyleMaskFullSizeContentView - mask &^= style - barTrans := C.int(C.NO) - titleVis := C.NSWindowTitleVisibility(C.NSWindowTitleVisible) - if !cnf.Decorated { - mask |= style - barTrans = C.YES - titleVis = C.NSWindowTitleHidden + if C.isWindowZoomed(window) != 0 { + C.zoomWindow(window) } - C.setWindowTitlebarAppearsTransparent(window, barTrans) - C.setWindowTitleVisibility(window, titleVis) - C.setWindowStyleMask(window, mask) - C.setWindowStandardButtonHidden(window, C.NSWindowCloseButton, barTrans) - C.setWindowStandardButtonHidden(window, C.NSWindowMiniaturizeButton, barTrans) - C.setWindowStandardButtonHidden(window, C.NSWindowZoomButton, barTrans) - // When toggling the titlebar, the layer doesn't update its frame - // until the next resize. Force it. - C.resetLayerFrame(w.view) } - w.ProcessEvent(ConfigEvent{Config: w.config}) + style := C.NSWindowStyleMask(C.NSWindowStyleMaskTitled | C.NSWindowStyleMaskResizable | C.NSWindowStyleMaskMiniaturizable | C.NSWindowStyleMaskClosable) + style = C.NSWindowStyleMaskFullSizeContentView + mask &^= style + barTrans := C.int(C.NO) + titleVis := C.NSWindowTitleVisibility(C.NSWindowTitleVisible) + if !cnf.Decorated { + mask |= style + barTrans = C.YES + titleVis = C.NSWindowTitleHidden + } + C.setWindowTitlebarAppearsTransparent(window, barTrans) + C.setWindowTitleVisibility(window, titleVis) + C.setWindowStyleMask(window, mask) + C.setWindowStandardButtonHidden(window, C.NSWindowCloseButton, barTrans) + C.setWindowStandardButtonHidden(window, C.NSWindowMiniaturizeButton, barTrans) + C.setWindowStandardButtonHidden(window, C.NSWindowZoomButton, barTrans) + // When toggling the titlebar, the layer doesn't update its frame + // until the next resize. Force it. + C.resetLayerFrame(w.view) } -func (w *window) setTitle(prev, cnf Config) { - if prev.Title != cnf.Title { - w.config.Title = cnf.Title - title := stringToNSString(cnf.Title) - defer C.CFRelease(title) - C.setTitle(C.windowForView(w.view), title) - } +func (w *window) setTitle(title string) { + w.config.Title = title + titleC := stringToNSString(title) + defer C.CFRelease(titleC) + C.setTitle(C.windowForView(w.view), titleC) } func (w *window) Perform(acts system.Action) { @@ -520,7 +523,8 @@ func (w *window) SetInputHint(_ key.InputHint) {} func (w *window) SetAnimating(anim bool) { w.anim = anim - if w.anim && w.visible { + window := C.windowForView(w.view) + if w.anim && window != 0 { w.displayLink.Start() } else { w.displayLink.Stop() @@ -799,24 +803,19 @@ func gio_firstRectForCharacterRange(h C.uintptr_t, crng C.NSRange, actual C.NSRa } func (w *window) draw() { + cnf := w.config + w.updateWindowMode() + if w.config != cnf { + w.ProcessEvent(ConfigEvent{Config: w.config}) + } select { case <-w.redraw: default: } - w.visible = true if w.anim { w.SetAnimating(w.anim) } - w.scale = float32(C.getViewBackingScale(w.view)) - wf, hf := float32(C.viewWidth(w.view)), float32(C.viewHeight(w.view)) - sz := image.Point{ - X: int(wf*w.scale + .5), - Y: int(hf*w.scale + .5), - } - if sz != w.config.Size { - w.config.Size = sz - w.ProcessEvent(ConfigEvent{Config: w.config}) - } + sz := w.config.Size if sz.X == 0 || sz.Y == 0 { return } @@ -824,7 +823,7 @@ func (w *window) draw() { w.ProcessEvent(frameEvent{ FrameEvent: FrameEvent{ Now: time.Now(), - Size: w.config.Size, + Size: sz, Metric: cfg, }, Sync: true, @@ -867,7 +866,6 @@ func gio_onAttached(h C.uintptr_t, attached C.int) { w.ProcessEvent(AppKitViewEvent{View: uintptr(w.view), Layer: uintptr(layer)}) } else { w.ProcessEvent(AppKitViewEvent{}) - w.visible = false w.SetAnimating(w.anim) } } @@ -882,33 +880,6 @@ func gio_onDestroy(h C.uintptr_t) { w.view = 0 } -//export gio_onHide -func gio_onHide(h C.uintptr_t) { - w := windowFor(h) - w.visible = false - w.SetAnimating(w.anim) -} - -//export gio_onShow -func gio_onShow(h C.uintptr_t) { - w := windowFor(h) - w.draw() -} - -//export gio_onFullscreen -func gio_onFullscreen(h C.uintptr_t) { - w := windowFor(h) - w.config.Mode = Fullscreen - w.ProcessEvent(ConfigEvent{Config: w.config}) -} - -//export gio_onWindowed -func gio_onWindowed(h C.uintptr_t) { - w := windowFor(h) - w.config.Mode = Windowed - w.ProcessEvent(ConfigEvent{Config: w.config}) -} - //export gio_onFinishLaunching func gio_onFinishLaunching() { close(launched) @@ -931,10 +902,9 @@ func newWindow(win *callbacks, options []Option) { w.ProcessEvent(DestroyEvent{Err: err}) return } - window := C.gio_createWindow(w.view, 0, 0, 0, 0, 0, 0) + window := C.gio_createWindow(w.view, C.CGFloat(cnf.Size.X), C.CGFloat(cnf.Size.Y), 0, 0, 0, 0) // Release our reference now that the NSWindow has it. C.CFRelease(w.view) - w.updateWindowMode() w.Configure(options) if nextTopLeft.x == 0 && nextTopLeft.y == 0 { // cascadeTopLeftFromPoint treats (0, 0) as a no-op, @@ -942,6 +912,7 @@ func newWindow(win *callbacks, options []Option) { nextTopLeft = C.cascadeTopLeftFromPoint(window, nextTopLeft) } nextTopLeft = C.cascadeTopLeftFromPoint(window, nextTopLeft) + C.makeFirstResponder(window, w.view) // makeKeyAndOrderFront assumes ownership of our window reference. C.makeKeyAndOrderFront(window) }) @@ -966,9 +937,7 @@ func (w *window) init(customRenderer bool) error { return } w.runOnMain(func() { - if w.visible { - C.setNeedsDisplay(w.view) - } + C.setNeedsDisplay(w.view) }) }) w.displayLink = dl diff --git a/app/os_macos.m b/app/os_macos.m index b1940321..9c895148 100644 --- a/app/os_macos.m +++ b/app/os_macos.m @@ -23,22 +23,22 @@ __attribute__ ((visibility ("hidden"))) CALayer *gio_layerFactory(BOOL presentWi - (void)windowWillMiniaturize:(NSNotification *)notification { NSWindow *window = (NSWindow *)[notification object]; GioView *view = (GioView *)window.contentView; - gio_onHide(view.handle); + gio_onDraw(view.handle); } - (void)windowDidDeminiaturize:(NSNotification *)notification { NSWindow *window = (NSWindow *)[notification object]; GioView *view = (GioView *)window.contentView; - gio_onShow(view.handle); + gio_onDraw(view.handle); } - (void)windowWillEnterFullScreen:(NSNotification *)notification { NSWindow *window = (NSWindow *)[notification object]; GioView *view = (GioView *)window.contentView; - gio_onFullscreen(view.handle); + gio_onDraw(view.handle); } - (void)windowWillExitFullScreen:(NSNotification *)notification { NSWindow *window = (NSWindow *)[notification object]; GioView *view = (GioView *)window.contentView; - gio_onWindowed(view.handle); + gio_onDraw(view.handle); } - (void)windowDidChangeScreen:(NSNotification *)notification { NSWindow *window = (NSWindow *)[notification object]; @@ -202,10 +202,10 @@ static void handleMouse(GioView *view, NSEvent *event, int typ, CGFloat dx, CGFl return [[self window] convertRectToScreen:r]; } - (void)applicationWillUnhide:(NSNotification *)notification { - gio_onShow(self.handle); + gio_onDraw(self.handle); } - (void)applicationDidHide:(NSNotification *)notification { - gio_onHide(self.handle); + gio_onDraw(self.handle); } - (void)dealloc { gio_onDestroy(self.handle); @@ -387,7 +387,6 @@ CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height, CGF [window setAcceptsMouseMovedEvents:YES]; NSView *view = (__bridge NSView *)viewRef; [window setContentView:view]; - [window makeFirstResponder:view]; window.delegate = globalWindowDel; return (__bridge_retained CFTypeRef)window; }