From a699f771c6e60909ef925fc5923bb1cc1d919cab Mon Sep 17 00:00:00 2001 From: Pierre Curto Date: Sat, 13 Nov 2021 18:23:30 +0100 Subject: [PATCH] app: add Maximize and Center methods support for macOS and X11 Commit 9835cd59 added support for the Window.Maximize and Window.Center methods for Windows only. This patch also adds support for macOS and X11. Signed-off-by: Pierre Curto --- app/os_macos.go | 31 ++++++++++++++--- app/os_x11.go | 89 +++++++++++++++++++++++++++++++++---------------- app/window.go | 4 +-- 3 files changed, 90 insertions(+), 34 deletions(-) diff --git a/app/os_macos.go b/app/os_macos.go index de4e0b34..be511998 100644 --- a/app/os_macos.go +++ b/app/os_macos.go @@ -118,6 +118,17 @@ static void setMaxSize(CFTypeRef windowRef, CGFloat width, CGFloat height) { window.contentMaxSize = NSMakeSize(width, height); } +static void setScreenFrame(CFTypeRef windowRef, CGFloat x, CGFloat y, CGFloat w, CGFloat h) { + NSWindow* window = (__bridge NSWindow *)windowRef; + NSRect r = NSMakeRect(x, y, w, h); + [window setFrame:r display:YES]; +} + +static NSRect getScreenFrame(CFTypeRef windowRef) { + NSWindow* window = (__bridge NSWindow *)windowRef; + return [[window screen] frame]; +} + static void setTitle(CFTypeRef windowRef, const char *title) { NSWindow* window = (__bridge NSWindow *)windowRef; window.title = [NSString stringWithUTF8String: title]; @@ -291,11 +302,23 @@ func (w *window) Close() { C.closeWindow(w.window) } -// Maximize the window. Not implemented for macos. -func (w *window) Maximize() {} +// Maximize the window. +func (w *window) Maximize() { + r := C.getScreenFrame(w.window) // the screen size of the window + C.setScreenFrame(w.window, C.CGFloat(0), C.CGFloat(0), r.size.width, r.size.height) +} -// Center the window. Not implemented for macos. -func (w *window) Center() {} +// Center the window. +func (w *window) Center() { + r := C.getScreenFrame(w.window) // the screen size of the window + + screenScale := float32(C.getScreenBackingScale()) + sz := w.config.Size.Div(int(screenScale)) + x := (int(r.size.width) - sz.X) / 2 + y := (int(r.size.height) - sz.Y) / 2 + + C.setScreenFrame(w.window, C.CGFloat(x), C.CGFloat(y), C.CGFloat(sz.X), C.CGFloat(sz.Y)) +} func (w *window) setStage(stage system.Stage) { if stage == w.stage { diff --git a/app/os_x11.go b/app/os_x11.go index 6687c674..79a063d5 100644 --- a/app/os_x11.go +++ b/app/os_x11.go @@ -49,6 +49,11 @@ import ( "gioui.org/app/internal/xkb" ) +const ( + _NET_WM_STATE_REMOVE = 0 + _NET_WM_STATE_ADD = 1 +) + type x11Window struct { w *callbacks x *C.Display @@ -81,6 +86,10 @@ type x11Window struct { wmStateFullscreen C.Atom // "_NET_ACTIVE_WINDOW" wmActiveWindow C.Atom + // _NET_WM_STATE_MAXIMIZED_HORZ + wmStateMaximizedHorz C.Atom + // _NET_WM_STATE_MAXIMIZED_VERT + wmStateMaximizedVert C.Atom } stage system.Stage metric unit.Metric @@ -241,37 +250,16 @@ func (w *x11Window) SetWindowMode(mode WindowMode) { var action C.long switch mode { case Windowed: - action = 0 // _NET_WM_STATE_REMOVE + action = _NET_WM_STATE_REMOVE case Fullscreen: - action = 1 // _NET_WM_STATE_ADD + action = _NET_WM_STATE_ADD default: return } w.config.Mode = mode // "A Client wishing to change the state of a window MUST send // a _NET_WM_STATE client message to the root window." - var xev C.XEvent - ev := (*C.XClientMessageEvent)(unsafe.Pointer(&xev)) - *ev = C.XClientMessageEvent{ - _type: C.ClientMessage, - display: w.x, - window: w.xw, - message_type: w.atoms.wmState, - format: 32, - } - arr := (*[5]C.long)(unsafe.Pointer(&ev.data)) - arr[0] = action - arr[1] = C.long(w.atoms.wmStateFullscreen) - arr[2] = 0 - arr[3] = 1 // application - arr[4] = 0 - C.XSendEvent( - w.x, - C.XDefaultRootWindow(w.x), // MUST be the root window - C.False, - C.SubstructureNotifyMask|C.SubstructureRedirectMask, - &xev, - ) + w.sendWMStateEvent(action, w.atoms.wmStateFullscreen, 0) } func (w *x11Window) ShowTextInput(show bool) {} @@ -295,11 +283,54 @@ func (w *x11Window) Close() { C.XSendEvent(w.x, w.xw, C.False, C.NoEventMask, &xev) } -// Maximize the window. Not implemented for x11. -func (w *x11Window) Maximize() {} +// Maximize the window. +func (w *x11Window) Maximize() { + w.sendWMStateEvent(_NET_WM_STATE_ADD, w.atoms.wmStateMaximizedHorz, w.atoms.wmStateMaximizedVert) +} -// Center the window. Not implemented for x11. -func (w *x11Window) Center() {} +// Center the window. +func (w *x11Window) Center() { + screen := C.XDefaultScreen(w.x) + width := C.XDisplayWidth(w.x, screen) + height := C.XDisplayHeight(w.x, screen) + + var attrs C.XWindowAttributes + C.XGetWindowAttributes(w.x, w.xw, &attrs) + width -= attrs.border_width + height -= attrs.border_width + + sz := w.config.Size + x := (int(width) - sz.X) / 2 + y := (int(height) - sz.Y) / 2 + + C.XMoveResizeWindow(w.x, w.xw, C.int(x), C.int(y), C.uint(sz.X), C.uint(sz.Y)) +} + +// action is one of _NET_WM_STATE_REMOVE, _NET_WM_STATE_ADD. +func (w *x11Window) sendWMStateEvent(action C.long, atom1, atom2 C.ulong) { + var xev C.XEvent + ev := (*C.XClientMessageEvent)(unsafe.Pointer(&xev)) + *ev = C.XClientMessageEvent{ + _type: C.ClientMessage, + display: w.x, + window: w.xw, + message_type: w.atoms.wmState, + format: 32, + } + data := (*[5]C.long)(unsafe.Pointer(&ev.data)) + data[0] = C.long(action) + data[1] = C.long(atom1) + data[2] = C.long(atom2) + data[3] = 1 // application + + C.XSendEvent( + w.x, + C.XDefaultRootWindow(w.x), // MUST be the root window + C.False, + C.SubstructureNotifyMask|C.SubstructureRedirectMask, + &xev, + ) +} var x11OneByte = make([]byte, 1) @@ -735,6 +766,8 @@ func newX11Window(gioWin *callbacks, options []Option) error { w.atoms.wmState = w.atom("_NET_WM_STATE", false) w.atoms.wmStateFullscreen = w.atom("_NET_WM_STATE_FULLSCREEN", false) w.atoms.wmActiveWindow = w.atom("_NET_ACTIVE_WINDOW", false) + w.atoms.wmStateMaximizedHorz = w.atom("_NET_WM_STATE_MAXIMIZED_HORZ", false) + w.atoms.wmStateMaximizedVert = w.atom("_NET_WM_STATE_MAXIMIZED_VERT", false) // extensions C.XSetWMProtocols(dpy, win, &w.atoms.evDelWindow, 1) diff --git a/app/window.go b/app/window.go index 58e24dc2..18b3a0ad 100644 --- a/app/window.go +++ b/app/window.go @@ -312,7 +312,7 @@ func (w *Window) Close() { } // Maximize the window. -// Note: only implemented on Windows. +// Note: only implemented on Windows, macOS and X11. func (w *Window) Maximize() { w.driverDefer(func(d driver) { d.Maximize() @@ -320,7 +320,7 @@ func (w *Window) Maximize() { } // Center the window. -// Note: only implemented on Windows. +// Note: only implemented on Windows, macOS and X11. func (w *Window) Center() { w.driverDefer(func(d driver) { d.Center()