mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
app: [macOS] track window state changes initiated by the operating system
Before this change, the window state was explicitly updated whenever Window.Option was called. However, the system may also change window state as a result of user gestures, but those changes did not result in ConfigEvents reflecting them. Remove the explicit state updates and track them when the system tells us it has changed. This is a step towards fixing #600 which require accurate window state tracking. References: https://todo.sr.ht/~eliasnaur/gio/600 Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+95
-126
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user