Skip to content

Commit a699f77

Browse files
pierreceliasnaur
authored andcommitted
app: add Maximize and Center methods support for macOS and X11
Commit 9835cd5 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 <[email protected]>
1 parent f4c82ad commit a699f77

File tree

3 files changed

+90
-34
lines changed

3 files changed

+90
-34
lines changed

app/os_macos.go

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,17 @@ static void setMaxSize(CFTypeRef windowRef, CGFloat width, CGFloat height) {
118118
window.contentMaxSize = NSMakeSize(width, height);
119119
}
120120
121+
static void setScreenFrame(CFTypeRef windowRef, CGFloat x, CGFloat y, CGFloat w, CGFloat h) {
122+
NSWindow* window = (__bridge NSWindow *)windowRef;
123+
NSRect r = NSMakeRect(x, y, w, h);
124+
[window setFrame:r display:YES];
125+
}
126+
127+
static NSRect getScreenFrame(CFTypeRef windowRef) {
128+
NSWindow* window = (__bridge NSWindow *)windowRef;
129+
return [[window screen] frame];
130+
}
131+
121132
static void setTitle(CFTypeRef windowRef, const char *title) {
122133
NSWindow* window = (__bridge NSWindow *)windowRef;
123134
window.title = [NSString stringWithUTF8String: title];
@@ -291,11 +302,23 @@ func (w *window) Close() {
291302
C.closeWindow(w.window)
292303
}
293304

294-
// Maximize the window. Not implemented for macos.
295-
func (w *window) Maximize() {}
305+
// Maximize the window.
306+
func (w *window) Maximize() {
307+
r := C.getScreenFrame(w.window) // the screen size of the window
308+
C.setScreenFrame(w.window, C.CGFloat(0), C.CGFloat(0), r.size.width, r.size.height)
309+
}
310+
311+
// Center the window.
312+
func (w *window) Center() {
313+
r := C.getScreenFrame(w.window) // the screen size of the window
296314

297-
// Center the window. Not implemented for macos.
298-
func (w *window) Center() {}
315+
screenScale := float32(C.getScreenBackingScale())
316+
sz := w.config.Size.Div(int(screenScale))
317+
x := (int(r.size.width) - sz.X) / 2
318+
y := (int(r.size.height) - sz.Y) / 2
319+
320+
C.setScreenFrame(w.window, C.CGFloat(x), C.CGFloat(y), C.CGFloat(sz.X), C.CGFloat(sz.Y))
321+
}
299322

300323
func (w *window) setStage(stage system.Stage) {
301324
if stage == w.stage {

app/os_x11.go

Lines changed: 61 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ import (
4949
"gioui.org/app/internal/xkb"
5050
)
5151

52+
const (
53+
_NET_WM_STATE_REMOVE = 0
54+
_NET_WM_STATE_ADD = 1
55+
)
56+
5257
type x11Window struct {
5358
w *callbacks
5459
x *C.Display
@@ -81,6 +86,10 @@ type x11Window struct {
8186
wmStateFullscreen C.Atom
8287
// "_NET_ACTIVE_WINDOW"
8388
wmActiveWindow C.Atom
89+
// _NET_WM_STATE_MAXIMIZED_HORZ
90+
wmStateMaximizedHorz C.Atom
91+
// _NET_WM_STATE_MAXIMIZED_VERT
92+
wmStateMaximizedVert C.Atom
8493
}
8594
stage system.Stage
8695
metric unit.Metric
@@ -241,37 +250,16 @@ func (w *x11Window) SetWindowMode(mode WindowMode) {
241250
var action C.long
242251
switch mode {
243252
case Windowed:
244-
action = 0 // _NET_WM_STATE_REMOVE
253+
action = _NET_WM_STATE_REMOVE
245254
case Fullscreen:
246-
action = 1 // _NET_WM_STATE_ADD
255+
action = _NET_WM_STATE_ADD
247256
default:
248257
return
249258
}
250259
w.config.Mode = mode
251260
// "A Client wishing to change the state of a window MUST send
252261
// a _NET_WM_STATE client message to the root window."
253-
var xev C.XEvent
254-
ev := (*C.XClientMessageEvent)(unsafe.Pointer(&xev))
255-
*ev = C.XClientMessageEvent{
256-
_type: C.ClientMessage,
257-
display: w.x,
258-
window: w.xw,
259-
message_type: w.atoms.wmState,
260-
format: 32,
261-
}
262-
arr := (*[5]C.long)(unsafe.Pointer(&ev.data))
263-
arr[0] = action
264-
arr[1] = C.long(w.atoms.wmStateFullscreen)
265-
arr[2] = 0
266-
arr[3] = 1 // application
267-
arr[4] = 0
268-
C.XSendEvent(
269-
w.x,
270-
C.XDefaultRootWindow(w.x), // MUST be the root window
271-
C.False,
272-
C.SubstructureNotifyMask|C.SubstructureRedirectMask,
273-
&xev,
274-
)
262+
w.sendWMStateEvent(action, w.atoms.wmStateFullscreen, 0)
275263
}
276264

277265
func (w *x11Window) ShowTextInput(show bool) {}
@@ -295,11 +283,54 @@ func (w *x11Window) Close() {
295283
C.XSendEvent(w.x, w.xw, C.False, C.NoEventMask, &xev)
296284
}
297285

298-
// Maximize the window. Not implemented for x11.
299-
func (w *x11Window) Maximize() {}
286+
// Maximize the window.
287+
func (w *x11Window) Maximize() {
288+
w.sendWMStateEvent(_NET_WM_STATE_ADD, w.atoms.wmStateMaximizedHorz, w.atoms.wmStateMaximizedVert)
289+
}
290+
291+
// Center the window.
292+
func (w *x11Window) Center() {
293+
screen := C.XDefaultScreen(w.x)
294+
width := C.XDisplayWidth(w.x, screen)
295+
height := C.XDisplayHeight(w.x, screen)
296+
297+
var attrs C.XWindowAttributes
298+
C.XGetWindowAttributes(w.x, w.xw, &attrs)
299+
width -= attrs.border_width
300+
height -= attrs.border_width
300301

301-
// Center the window. Not implemented for x11.
302-
func (w *x11Window) Center() {}
302+
sz := w.config.Size
303+
x := (int(width) - sz.X) / 2
304+
y := (int(height) - sz.Y) / 2
305+
306+
C.XMoveResizeWindow(w.x, w.xw, C.int(x), C.int(y), C.uint(sz.X), C.uint(sz.Y))
307+
}
308+
309+
// action is one of _NET_WM_STATE_REMOVE, _NET_WM_STATE_ADD.
310+
func (w *x11Window) sendWMStateEvent(action C.long, atom1, atom2 C.ulong) {
311+
var xev C.XEvent
312+
ev := (*C.XClientMessageEvent)(unsafe.Pointer(&xev))
313+
*ev = C.XClientMessageEvent{
314+
_type: C.ClientMessage,
315+
display: w.x,
316+
window: w.xw,
317+
message_type: w.atoms.wmState,
318+
format: 32,
319+
}
320+
data := (*[5]C.long)(unsafe.Pointer(&ev.data))
321+
data[0] = C.long(action)
322+
data[1] = C.long(atom1)
323+
data[2] = C.long(atom2)
324+
data[3] = 1 // application
325+
326+
C.XSendEvent(
327+
w.x,
328+
C.XDefaultRootWindow(w.x), // MUST be the root window
329+
C.False,
330+
C.SubstructureNotifyMask|C.SubstructureRedirectMask,
331+
&xev,
332+
)
333+
}
303334

304335
var x11OneByte = make([]byte, 1)
305336

@@ -735,6 +766,8 @@ func newX11Window(gioWin *callbacks, options []Option) error {
735766
w.atoms.wmState = w.atom("_NET_WM_STATE", false)
736767
w.atoms.wmStateFullscreen = w.atom("_NET_WM_STATE_FULLSCREEN", false)
737768
w.atoms.wmActiveWindow = w.atom("_NET_ACTIVE_WINDOW", false)
769+
w.atoms.wmStateMaximizedHorz = w.atom("_NET_WM_STATE_MAXIMIZED_HORZ", false)
770+
w.atoms.wmStateMaximizedVert = w.atom("_NET_WM_STATE_MAXIMIZED_VERT", false)
738771

739772
// extensions
740773
C.XSetWMProtocols(dpy, win, &w.atoms.evDelWindow, 1)

app/window.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,15 +312,15 @@ func (w *Window) Close() {
312312
}
313313

314314
// Maximize the window.
315-
// Note: only implemented on Windows.
315+
// Note: only implemented on Windows, macOS and X11.
316316
func (w *Window) Maximize() {
317317
w.driverDefer(func(d driver) {
318318
d.Maximize()
319319
})
320320
}
321321

322322
// Center the window.
323-
// Note: only implemented on Windows.
323+
// Note: only implemented on Windows, macOS and X11.
324324
func (w *Window) Center() {
325325
w.driverDefer(func(d driver) {
326326
d.Center()

0 commit comments

Comments
 (0)