app: update Window for macOS, X11 and Wayland

Commit #c4f98d3c1eab201419be255fafb139f7e10ad273 added
the Minimized and Maximized options for the Windows platform.
This change adds those for the remaining desktop platforms.

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
This commit is contained in:
Pierre Curto
2022-01-17 20:59:39 +01:00
committed by Elias Naur
parent 2ce9ad36af
commit b15a9cb595
5 changed files with 230 additions and 116 deletions
+76 -38
View File
@@ -129,6 +129,16 @@ static void setScreenFrame(CFTypeRef windowRef, CGFloat x, CGFloat y, CGFloat w,
[window setFrame:r display:YES];
}
static void hideWindow(CFTypeRef windowRef) {
NSWindow* window = (__bridge NSWindow *)windowRef;
[window miniaturize:window];
}
static void unhideWindow(CFTypeRef windowRef) {
NSWindow* window = (__bridge NSWindow *)windowRef;
[window deminiaturize:window];
}
static NSRect getScreenFrame(CFTypeRef windowRef) {
NSWindow* window = (__bridge NSWindow *)windowRef;
return [[window screen] frame];
@@ -252,35 +262,81 @@ func (w *window) Configure(options []Option) {
cnf.MinSize = cnf.MinSize.Div(int(screenScale))
cnf.MaxSize = cnf.MaxSize.Div(int(screenScale))
if cnf.Mode != Fullscreen && prev.Size != cnf.Size {
w.config.Size = cnf.Size
C.setSize(w.window, C.CGFloat(cnf.Size.X), C.CGFloat(cnf.Size.Y))
switch cnf.Mode {
case Fullscreen:
switch prev.Mode {
case Fullscreen:
case Minimized:
C.unhideWindow(w.window)
fallthrough
default:
w.config.Mode = Fullscreen
C.toggleFullScreen(w.window)
}
case Minimized:
switch prev.Mode {
case Minimized, Fullscreen:
default:
w.config.Mode = Minimized
C.hideWindow(w.window)
}
case Maximized:
switch prev.Mode {
case Fullscreen:
case Minimized:
C.unhideWindow(w.window)
fallthrough
default:
w.config.Mode = Maximized
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)
w.config.Size = image.Pt(int(r.size.width), int(r.size.height))
w.setTitle(prev, cnf)
}
case Windowed:
switch prev.Mode {
case Fullscreen:
w.config.Mode = Windowed
C.toggleFullScreen(w.window)
case Minimized:
w.config.Mode = Windowed
C.unhideWindow(w.window)
case Maximized:
w.config.Mode = Windowed
}
w.setTitle(prev, cnf)
if prev.Size != cnf.Size {
w.config.Size = cnf.Size
C.setSize(w.window, C.CGFloat(cnf.Size.X), C.CGFloat(cnf.Size.Y))
}
if prev.MinSize != cnf.MinSize {
w.config.MinSize = cnf.MinSize
C.setMinSize(w.window, C.CGFloat(cnf.MinSize.X), C.CGFloat(cnf.MinSize.Y))
}
if prev.MaxSize != cnf.MaxSize {
w.config.MaxSize = cnf.MaxSize
C.setMaxSize(w.window, C.CGFloat(cnf.MaxSize.X), C.CGFloat(cnf.MaxSize.Y))
}
if cnf.center {
r := C.getScreenFrame(w.window) // the screen size of the window
sz := w.config.Size
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))
}
}
if prev.MinSize != cnf.MinSize {
w.config.MinSize = cnf.MinSize
C.setMinSize(w.window, C.CGFloat(cnf.MinSize.X), C.CGFloat(cnf.MinSize.Y))
}
if prev.MaxSize != cnf.MaxSize {
w.config.MaxSize = cnf.MaxSize
C.setMaxSize(w.window, C.CGFloat(cnf.MaxSize.X), C.CGFloat(cnf.MaxSize.Y))
if w.config != prev {
w.w.Event(ConfigEvent{Config: w.config})
}
}
func (w *window) setTitle(prev, cnf Config) {
if prev.Title != cnf.Title {
w.config.Title = cnf.Title
title := C.CString(cnf.Title)
defer C.free(unsafe.Pointer(title))
C.setTitle(w.window, title)
}
if prev.Mode != cnf.Mode {
switch cnf.Mode {
case Windowed, Fullscreen:
w.config.Mode = cnf.Mode
C.toggleFullScreen(w.window)
}
}
if w.config != prev {
w.w.Event(ConfigEvent{Config: w.config})
}
}
func (w *window) SetCursor(name pointer.CursorName) {
@@ -317,24 +373,6 @@ func (w *window) Close() {
C.closeWindow(w.window)
}
// 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.
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 {
return
+66 -10
View File
@@ -190,6 +190,7 @@ type window struct {
// size is the unscaled window size (unlike config.Size which is scaled).
size image.Point
config Config
wsize image.Point // window config size before going fullscreen
wakeups chan struct{}
}
@@ -919,22 +920,77 @@ func (w *window) Configure(options []Option) {
prev := w.config
cnf := w.config
cnf.apply(cfg, options)
if prev.Size != cnf.Size {
w.size = image.Pt(cnf.Size.X/w.scale, cnf.Size.Y/w.scale)
w.config.Size = cnf.Size
}
if prev.Title != cnf.Title {
w.config.Title = cnf.Title
title := C.CString(cnf.Title)
C.xdg_toplevel_set_title(w.topLvl, title)
C.free(unsafe.Pointer(title))
switch cnf.Mode {
case Fullscreen:
switch prev.Mode {
case Minimized, Fullscreen:
default:
w.config.Mode = Fullscreen
w.wsize = w.config.Size
C.xdg_toplevel_set_fullscreen(w.topLvl, nil)
}
case Minimized:
switch prev.Mode {
case Minimized, Fullscreen:
default:
w.config.Mode = Minimized
C.xdg_toplevel_set_minimized(w.topLvl)
}
case Maximized:
switch prev.Mode {
case Minimized, Fullscreen:
default:
w.config.Mode = Maximized
w.wsize = w.config.Size
C.xdg_toplevel_set_maximized(w.topLvl)
}
case Windowed:
switch prev.Mode {
case Fullscreen:
w.config.Mode = Windowed
w.size = w.wsize.Div(w.scale)
C.xdg_toplevel_unset_fullscreen(w.topLvl)
case Minimized:
w.config.Mode = Windowed
w.config.Size = w.wsize
case Maximized:
w.config.Mode = Windowed
w.size = w.wsize.Div(w.scale)
C.xdg_toplevel_unset_maximized(w.topLvl)
}
w.setTitle(prev, cnf)
if prev.Size != cnf.Size {
w.config.Size = cnf.Size
w.size = cnf.Size.Div(w.scale)
}
if prev.MinSize != cnf.MinSize {
w.config.MinSize = cnf.MinSize
C.xdg_toplevel_set_min_size(w.topLvl, C.int32_t(cnf.MinSize.X), C.int32_t(cnf.MinSize.Y))
}
if prev.MaxSize != cnf.MaxSize {
w.config.MaxSize = cnf.MaxSize
C.xdg_toplevel_set_max_size(w.topLvl, C.int32_t(cnf.MaxSize.X), C.int32_t(cnf.MaxSize.Y))
}
}
if w.config != prev {
w.w.Event(ConfigEvent{Config: w.config})
}
}
func (w *window) Raise() {}
func (w *window) setTitle(prev, cnf Config) {
if prev.Title != cnf.Title {
w.config.Title = cnf.Title
title := C.CString(cnf.Title)
C.xdg_toplevel_set_title(w.topLvl, title)
C.free(unsafe.Pointer(title))
}
}
func (w *window) Raise() {
// NB. there is no way for a minimized window to be unminimized.
// https://wayland.app/protocols/xdg-shell#xdg_toplevel:request:set_minimized
}
func (w *window) SetCursor(name pointer.CursorName) {
ptr := w.disp.seat.pointer
+1 -1
View File
@@ -571,7 +571,7 @@ func (w *window) Configure(options []Option) {
}
// A config event is sent to the main event loop whenever the configuration is changed
if oldConfig.Mode != w.config.Mode || oldConfig.Size != w.config.Size {
if oldConfig != w.config {
w.w.Event(ConfigEvent{Config: w.config})
}
}
+85 -65
View File
@@ -160,27 +160,93 @@ func (w *x11Window) Configure(options []Option) {
prev := w.config
cnf := w.config
cnf.apply(w.metric, options)
if prev.MinSize != cnf.MinSize {
w.config.MinSize = cnf.MinSize
shints.min_width = C.int(cnf.MinSize.X)
shints.min_height = C.int(cnf.MinSize.Y)
shints.flags = C.PMinSize
}
if prev.MaxSize != cnf.MaxSize {
w.config.MaxSize = cnf.MaxSize
shints.max_width = C.int(cnf.MaxSize.X)
shints.max_height = C.int(cnf.MaxSize.Y)
shints.flags = shints.flags | C.PMaxSize
}
if shints.flags != 0 {
C.XSetWMNormalHints(w.x, w.xw, &shints)
}
if cnf.Mode != Fullscreen && prev.Size != cnf.Size {
w.config.Size = cnf.Size
C.XResizeWindow(w.x, w.xw, C.uint(cnf.Size.X), C.uint(cnf.Size.Y))
}
switch cnf.Mode {
case Fullscreen:
switch prev.Mode {
case Fullscreen:
case Minimized:
w.Raise()
fallthrough
default:
w.config.Mode = Fullscreen
w.sendWMStateEvent(_NET_WM_STATE_ADD, w.atoms.wmStateFullscreen, 0)
}
case Minimized:
switch prev.Mode {
case Minimized, Fullscreen:
default:
w.config.Mode = Minimized
screen := C.XDefaultScreen(w.x)
C.XIconifyWindow(w.x, w.xw, screen)
}
case Maximized:
switch prev.Mode {
case Fullscreen:
case Minimized:
w.Raise()
fallthrough
default:
w.config.Mode = Maximized
w.sendWMStateEvent(_NET_WM_STATE_ADD, w.atoms.wmStateMaximizedHorz, w.atoms.wmStateMaximizedVert)
w.setTitle(prev, cnf)
}
case Windowed:
switch prev.Mode {
case Fullscreen:
w.config.Mode = Windowed
w.sendWMStateEvent(_NET_WM_STATE_REMOVE, w.atoms.wmStateFullscreen, 0)
C.XResizeWindow(w.x, w.xw, C.uint(cnf.Size.X), C.uint(cnf.Size.Y))
case Minimized:
w.config.Mode = Windowed
w.Raise()
case Maximized:
w.config.Mode = Windowed
w.sendWMStateEvent(_NET_WM_STATE_REMOVE, w.atoms.wmStateMaximizedHorz, w.atoms.wmStateMaximizedVert)
}
w.setTitle(prev, cnf)
if prev.Size != cnf.Size {
w.config.Size = cnf.Size
C.XResizeWindow(w.x, w.xw, C.uint(cnf.Size.X), C.uint(cnf.Size.Y))
}
if prev.MinSize != cnf.MinSize {
w.config.MinSize = cnf.MinSize
shints.min_width = C.int(cnf.MinSize.X)
shints.min_height = C.int(cnf.MinSize.Y)
shints.flags = C.PMinSize
}
if prev.MaxSize != cnf.MaxSize {
w.config.MaxSize = cnf.MaxSize
shints.max_width = C.int(cnf.MaxSize.X)
shints.max_height = C.int(cnf.MaxSize.Y)
shints.flags = shints.flags | C.PMaxSize
}
if shints.flags != 0 {
C.XSetWMNormalHints(w.x, w.xw, &shints)
}
if cnf.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))
}
}
if w.config != prev {
w.w.Event(ConfigEvent{Config: w.config})
}
}
func (w *x11Window) setTitle(prev, cnf Config) {
if prev.Title != cnf.Title {
title := cnf.Title
ctitle := C.CString(title)
@@ -196,13 +262,6 @@ func (w *x11Window) Configure(options []Option) {
},
w.atoms.wmName)
}
if prev.Mode != cnf.Mode {
w.SetWindowMode(cnf.Mode)
}
if w.config != prev {
w.w.Event(ConfigEvent{Config: w.config})
}
}
func (w *x11Window) Raise() {
@@ -249,22 +308,6 @@ func (w *x11Window) SetCursor(name pointer.CursorName) {
C.XDefineCursor(w.x, w.xw, c)
}
func (w *x11Window) SetWindowMode(mode WindowMode) {
var action C.long
switch mode {
case Windowed:
action = _NET_WM_STATE_REMOVE
case Fullscreen:
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."
w.sendWMStateEvent(action, w.atoms.wmStateFullscreen, 0)
}
func (w *x11Window) ShowTextInput(show bool) {}
func (w *x11Window) SetInputHint(_ key.InputHint) {}
@@ -286,29 +329,6 @@ func (w *x11Window) Close() {
C.XSendEvent(w.x, w.xw, C.False, C.NoEventMask, &xev)
}
// Maximize the window.
func (w *x11Window) Maximize() {
w.sendWMStateEvent(_NET_WM_STATE_ADD, w.atoms.wmStateMaximizedHorz, w.atoms.wmStateMaximizedVert)
}
// 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
+2 -2
View File
@@ -704,8 +704,8 @@ func Size(w, h unit.Value) Option {
}
}
// Center is an option to center the window on the screen.
// The option is ignored in Fullscreen mode.
// Centered is an option to center the window on the screen.
// The option is ignored in Fullscreen mode and on Wayland.
func Centered() Option {
return func(m unit.Metric, cnf *Config) {
// Set the flag so the driver can later do the actual centering.