app/window,app/internal/window: allow min/max window size

The app.MinSize and app.MaxSize options restricts the window size:

w := app.NewWindow(
	app.Size(unit.Dp(600), unit.Dp(596)),
	app.MinSize(unit.Dp(600), unit.Dp(596)),
	app.MaxSize(unit.Dp(600), unit.Dp(596)),
	app.Title(APPNAME),
)

Signed-off-by: Jason <sourcehut@sweatyballs.es>
This commit is contained in:
Jason
2020-06-22 12:49:41 +02:00
committed by Elias Naur
parent 20cf570709
commit 9cfbdafe14
7 changed files with 150 additions and 33 deletions
+11 -2
View File
@@ -41,7 +41,7 @@ __attribute__ ((visibility ("hidden"))) CFTypeRef gio_readClipboard(void);
__attribute__ ((visibility ("hidden"))) void gio_writeClipboard(unichar *chars, NSUInteger length);
__attribute__ ((visibility ("hidden"))) void gio_setNeedsDisplay(CFTypeRef viewRef);
__attribute__ ((visibility ("hidden"))) void gio_appTerminate(void);
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height);
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight);
__attribute__ ((visibility ("hidden"))) void gio_makeKeyAndOrderFront(CFTypeRef windowRef);
__attribute__ ((visibility ("hidden"))) NSPoint gio_cascadeTopLeftFromPoint(CFTypeRef windowRef, NSPoint topLeft);
__attribute__ ((visibility ("hidden"))) void gio_close(CFTypeRef windowRef);
@@ -323,12 +323,21 @@ func NewWindow(win Callbacks, opts *Options) error {
// 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))
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, title, 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,
// and just returns the offset we need for the first window.
+7 -1
View File
@@ -124,7 +124,7 @@ void gio_makeKeyAndOrderFront(CFTypeRef windowRef) {
[window makeKeyAndOrderFront:nil];
}
CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height) {
CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight) {
@autoreleasepool {
NSRect rect = NSMakeRect(0, 0, width, height);
NSUInteger styleMask = NSTitledWindowMask |
@@ -136,6 +136,12 @@ CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width,
styleMask:styleMask
backing:NSBackingStoreBuffered
defer:NO];
if (minWidth > 0 || minHeight > 0) {
window.contentMinSize = NSMakeSize(minWidth, minHeight);
}
if (maxWidth > 0 || maxHeight > 0) {
window.contentMaxSize = NSMakeSize(maxWidth, maxHeight);
}
[window setAcceptsMouseMovedEvents:YES];
window.title = [NSString stringWithUTF8String: title];
NSView *view = (__bridge NSView *)viewRef;
+49 -1
View File
@@ -27,6 +27,16 @@ import (
"gioui.org/io/system"
)
type winConstraints struct {
minWidth, minHeight int32
maxWidth, maxHeight int32
}
type winDeltas struct {
width int32
height int32
}
type window struct {
hwnd syscall.Handle
hdc syscall.Handle
@@ -39,6 +49,10 @@ type window struct {
mu sync.Mutex
animating bool
minmax winConstraints
deltas winDeltas
opts *Options
}
const _WM_REDRAW = windows.WM_USER + 0
@@ -137,6 +151,15 @@ func initResources() error {
return nil
}
func getWindowConstraints(cfg unit.Metric, opts *Options, d winDeltas) winConstraints {
var minmax winConstraints
minmax.minWidth = int32(cfg.Px(opts.MinWidth)) + d.width
minmax.minHeight = int32(cfg.Px(opts.MinHeight)) + d.height
minmax.maxWidth = int32(cfg.Px(opts.MaxWidth)) + d.width
minmax.maxHeight = int32(cfg.Px(opts.MaxHeight)) + d.height
return minmax
}
func createNativeWindow(opts *Options) (*window, error) {
var resErr error
resources.once.Do(func() {
@@ -152,7 +175,14 @@ func createNativeWindow(opts *Options) (*window, error) {
}
dwStyle := uint32(windows.WS_OVERLAPPEDWINDOW)
dwExStyle := uint32(windows.WS_EX_APPWINDOW | windows.WS_EX_WINDOWEDGE)
deltas := winDeltas{
width: wr.Right,
height: wr.Bottom,
}
windows.AdjustWindowRectEx(&wr, dwStyle, 0, dwExStyle)
deltas.width = wr.Right - wr.Left - deltas.width
deltas.height = wr.Bottom - wr.Top - deltas.height
hwnd, err := windows.CreateWindowEx(dwExStyle,
resources.class,
opts.Title,
@@ -168,7 +198,10 @@ func createNativeWindow(opts *Options) (*window, error) {
return nil, err
}
w := &window{
hwnd: hwnd,
hwnd: hwnd,
minmax: getWindowConstraints(cfg, opts, deltas),
deltas: deltas,
opts: opts,
}
w.hdc, err = windows.GetDC(hwnd)
if err != nil {
@@ -251,6 +284,20 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
case windows.SIZE_MAXIMIZED, windows.SIZE_RESTORED:
w.setStage(system.StageRunning)
}
case windows.WM_GETMINMAXINFO:
mm := (*windows.MinMaxInfo)(unsafe.Pointer(uintptr(lParam)))
if w.minmax.minWidth > 0 || w.minmax.minHeight > 0 {
mm.PtMinTrackSize = windows.Point{
w.minmax.minWidth,
w.minmax.minHeight,
}
}
if w.minmax.maxWidth > 0 || w.minmax.maxHeight > 0 {
mm.PtMaxTrackSize = windows.Point{
w.minmax.maxWidth,
w.minmax.maxHeight,
}
}
}
return windows.DefWindowProc(hwnd, msg, wParam, lParam)
@@ -375,6 +422,7 @@ func (w *window) draw(sync bool) {
return
}
cfg := configForDC()
w.minmax = getWindowConstraints(cfg, w.opts, w.deltas)
w.w.Event(FrameEvent{
FrameEvent: system.FrameEvent{
Now: time.Now(),
+15
View File
@@ -514,6 +514,21 @@ func newX11Window(gioWin Callbacks, opts *Options) error {
hints.flags = C.InputHint
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))
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))
shints.flags = shints.flags | C.PMaxSize
}
if shints.flags != 0 {
C.XSetWMNormalHints(dpy, win, &shints)
}
name := C.CString(filepath.Base(os.Args[0]))
defer C.free(unsafe.Pointer(name))
wmhints := C.XClassHint{name, name}
+4 -2
View File
@@ -14,8 +14,10 @@ import (
)
type Options struct {
Width, Height unit.Value
Title string
Width, Height unit.Value
MinWidth, MinHeight unit.Value
MaxWidth, MaxHeight unit.Value
Title string
}
type FrameEvent struct {
+36 -27
View File
@@ -46,6 +46,14 @@ type Point struct {
X, Y int32
}
type MinMaxInfo struct {
PtReserved Point
PtMaxSize Point
PtMaxPosition Point
PtMinTrackSize Point
PtMaxTrackSize Point
}
const (
CS_HREDRAW = 0x0002
CS_VREDRAW = 0x0001
@@ -120,33 +128,34 @@ const (
UNICODE_NOCHAR = 65535
WM_CANCELMODE = 0x001F
WM_CHAR = 0x0102
WM_CREATE = 0x0001
WM_DPICHANGED = 0x02E0
WM_DESTROY = 0x0002
WM_ERASEBKGND = 0x0014
WM_KEYDOWN = 0x0100
WM_KEYUP = 0x0101
WM_LBUTTONDOWN = 0x0201
WM_LBUTTONUP = 0x0202
WM_MBUTTONDOWN = 0x0207
WM_MBUTTONUP = 0x0208
WM_MOUSEMOVE = 0x0200
WM_MOUSEWHEEL = 0x020A
WM_PAINT = 0x000F
WM_CLOSE = 0x0010
WM_QUIT = 0x0012
WM_SETFOCUS = 0x0007
WM_KILLFOCUS = 0x0008
WM_SHOWWINDOW = 0x0018
WM_SIZE = 0x0005
WM_SYSKEYDOWN = 0x0104
WM_RBUTTONDOWN = 0x0204
WM_RBUTTONUP = 0x0205
WM_TIMER = 0x0113
WM_UNICHAR = 0x0109
WM_USER = 0x0400
WM_CANCELMODE = 0x001F
WM_CHAR = 0x0102
WM_CREATE = 0x0001
WM_DPICHANGED = 0x02E0
WM_DESTROY = 0x0002
WM_ERASEBKGND = 0x0014
WM_KEYDOWN = 0x0100
WM_KEYUP = 0x0101
WM_LBUTTONDOWN = 0x0201
WM_LBUTTONUP = 0x0202
WM_MBUTTONDOWN = 0x0207
WM_MBUTTONUP = 0x0208
WM_MOUSEMOVE = 0x0200
WM_MOUSEWHEEL = 0x020A
WM_PAINT = 0x000F
WM_CLOSE = 0x0010
WM_QUIT = 0x0012
WM_SETFOCUS = 0x0007
WM_KILLFOCUS = 0x0008
WM_SHOWWINDOW = 0x0018
WM_SIZE = 0x0005
WM_SYSKEYDOWN = 0x0104
WM_RBUTTONDOWN = 0x0204
WM_RBUTTONUP = 0x0205
WM_TIMER = 0x0113
WM_UNICHAR = 0x0109
WM_USER = 0x0400
WM_GETMINMAXINFO = 0x0024
WS_CLIPCHILDREN = 0x00010000
WS_CLIPSIBLINGS = 0x04000000
+28
View File
@@ -427,4 +427,32 @@ func Size(w, h unit.Value) Option {
}
}
// MaxSize sets the maximum size of the window.
func MaxSize(w, h unit.Value) Option {
if w.V <= 0 {
panic("width must be larger than or equal to 0")
}
if h.V <= 0 {
panic("height must be larger than or equal to 0")
}
return func(opts *window.Options) {
opts.MaxWidth = w
opts.MaxHeight = h
}
}
// MinSize sets the minimum size of the window.
func MinSize(w, h unit.Value) Option {
if w.V <= 0 {
panic("width must be larger than or equal to 0")
}
if h.V <= 0 {
panic("height must be larger than or equal to 0")
}
return func(opts *window.Options) {
opts.MinWidth = w
opts.MinHeight = h
}
}
func (driverEvent) ImplementsEvent() {}