mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 18:05:35 +00:00
app: [API] add minimized window mode, change methods to options
The window modes are extended, following microsoft conventions.
We have Fullscreen, Overlapping, Maximized and Minimized.
These modes can be set via options when a new window is creates,
or modified later by calling helper functions like w.Maximize() and w.Center()
The window configuration is automatically updated when a user
modifies the window by dragging or clicking the icons on the window's title-bar,
minimizing or maximizing the window.
Any change, either by the user or the application will emit a ConfigChange event.
This is implemented and tested on Windows only.
API change. the app.Window methods Maximize and Center are replaced with similar
options. For example, to maximize a window use
w.Option(app.Maximized.Option())
Also, Maximize and Center implementations for X11 and macOS are left for a future
change.
Fixes: https://todo.sr.ht/~eliasnaur/gio/315
Signed-off-by: Jan Kåre Vatne <jkvatne@online.no>
This commit is contained in:
committed by
Elias Naur
parent
13183522dd
commit
c4f98d3c1e
@@ -106,9 +106,13 @@ const (
|
|||||||
SIZE_MINIMIZED = 1
|
SIZE_MINIMIZED = 1
|
||||||
SIZE_RESTORED = 0
|
SIZE_RESTORED = 0
|
||||||
|
|
||||||
SW_SHOWDEFAULT = 10
|
SW_SHOWDEFAULT = 10
|
||||||
|
SW_SHOWMINIMIZED = 2
|
||||||
|
SW_SHOWMAXIMIZED = 3
|
||||||
|
SW_SHOWNORMAL = 1
|
||||||
|
SW_SHOW = 5
|
||||||
|
SWP_FRAMECHANGED = 0x0020
|
||||||
|
|
||||||
SWP_FRAMECHANGED = 0x0020
|
|
||||||
SWP_NOMOVE = 0x0002
|
SWP_NOMOVE = 0x0002
|
||||||
SWP_NOOWNERZORDER = 0x0200
|
SWP_NOOWNERZORDER = 0x0200
|
||||||
SWP_NOSIZE = 0x0001
|
SWP_NOSIZE = 0x0001
|
||||||
@@ -166,42 +170,44 @@ const (
|
|||||||
|
|
||||||
UNICODE_NOCHAR = 65535
|
UNICODE_NOCHAR = 65535
|
||||||
|
|
||||||
WM_CANCELMODE = 0x001F
|
WM_CANCELMODE = 0x001F
|
||||||
WM_CHAR = 0x0102
|
WM_CHAR = 0x0102
|
||||||
WM_CREATE = 0x0001
|
WM_CREATE = 0x0001
|
||||||
WM_DPICHANGED = 0x02E0
|
WM_DPICHANGED = 0x02E0
|
||||||
WM_DESTROY = 0x0002
|
WM_DESTROY = 0x0002
|
||||||
WM_ERASEBKGND = 0x0014
|
WM_ERASEBKGND = 0x0014
|
||||||
WM_KEYDOWN = 0x0100
|
WM_KEYDOWN = 0x0100
|
||||||
WM_KEYUP = 0x0101
|
WM_KEYUP = 0x0101
|
||||||
WM_LBUTTONDOWN = 0x0201
|
WM_LBUTTONDOWN = 0x0201
|
||||||
WM_LBUTTONUP = 0x0202
|
WM_LBUTTONUP = 0x0202
|
||||||
WM_MBUTTONDOWN = 0x0207
|
WM_MBUTTONDOWN = 0x0207
|
||||||
WM_MBUTTONUP = 0x0208
|
WM_MBUTTONUP = 0x0208
|
||||||
WM_MOUSEMOVE = 0x0200
|
WM_MOUSEMOVE = 0x0200
|
||||||
WM_MOUSEWHEEL = 0x020A
|
WM_MOUSEWHEEL = 0x020A
|
||||||
WM_MOUSEHWHEEL = 0x020E
|
WM_MOUSEHWHEEL = 0x020E
|
||||||
WM_PAINT = 0x000F
|
WM_PAINT = 0x000F
|
||||||
WM_CLOSE = 0x0010
|
WM_CLOSE = 0x0010
|
||||||
WM_QUIT = 0x0012
|
WM_QUIT = 0x0012
|
||||||
WM_SETCURSOR = 0x0020
|
WM_SETCURSOR = 0x0020
|
||||||
WM_SETFOCUS = 0x0007
|
WM_SETFOCUS = 0x0007
|
||||||
WM_KILLFOCUS = 0x0008
|
WM_KILLFOCUS = 0x0008
|
||||||
WM_SHOWWINDOW = 0x0018
|
WM_SHOWWINDOW = 0x0018
|
||||||
WM_SIZE = 0x0005
|
WM_SIZE = 0x0005
|
||||||
WM_SYSKEYDOWN = 0x0104
|
WM_SYSKEYDOWN = 0x0104
|
||||||
WM_SYSKEYUP = 0x0105
|
WM_SYSKEYUP = 0x0105
|
||||||
WM_RBUTTONDOWN = 0x0204
|
WM_RBUTTONDOWN = 0x0204
|
||||||
WM_RBUTTONUP = 0x0205
|
WM_RBUTTONUP = 0x0205
|
||||||
WM_TIMER = 0x0113
|
WM_TIMER = 0x0113
|
||||||
WM_UNICHAR = 0x0109
|
WM_UNICHAR = 0x0109
|
||||||
WM_USER = 0x0400
|
WM_USER = 0x0400
|
||||||
WM_GETMINMAXINFO = 0x0024
|
WM_GETMINMAXINFO = 0x0024
|
||||||
|
WM_WINDOWPOSCHANGED = 0x0047
|
||||||
WS_CLIPCHILDREN = 0x00010000
|
WS_CLIPCHILDREN = 0x00010000
|
||||||
WS_CLIPSIBLINGS = 0x04000000
|
WS_CLIPSIBLINGS = 0x04000000
|
||||||
WS_MAXIMIZE = 0x01000000
|
WS_MAXIMIZE = 0x01000000
|
||||||
|
WS_ICONIC = 0x20000000
|
||||||
WS_VISIBLE = 0x10000000
|
WS_VISIBLE = 0x10000000
|
||||||
|
|
||||||
WS_OVERLAPPED = 0x00000000
|
WS_OVERLAPPED = 0x00000000
|
||||||
WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME |
|
WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME |
|
||||||
WS_MINIMIZEBOX | WS_MAXIMIZEBOX
|
WS_MINIMIZEBOX | WS_MAXIMIZEBOX
|
||||||
@@ -259,7 +265,7 @@ var (
|
|||||||
_DestroyWindow = user32.NewProc("DestroyWindow")
|
_DestroyWindow = user32.NewProc("DestroyWindow")
|
||||||
_DispatchMessage = user32.NewProc("DispatchMessageW")
|
_DispatchMessage = user32.NewProc("DispatchMessageW")
|
||||||
_EmptyClipboard = user32.NewProc("EmptyClipboard")
|
_EmptyClipboard = user32.NewProc("EmptyClipboard")
|
||||||
_GetClientRect = user32.NewProc("GetClientRect")
|
_GetWindowRect = user32.NewProc("GetWindowRect")
|
||||||
_GetClipboardData = user32.NewProc("GetClipboardData")
|
_GetClipboardData = user32.NewProc("GetClipboardData")
|
||||||
_GetDC = user32.NewProc("GetDC")
|
_GetDC = user32.NewProc("GetDC")
|
||||||
_GetDpiForWindow = user32.NewProc("GetDpiForWindow")
|
_GetDpiForWindow = user32.NewProc("GetDpiForWindow")
|
||||||
@@ -370,8 +376,8 @@ func EmptyClipboard() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetClientRect(hwnd syscall.Handle, r *Rect) {
|
func GetWindowRect(hwnd syscall.Handle, r *Rect) {
|
||||||
_GetClientRect.Call(uintptr(hwnd), uintptr(unsafe.Pointer(r)))
|
_GetWindowRect.Call(uintptr(hwnd), uintptr(unsafe.Pointer(r)))
|
||||||
issue34474KeepAlive(r)
|
issue34474KeepAlive(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -683,6 +689,21 @@ func (p WindowPlacement) Rect() Rect {
|
|||||||
return p.rcNormalPosition
|
return p.rcNormalPosition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p WindowPlacement) IsMinimized() bool {
|
||||||
|
return p.showCmd == SW_SHOWMINIMIZED
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p WindowPlacement) IsMaximized() bool {
|
||||||
|
return p.showCmd == SW_SHOWMAXIMIZED
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *WindowPlacement) Set(Left, Top, Right, Bottom int) {
|
||||||
|
p.rcNormalPosition.Left = int32(Left)
|
||||||
|
p.rcNormalPosition.Top = int32(Top)
|
||||||
|
p.rcNormalPosition.Right = int32(Right)
|
||||||
|
p.rcNormalPosition.Bottom = int32(Bottom)
|
||||||
|
}
|
||||||
|
|
||||||
// issue34474KeepAlive calls runtime.KeepAlive as a
|
// issue34474KeepAlive calls runtime.KeepAlive as a
|
||||||
// workaround for golang.org/issue/34474.
|
// workaround for golang.org/issue/34474.
|
||||||
func issue34474KeepAlive(v interface{}) {
|
func issue34474KeepAlive(v interface{}) {
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ type Config struct {
|
|||||||
// CustomRenderer is true when the window content is rendered by the
|
// CustomRenderer is true when the window content is rendered by the
|
||||||
// client.
|
// client.
|
||||||
CustomRenderer bool
|
CustomRenderer bool
|
||||||
|
// center is a flag used to center the window. Set by option.
|
||||||
|
center bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigEvent is sent whenever the configuration of a Window changes.
|
// ConfigEvent is sent whenever the configuration of a Window changes.
|
||||||
@@ -57,8 +59,8 @@ func (c *Config) apply(m unit.Metric, options []Option) {
|
|||||||
type wakeupEvent struct{}
|
type wakeupEvent struct{}
|
||||||
|
|
||||||
// WindowMode is the window mode (WindowMode.Option sets it).
|
// WindowMode is the window mode (WindowMode.Option sets it).
|
||||||
//
|
// Note that mode can be changed programatically as well as by the user
|
||||||
// Supported platforms are macOS, X11, Windows, Android and JS.
|
// clicking on the minimize/maximize buttons on the window's title bar.
|
||||||
type WindowMode uint8
|
type WindowMode uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -66,20 +68,30 @@ const (
|
|||||||
Windowed WindowMode = iota
|
Windowed WindowMode = iota
|
||||||
// Fullscreen is the full screen window mode.
|
// Fullscreen is the full screen window mode.
|
||||||
Fullscreen
|
Fullscreen
|
||||||
|
// Minimized is for systems where the window can be minimized to an icon.
|
||||||
|
Minimized
|
||||||
|
// Maximized is for systems where the window can be made to fill the available monitor area.
|
||||||
|
Maximized
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Option changes the mode of a Window.
|
||||||
func (m WindowMode) Option() Option {
|
func (m WindowMode) Option() Option {
|
||||||
return func(_ unit.Metric, cnf *Config) {
|
return func(_ unit.Metric, cnf *Config) {
|
||||||
cnf.Mode = m
|
cnf.Mode = m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String returns the mode name.
|
||||||
func (m WindowMode) String() string {
|
func (m WindowMode) String() string {
|
||||||
switch m {
|
switch m {
|
||||||
case Windowed:
|
case Windowed:
|
||||||
return "windowed"
|
return "windowed"
|
||||||
case Fullscreen:
|
case Fullscreen:
|
||||||
return "fullscreen"
|
return "fullscreen"
|
||||||
|
case Minimized:
|
||||||
|
return "minimized"
|
||||||
|
case Maximized:
|
||||||
|
return "maximized"
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -165,11 +177,6 @@ type driver interface {
|
|||||||
|
|
||||||
// Wakeup wakes up the event loop and sends a WakeupEvent.
|
// Wakeup wakes up the event loop and sends a WakeupEvent.
|
||||||
Wakeup()
|
Wakeup()
|
||||||
|
|
||||||
// Maximize will make the window as large as possible, but keep the frame decorations.
|
|
||||||
Maximize()
|
|
||||||
// Center will place the window at monitor center.
|
|
||||||
Center()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type windowRendezvous struct {
|
type windowRendezvous struct {
|
||||||
|
|||||||
@@ -1261,12 +1261,6 @@ func setNavigationColor(env *C.JNIEnv, view C.jobject, color color.NRGBA) {
|
|||||||
// Close the window. Not implemented for Android.
|
// Close the window. Not implemented for Android.
|
||||||
func (w *window) Close() {}
|
func (w *window) Close() {}
|
||||||
|
|
||||||
// Maximize maximizes the window. Not implemented for Android.
|
|
||||||
func (w *window) Maximize() {}
|
|
||||||
|
|
||||||
// Center the window. Not implemented for Android.
|
|
||||||
func (w *window) Center() {}
|
|
||||||
|
|
||||||
// runOnMain runs a function on the Java main thread.
|
// runOnMain runs a function on the Java main thread.
|
||||||
func runOnMain(f func(env *C.JNIEnv)) {
|
func runOnMain(f func(env *C.JNIEnv)) {
|
||||||
go func() {
|
go func() {
|
||||||
|
|||||||
@@ -336,12 +336,6 @@ func (w *window) SetInputHint(_ key.InputHint) {}
|
|||||||
// Close the window. Not implemented for iOS.
|
// Close the window. Not implemented for iOS.
|
||||||
func (w *window) Close() {}
|
func (w *window) Close() {}
|
||||||
|
|
||||||
// Maximize the window. Not implemented for iOS.
|
|
||||||
func (w *window) Maximize() {}
|
|
||||||
|
|
||||||
// Center the window. Not implemented for iOS.
|
|
||||||
func (w *window) Center() {}
|
|
||||||
|
|
||||||
func newWindow(win *callbacks, options []Option) error {
|
func newWindow(win *callbacks, options []Option) error {
|
||||||
mainWindow.in <- windowAndConfig{win, options}
|
mainWindow.in <- windowAndConfig{win, options}
|
||||||
return <-mainWindow.errs
|
return <-mainWindow.errs
|
||||||
|
|||||||
@@ -566,12 +566,6 @@ func (w *window) SetInputHint(mode key.InputHint) {
|
|||||||
// Close the window. Not implemented for js.
|
// Close the window. Not implemented for js.
|
||||||
func (w *window) Close() {}
|
func (w *window) Close() {}
|
||||||
|
|
||||||
// Maximize the window. Not implemented for js.
|
|
||||||
func (w *window) Maximize() {}
|
|
||||||
|
|
||||||
// Center the window. Not implemented for js.
|
|
||||||
func (w *window) Center() {}
|
|
||||||
|
|
||||||
func (w *window) resize() {
|
func (w *window) resize() {
|
||||||
w.scale = float32(w.window.Get("devicePixelRatio").Float())
|
w.scale = float32(w.window.Get("devicePixelRatio").Float())
|
||||||
|
|
||||||
|
|||||||
@@ -1487,12 +1487,6 @@ func (w *window) Close() {
|
|||||||
w.dead = true
|
w.dead = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maximize the window. Not implemented for Wayland.
|
|
||||||
func (w *window) Maximize() {}
|
|
||||||
|
|
||||||
// Center the window. Not implemented for Wayland.
|
|
||||||
func (w *window) Center() {}
|
|
||||||
|
|
||||||
func (w *window) NewContext() (context, error) {
|
func (w *window) NewContext() (context, error) {
|
||||||
var firstErr error
|
var firstErr error
|
||||||
if f := newWaylandVulkanContext; f != nil {
|
if f := newWaylandVulkanContext; f != nil {
|
||||||
|
|||||||
+78
-138
@@ -108,7 +108,6 @@ func newWindow(window *callbacks, options []Option) error {
|
|||||||
w.w.SetDriver(w)
|
w.w.SetDriver(w)
|
||||||
w.w.Event(ViewEvent{HWND: uintptr(w.hwnd)})
|
w.w.Event(ViewEvent{HWND: uintptr(w.hwnd)})
|
||||||
w.Configure(options)
|
w.Configure(options)
|
||||||
windows.ShowWindow(w.hwnd, windows.SW_SHOWDEFAULT)
|
|
||||||
windows.SetForegroundWindow(w.hwnd)
|
windows.SetForegroundWindow(w.hwnd)
|
||||||
windows.SetFocus(w.hwnd)
|
windows.SetFocus(w.hwnd)
|
||||||
// Since the window class for the cursor is null,
|
// Since the window class for the cursor is null,
|
||||||
@@ -185,6 +184,43 @@ func createNativeWindow() (*window, error) {
|
|||||||
return w, nil
|
return w, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update() handles changes done by the user, and updates the configuration.
|
||||||
|
// It reads the window style and size/position and updates w.config.
|
||||||
|
// If anything has changed it emits a ConfigEvent to notify the application.
|
||||||
|
func (w *window) update() {
|
||||||
|
var triggerEvent bool
|
||||||
|
var r windows.Rect
|
||||||
|
windows.GetWindowRect(w.hwnd, &r)
|
||||||
|
size := image.Point{
|
||||||
|
X: int(r.Right - r.Left - w.deltas.width),
|
||||||
|
Y: int(r.Bottom - r.Top - w.deltas.height),
|
||||||
|
}
|
||||||
|
if size != w.config.Size {
|
||||||
|
w.config.Size = size
|
||||||
|
triggerEvent = true
|
||||||
|
}
|
||||||
|
// Check the window mode.
|
||||||
|
mode := w.config.Mode
|
||||||
|
p := windows.GetWindowPlacement(w.hwnd)
|
||||||
|
style := windows.GetWindowLong(w.hwnd)
|
||||||
|
if style&windows.WS_OVERLAPPEDWINDOW == 0 {
|
||||||
|
mode = Fullscreen
|
||||||
|
} else if p.IsMinimized() {
|
||||||
|
mode = Minimized
|
||||||
|
} else if p.IsMaximized() {
|
||||||
|
mode = Maximized
|
||||||
|
} else {
|
||||||
|
mode = Windowed
|
||||||
|
}
|
||||||
|
if mode != w.config.Mode {
|
||||||
|
w.config.Mode = mode
|
||||||
|
triggerEvent = true
|
||||||
|
}
|
||||||
|
if triggerEvent {
|
||||||
|
w.w.Event(ConfigEvent{Config: w.config})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr {
|
func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr {
|
||||||
win, exists := winMap.Load(hwnd)
|
win, exists := winMap.Load(hwnd)
|
||||||
if !exists {
|
if !exists {
|
||||||
@@ -280,29 +316,10 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
|
|||||||
case windows.WM_SIZE:
|
case windows.WM_SIZE:
|
||||||
switch wParam {
|
switch wParam {
|
||||||
case windows.SIZE_MINIMIZED:
|
case windows.SIZE_MINIMIZED:
|
||||||
|
w.update()
|
||||||
w.setStage(system.StagePaused)
|
w.setStage(system.StagePaused)
|
||||||
case windows.SIZE_MAXIMIZED, windows.SIZE_RESTORED:
|
case windows.SIZE_MAXIMIZED, windows.SIZE_RESTORED:
|
||||||
var triggerEvent bool
|
w.update()
|
||||||
// Check the window size change.
|
|
||||||
var r windows.Rect
|
|
||||||
windows.GetClientRect(w.hwnd, &r)
|
|
||||||
size := image.Point{
|
|
||||||
X: int(r.Right - r.Left),
|
|
||||||
Y: int(r.Bottom - r.Top),
|
|
||||||
}
|
|
||||||
if size != w.config.Size {
|
|
||||||
w.config.Size = size
|
|
||||||
triggerEvent = true
|
|
||||||
}
|
|
||||||
// Check the window mode.
|
|
||||||
mode := w.config.Mode
|
|
||||||
w.updateWindowMode()
|
|
||||||
if mode != w.config.Mode {
|
|
||||||
triggerEvent = true
|
|
||||||
}
|
|
||||||
if triggerEvent {
|
|
||||||
w.w.Event(ConfigEvent{Config: w.config})
|
|
||||||
}
|
|
||||||
w.setStage(system.StageRunning)
|
w.setStage(system.StageRunning)
|
||||||
}
|
}
|
||||||
case windows.WM_GETMINMAXINFO:
|
case windows.WM_GETMINMAXINFO:
|
||||||
@@ -503,127 +520,44 @@ func (w *window) readClipboard() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) updateWindowMode() {
|
|
||||||
p := windows.GetWindowPlacement(w.hwnd)
|
|
||||||
r := p.Rect()
|
|
||||||
mi := windows.GetMonitorInfo(w.hwnd)
|
|
||||||
if r == mi.Monitor {
|
|
||||||
w.config.Mode = Fullscreen
|
|
||||||
} else {
|
|
||||||
w.config.Mode = Windowed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *window) Configure(options []Option) {
|
func (w *window) Configure(options []Option) {
|
||||||
|
oldConfig := w.config
|
||||||
dpi := windows.GetSystemDPI()
|
dpi := windows.GetSystemDPI()
|
||||||
cfg := configForDPI(dpi)
|
metric := configForDPI(dpi)
|
||||||
prev := w.config
|
w.config.apply(metric, options)
|
||||||
w.updateWindowMode()
|
switch w.config.Mode {
|
||||||
cnf := w.config
|
case Minimized:
|
||||||
cnf.apply(cfg, options)
|
windows.ShowWindow(w.hwnd, windows.SW_SHOWMINIMIZED)
|
||||||
|
|
||||||
if cnf.Mode != Fullscreen && prev.Size != cnf.Size {
|
case Maximized:
|
||||||
width := int32(cnf.Size.X)
|
windows.SetWindowText(w.hwnd, w.config.Title)
|
||||||
height := int32(cnf.Size.Y)
|
style := windows.GetWindowLong(w.hwnd) & (^uintptr(windows.WS_MAXIMIZE))
|
||||||
w.config.Size = cnf.Size
|
|
||||||
|
|
||||||
// Include the window decorations.
|
|
||||||
wr := windows.Rect{
|
|
||||||
Right: width,
|
|
||||||
Bottom: height,
|
|
||||||
}
|
|
||||||
dwStyle := uint32(windows.WS_OVERLAPPEDWINDOW)
|
|
||||||
dwExStyle := uint32(windows.WS_EX_APPWINDOW | windows.WS_EX_WINDOWEDGE)
|
|
||||||
windows.AdjustWindowRectEx(&wr, dwStyle, 0, dwExStyle)
|
|
||||||
|
|
||||||
dw, dh := width, height
|
|
||||||
width = wr.Right - wr.Left
|
|
||||||
height = wr.Bottom - wr.Top
|
|
||||||
w.deltas.width = width - dw
|
|
||||||
w.deltas.height = height - dh
|
|
||||||
|
|
||||||
windows.MoveWindow(w.hwnd, 0, 0, width, height, true)
|
|
||||||
}
|
|
||||||
if prev.MinSize != cnf.MinSize {
|
|
||||||
w.config.MinSize = cnf.MinSize
|
|
||||||
}
|
|
||||||
if prev.MaxSize != cnf.MaxSize {
|
|
||||||
w.config.MaxSize = cnf.MaxSize
|
|
||||||
}
|
|
||||||
if prev.Title != cnf.Title {
|
|
||||||
w.config.Title = cnf.Title
|
|
||||||
windows.SetWindowText(w.hwnd, cnf.Title)
|
|
||||||
}
|
|
||||||
if prev.Mode != cnf.Mode {
|
|
||||||
w.SetWindowMode(cnf.Mode)
|
|
||||||
}
|
|
||||||
if w.config != prev {
|
|
||||||
w.w.Event(ConfigEvent{Config: w.config})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Maximize the window. It will have no effect when in fullscreen mode.
|
|
||||||
func (w *window) Maximize() {
|
|
||||||
if w.config.Mode == Fullscreen {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
style := windows.GetWindowLong(w.hwnd)
|
|
||||||
windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style|windows.WS_OVERLAPPEDWINDOW|windows.WS_MAXIMIZE)
|
|
||||||
mi := windows.GetMonitorInfo(w.hwnd)
|
|
||||||
windows.SetWindowPos(w.hwnd, 0,
|
|
||||||
mi.Monitor.Left, mi.Monitor.Top,
|
|
||||||
mi.Monitor.Right-mi.Monitor.Left,
|
|
||||||
mi.Monitor.Bottom-mi.Monitor.Top,
|
|
||||||
windows.SWP_NOOWNERZORDER|windows.SWP_FRAMECHANGED,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Center will place window at monitor center.
|
|
||||||
func (w *window) Center() {
|
|
||||||
// Make sure that the window is sizeable
|
|
||||||
style := windows.GetWindowLong(w.hwnd) & (^uintptr(windows.WS_MAXIMIZE))
|
|
||||||
windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style|windows.WS_OVERLAPPEDWINDOW)
|
|
||||||
|
|
||||||
// Find with/height including the window decorations.
|
|
||||||
wr := windows.Rect{
|
|
||||||
Right: int32(w.config.Size.X),
|
|
||||||
Bottom: int32(w.config.Size.Y),
|
|
||||||
}
|
|
||||||
dwStyle := uint32(windows.WS_OVERLAPPEDWINDOW)
|
|
||||||
dwExStyle := uint32(windows.WS_EX_APPWINDOW | windows.WS_EX_WINDOWEDGE)
|
|
||||||
windows.AdjustWindowRectEx(&wr, dwStyle, 0, dwExStyle)
|
|
||||||
width := wr.Right - wr.Left
|
|
||||||
height := wr.Bottom - wr.Top
|
|
||||||
|
|
||||||
// Move to center of current monitor
|
|
||||||
mi := windows.GetMonitorInfo(w.hwnd).Monitor
|
|
||||||
x := mi.Left + (mi.Right-mi.Left-width)/2
|
|
||||||
y := mi.Top + (mi.Bottom-mi.Top-height)/2
|
|
||||||
windows.MoveWindow(w.hwnd, x, y, width, height, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *window) SetWindowMode(mode WindowMode) {
|
|
||||||
// https://devblogs.microsoft.com/oldnewthing/20100412-00/?p=14353
|
|
||||||
switch mode {
|
|
||||||
case Windowed:
|
|
||||||
if w.placement == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.config.Mode = Windowed
|
|
||||||
windows.SetWindowPlacement(w.hwnd, w.placement)
|
|
||||||
w.placement = nil
|
|
||||||
style := windows.GetWindowLong(w.hwnd)
|
|
||||||
windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style|windows.WS_OVERLAPPEDWINDOW)
|
windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style|windows.WS_OVERLAPPEDWINDOW)
|
||||||
windows.SetWindowPos(w.hwnd, windows.HWND_TOPMOST,
|
windows.ShowWindow(w.hwnd, windows.SW_SHOWMAXIMIZED)
|
||||||
0, 0, 0, 0,
|
|
||||||
windows.SWP_NOOWNERZORDER|windows.SWP_FRAMECHANGED,
|
case Windowed:
|
||||||
)
|
var r windows.Rect
|
||||||
case Fullscreen:
|
windows.SetWindowText(w.hwnd, w.config.Title)
|
||||||
if w.placement != nil {
|
windows.GetWindowRect(w.hwnd, &r)
|
||||||
return
|
style := windows.GetWindowLong(w.hwnd) & (^uintptr(windows.WS_MAXIMIZE))
|
||||||
|
windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style|windows.WS_OVERLAPPEDWINDOW)
|
||||||
|
width := int32(w.config.Size.X)
|
||||||
|
height := int32(w.config.Size.Y)
|
||||||
|
|
||||||
|
if w.config.center {
|
||||||
|
// Calculate center position on current monitor
|
||||||
|
mi := windows.GetMonitorInfo(w.hwnd).Monitor
|
||||||
|
r.Left = mi.Left + (mi.Right-mi.Left-width)/2
|
||||||
|
r.Top = mi.Top + (mi.Bottom-mi.Top-height)/2
|
||||||
|
// Centering is done only once.
|
||||||
|
w.config.center = false
|
||||||
}
|
}
|
||||||
w.config.Mode = Fullscreen
|
|
||||||
w.placement = windows.GetWindowPlacement(w.hwnd)
|
windows.SetWindowPos(w.hwnd, 0, r.Left, r.Top, width, height,
|
||||||
|
windows.SWP_NOOWNERZORDER|windows.SWP_FRAMECHANGED)
|
||||||
|
windows.ShowWindow(w.hwnd, windows.SW_SHOWNORMAL)
|
||||||
|
|
||||||
|
case Fullscreen:
|
||||||
style := windows.GetWindowLong(w.hwnd)
|
style := windows.GetWindowLong(w.hwnd)
|
||||||
windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style&^windows.WS_OVERLAPPEDWINDOW)
|
windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style&^windows.WS_OVERLAPPEDWINDOW)
|
||||||
mi := windows.GetMonitorInfo(w.hwnd)
|
mi := windows.GetMonitorInfo(w.hwnd)
|
||||||
@@ -633,6 +567,12 @@ func (w *window) SetWindowMode(mode WindowMode) {
|
|||||||
mi.Monitor.Bottom-mi.Monitor.Top,
|
mi.Monitor.Bottom-mi.Monitor.Top,
|
||||||
windows.SWP_NOOWNERZORDER|windows.SWP_FRAMECHANGED,
|
windows.SWP_NOOWNERZORDER|windows.SWP_FRAMECHANGED,
|
||||||
)
|
)
|
||||||
|
windows.ShowWindow(w.hwnd, windows.SW_SHOWNORMAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
w.w.Event(ConfigEvent{Config: w.config})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-18
@@ -321,22 +321,6 @@ func (w *Window) Close() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maximize the window.
|
|
||||||
// Note: only implemented on Windows, macOS and X11.
|
|
||||||
func (w *Window) Maximize() {
|
|
||||||
w.driverDefer(func(d driver) {
|
|
||||||
d.Maximize()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Center the window.
|
|
||||||
// Note: only implemented on Windows, macOS and X11.
|
|
||||||
func (w *Window) Center() {
|
|
||||||
w.driverDefer(func(d driver) {
|
|
||||||
d.Center()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run f in the same thread as the native window event loop, and wait for f to
|
// Run f in the same thread as the native window event loop, and wait for f to
|
||||||
// return or the window to close. Run is guaranteed not to deadlock if it is
|
// return or the window to close. Run is guaranteed not to deadlock if it is
|
||||||
// invoked during the handling of a ViewEvent, system.FrameEvent,
|
// invoked during the handling of a ViewEvent, system.FrameEvent,
|
||||||
@@ -703,8 +687,7 @@ func Title(t string) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size sets the size of the window. The option is ignored
|
// Size sets the size of the window. The mode will be changed to Windowed.
|
||||||
// in Fullscreen mode.
|
|
||||||
func Size(w, h unit.Value) Option {
|
func Size(w, h unit.Value) Option {
|
||||||
if w.V <= 0 {
|
if w.V <= 0 {
|
||||||
panic("width must be larger than or equal to 0")
|
panic("width must be larger than or equal to 0")
|
||||||
@@ -713,6 +696,7 @@ func Size(w, h unit.Value) Option {
|
|||||||
panic("height must be larger than or equal to 0")
|
panic("height must be larger than or equal to 0")
|
||||||
}
|
}
|
||||||
return func(m unit.Metric, cnf *Config) {
|
return func(m unit.Metric, cnf *Config) {
|
||||||
|
cnf.Mode = Windowed
|
||||||
cnf.Size = image.Point{
|
cnf.Size = image.Point{
|
||||||
X: m.Px(w),
|
X: m.Px(w),
|
||||||
Y: m.Px(h),
|
Y: m.Px(h),
|
||||||
@@ -720,6 +704,15 @@ 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.
|
||||||
|
func Centered() Option {
|
||||||
|
return func(m unit.Metric, cnf *Config) {
|
||||||
|
// Set the flag so the driver can later do the actual centering.
|
||||||
|
cnf.center = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MaxSize sets the maximum size of the window.
|
// MaxSize sets the maximum size of the window.
|
||||||
func MaxSize(w, h unit.Value) Option {
|
func MaxSize(w, h unit.Value) Option {
|
||||||
if w.V <= 0 {
|
if w.V <= 0 {
|
||||||
|
|||||||
Reference in New Issue
Block a user