diff --git a/app/internal/wm/os_js.go b/app/internal/wm/os_js.go index 057fb586..7eb627b9 100644 --- a/app/internal/wm/os_js.go +++ b/app/internal/wm/os_js.go @@ -82,7 +82,9 @@ func NewWindow(win Callbacks, opts *Options) error { }) w.addEventListeners() w.addHistory() - w.windowMode(opts.WindowMode) + if o := opts.WindowMode; o != nil { + w.windowMode(*o) + } w.w = win go func() { diff --git a/app/internal/wm/os_macos.go b/app/internal/wm/os_macos.go index 68ead7b0..b82c8d0f 100644 --- a/app/internal/wm/os_macos.go +++ b/app/internal/wm/os_macos.go @@ -338,25 +338,32 @@ func NewWindow(win Callbacks, opts *Options) error { } screenScale := float32(C.gio_getScreenBackingScale()) cfg := configFor(screenScale) - width := cfg.Px(opts.Width) - height := cfg.Px(opts.Height) // Window sizes is in unscaled screen coordinates, not device pixels. - width = int(float32(width) / screenScale) - height = int(float32(height) / screenScale) - minWidth := cfg.Px(opts.MinWidth) - minHeight := cfg.Px(opts.MinHeight) - minWidth = int(float32(minWidth) / screenScale) - minHeight = int(float32(minHeight) / screenScale) - maxWidth := cfg.Px(opts.MaxWidth) - maxHeight := cfg.Px(opts.MaxHeight) - maxWidth = int(float32(maxWidth) / screenScale) - maxHeight = int(float32(maxHeight) / screenScale) - title := C.CString(opts.Title) - defer C.free(unsafe.Pointer(title)) + var width, height int + if o := opts.Size; o != nil { + width = int(float32(cfg.Px(o.Width)) / screenScale) + height = int(float32(cfg.Px(o.Height)) / screenScale) + } + var minWidth, minHeight int + if o := opts.MinSize; o != nil { + minWidth = int(float32(cfg.Px(o.Width)) / screenScale) + minHeight = int(float32(cfg.Px(o.Height)) / screenScale) + } + var maxWidth, maxHeight int + if o := opts.MaxSize; o != nil { + maxWidth = int(float32(cfg.Px(o.Width)) / screenScale) + maxHeight = int(float32(cfg.Px(o.Height)) / screenScale) + } + var title string + if o := opts.Title; o != nil { + title = *o + } + ctitle := C.CString(title) + defer C.free(unsafe.Pointer(ctitle)) errch <- nil win.SetDriver(w) w.w = win - w.window = C.gio_createWindow(w.view, title, C.CGFloat(width), C.CGFloat(height), + w.window = C.gio_createWindow(w.view, ctitle, C.CGFloat(width), C.CGFloat(height), C.CGFloat(minWidth), C.CGFloat(minHeight), C.CGFloat(maxWidth), C.CGFloat(maxHeight)) if nextTopLeft.x == 0 && nextTopLeft.y == 0 { // cascadeTopLeftFromPoint treats (0, 0) as a no-op, @@ -365,7 +372,9 @@ func NewWindow(win Callbacks, opts *Options) error { } nextTopLeft = C.gio_cascadeTopLeftFromPoint(w.window, nextTopLeft) C.gio_makeKeyAndOrderFront(w.window) - w.SetWindowMode(opts.WindowMode) + if o := opts.WindowMode; o != nil { + w.SetWindowMode(*o) + } }) return <-errch } diff --git a/app/internal/wm/os_wayland.go b/app/internal/wm/os_wayland.go index e0dd7230..4a52078c 100644 --- a/app/internal/wm/os_wayland.go +++ b/app/internal/wm/os_wayland.go @@ -356,13 +356,18 @@ func (d *wlDisplay) createNativeWindow(opts *Options) (*window, error) { C.wl_surface_add_listener(w.surf, &C.gio_surface_listener, unsafe.Pointer(w.surf)) C.xdg_surface_add_listener(w.wmSurf, &C.gio_xdg_surface_listener, unsafe.Pointer(w.surf)) C.xdg_toplevel_add_listener(w.topLvl, &C.gio_xdg_toplevel_listener, unsafe.Pointer(w.surf)) - title := C.CString(opts.Title) - C.xdg_toplevel_set_title(w.topLvl, title) - C.free(unsafe.Pointer(title)) + + if o := opts.Title; o != nil { + title := C.CString(*o) + C.xdg_toplevel_set_title(w.topLvl, title) + C.free(unsafe.Pointer(title)) + } _, _, cfg := w.config() - w.width = cfg.Px(opts.Width) - w.height = cfg.Px(opts.Height) + if o := opts.Size; o != nil { + w.width = cfg.Px(o.Width) + w.height = cfg.Px(o.Height) + } if d.decor != nil { // Request server side decorations. w.decor = C.zxdg_decoration_manager_v1_get_toplevel_decoration(d.decor, w.topLvl) diff --git a/app/internal/wm/os_windows.go b/app/internal/wm/os_windows.go index 29cd8f28..fe2daf4e 100644 --- a/app/internal/wm/os_windows.go +++ b/app/internal/wm/os_windows.go @@ -122,7 +122,9 @@ func NewWindow(window Callbacks, opts *Options) error { // Since the window class for the cursor is null, // set it here to show the cursor. w.SetCursor(pointer.CursorDefault) - w.SetWindowMode(opts.WindowMode) + if o := opts.WindowMode; o != nil { + w.SetWindowMode(*o) + } if err := w.loop(); err != nil { panic(err) } @@ -162,10 +164,14 @@ func initResources() error { func getWindowConstraints(cfg unit.Metric, opts *Options) winConstraints { var minmax winConstraints - minmax.minWidth = int32(cfg.Px(opts.MinWidth)) - minmax.minHeight = int32(cfg.Px(opts.MinHeight)) - minmax.maxWidth = int32(cfg.Px(opts.MaxWidth)) - minmax.maxHeight = int32(cfg.Px(opts.MaxHeight)) + if o := opts.MinSize; o != nil { + minmax.minWidth = int32(cfg.Px(o.Width)) + minmax.minHeight = int32(cfg.Px(o.Height)) + } + if o := opts.MaxSize; o != nil { + minmax.maxWidth = int32(cfg.Px(o.Width)) + minmax.maxHeight = int32(cfg.Px(o.Height)) + } return minmax } @@ -179,9 +185,10 @@ func createNativeWindow(opts *Options) (*window, error) { } dpi := windows.GetSystemDPI() cfg := configForDPI(dpi) - wr := windows.Rect{ - Right: int32(cfg.Px(opts.Width)), - Bottom: int32(cfg.Px(opts.Height)), + var wr windows.Rect + if o := opts.Size; o != nil { + wr.Right = int32(cfg.Px(o.Width)) + wr.Bottom = int32(cfg.Px(o.Height)) } dwStyle := uint32(windows.WS_OVERLAPPEDWINDOW) dwExStyle := uint32(windows.WS_EX_APPWINDOW | windows.WS_EX_WINDOWEDGE) @@ -193,9 +200,14 @@ func createNativeWindow(opts *Options) (*window, error) { deltas.width = wr.Right - wr.Left - deltas.width deltas.height = wr.Bottom - wr.Top - deltas.height + var title string + if o := opts.Title; o != nil { + title = *o + } + hwnd, err := windows.CreateWindowEx(dwExStyle, resources.class, - opts.Title, + title, dwStyle|windows.WS_CLIPSIBLINGS|windows.WS_CLIPCHILDREN, windows.CW_USEDEFAULT, windows.CW_USEDEFAULT, wr.Right-wr.Left, diff --git a/app/internal/wm/os_x11.go b/app/internal/wm/os_x11.go index a5a25c66..671fd4a9 100644 --- a/app/internal/wm/os_x11.go +++ b/app/internal/wm/os_x11.go @@ -576,15 +576,20 @@ func newX11Window(gioWin Callbacks, opts *Options) error { background_pixmap: C.None, override_redirect: C.False, } + var width, height int + if o := opts.Size; o != nil { + width = cfg.Px(o.Width) + height = cfg.Px(o.Height) + } win := C.XCreateWindow(dpy, C.XDefaultRootWindow(dpy), - 0, 0, C.uint(cfg.Px(opts.Width)), C.uint(cfg.Px(opts.Height)), + 0, 0, C.uint(width), C.uint(height), 0, C.CopyFromParent, C.InputOutput, nil, C.CWEventMask|C.CWBackPixmap|C.CWOverrideRedirect, &swa) w := &x11Window{ w: gioWin, x: dpy, xw: win, - width: cfg.Px(opts.Width), - height: cfg.Px(opts.Height), + width: width, + height: height, cfg: cfg, xkb: xkb, xkbEventBase: xkbEventBase, @@ -603,14 +608,14 @@ func newX11Window(gioWin Callbacks, opts *Options) error { C.XSetWMHints(dpy, win, &hints) var shints C.XSizeHints - if opts.MinWidth.V != 0 || opts.MinHeight.V != 0 { - shints.min_width = C.int(cfg.Px(opts.MinWidth)) - shints.min_height = C.int(cfg.Px(opts.MinHeight)) + if o := opts.MinSize; o != nil && (o.Width.V != 0 || o.Height.V != 0) { + shints.min_width = C.int(cfg.Px(o.Width)) + shints.min_height = C.int(cfg.Px(o.Height)) shints.flags = C.PMinSize } - if opts.MaxWidth.V != 0 || opts.MaxHeight.V != 0 { - shints.max_width = C.int(cfg.Px(opts.MaxWidth)) - shints.max_height = C.int(cfg.Px(opts.MaxHeight)) + if o := opts.MaxSize; o != nil && (o.Width.V != 0 || o.Height.V != 0) { + shints.max_width = C.int(cfg.Px(o.Width)) + shints.max_height = C.int(cfg.Px(o.Height)) shints.flags = shints.flags | C.PMaxSize } if shints.flags != 0 { @@ -635,7 +640,11 @@ func newX11Window(gioWin Callbacks, opts *Options) error { w.atoms.wmStateFullscreen = w.atom("_NET_WM_STATE_FULLSCREEN", false) // set the name - ctitle := C.CString(opts.Title) + var title string + if o := opts.Title; o != nil { + title = *o + } + ctitle := C.CString(title) defer C.free(unsafe.Pointer(ctitle)) C.XStoreName(dpy, win, ctitle) // set _NET_WM_NAME as well for UTF-8 support in window title. @@ -644,14 +653,16 @@ func newX11Window(gioWin Callbacks, opts *Options) error { value: (*C.uchar)(unsafe.Pointer(ctitle)), encoding: w.atoms.utf8string, format: 8, - nitems: C.ulong(len(opts.Title)), + nitems: C.ulong(len(title)), }, w.atoms.wmName) // extensions C.XSetWMProtocols(dpy, win, &w.atoms.evDelWindow, 1) - w.SetWindowMode(opts.WindowMode) + if o := opts.WindowMode; o != nil { + w.SetWindowMode(*o) + } // make the window visible on the screen C.XMapWindow(dpy, win) diff --git a/app/internal/wm/window.go b/app/internal/wm/window.go index b33a1b66..7eb51f2d 100644 --- a/app/internal/wm/window.go +++ b/app/internal/wm/window.go @@ -14,12 +14,17 @@ import ( "gioui.org/unit" ) +type Size struct { + Width unit.Value + Height unit.Value +} + type Options struct { - Width, Height unit.Value - MinWidth, MinHeight unit.Value - MaxWidth, MaxHeight unit.Value - Title string - WindowMode WindowMode + Size *Size + MinSize *Size + MaxSize *Size + Title *string + WindowMode *WindowMode } type WindowMode uint8 diff --git a/app/window.go b/app/window.go index 2de6c1ef..e8f691a9 100644 --- a/app/window.go +++ b/app/window.go @@ -84,11 +84,10 @@ var ackEvent event.Event // Calling NewWindow more than once is not supported on // iOS, Android, WebAssembly. func NewWindow(options ...Option) *Window { - opts := &wm.Options{ - Width: unit.Dp(800), - Height: unit.Dp(600), - Title: "Gio", - } + opts := new(wm.Options) + // Default options. + Size(unit.Px(800), unit.Px(600))(opts) + Title("Gio")(opts) for _, o := range options { o(opts) @@ -456,14 +455,14 @@ const ( // Supported platforms are macOS, X11 and Windows. func WindowMode(mode wm.WindowMode) Option { return func(opts *wm.Options) { - opts.WindowMode = mode + opts.WindowMode = &mode } } // Title sets the title of the wm. func Title(t string) Option { return func(opts *wm.Options) { - opts.Title = t + opts.Title = &t } } @@ -476,8 +475,10 @@ func Size(w, h unit.Value) Option { panic("height must be larger than or equal to 0") } return func(opts *wm.Options) { - opts.Width = w - opts.Height = h + opts.Size = &wm.Size{ + Width: w, + Height: h, + } } } @@ -490,8 +491,10 @@ func MaxSize(w, h unit.Value) Option { panic("height must be larger than or equal to 0") } return func(opts *wm.Options) { - opts.MaxWidth = w - opts.MaxHeight = h + opts.MaxSize = &wm.Size{ + Width: w, + Height: h, + } } } @@ -504,8 +507,10 @@ func MinSize(w, h unit.Value) Option { panic("height must be larger than or equal to 0") } return func(opts *wm.Options) { - opts.MinWidth = w - opts.MinHeight = h + opts.MinSize = &wm.Size{ + Width: w, + Height: h, + } } }