ebiten: Add {Set,}WindowSizeLimits

Closes #1385
This commit is contained in:
Hajime Hoshi 2021-04-17 02:27:04 +09:00
parent 5255a54e20
commit 9b6ba5ed2c
7 changed files with 170 additions and 10 deletions

View File

@ -25,6 +25,7 @@ import (
"log" "log"
"math" "math"
"math/rand" "math/rand"
"regexp"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -45,6 +46,8 @@ var (
flagMaximize = flag.Bool("maximize", false, "maximize the window") flagMaximize = flag.Bool("maximize", false, "maximize the window")
flagVsync = flag.Bool("vsync", true, "enable vsync") flagVsync = flag.Bool("vsync", true, "enable vsync")
flagInitFocused = flag.Bool("initfocused", true, "whether the window is focused on start") flagInitFocused = flag.Bool("initfocused", true, "whether the window is focused on start")
flagMinWindowSize = flag.String("minwindowsize", "", "minimum window size (e.g., 100x200)")
flagMaxWindowSize = flag.String("maxwindowsize", "", "maximium window size (e.g., 1920x1080)")
) )
func init() { func init() {
@ -280,6 +283,7 @@ func (g *game) Draw(screen *ebiten.Image) {
screen.DrawImage(gophersImage, op) screen.DrawImage(gophersImage, op)
wx, wy := ebiten.WindowPosition() wx, wy := ebiten.WindowPosition()
minw, minh, maxw, maxh := ebiten.WindowSizeLimits()
cx, cy := ebiten.CursorPosition() cx, cy := ebiten.CursorPosition()
tpsStr := "Uncapped" tpsStr := "Uncapped"
if t := ebiten.MaxTPS(); t != ebiten.UncappedTPS { if t := ebiten.MaxTPS(); t != ebiten.UncappedTPS {
@ -319,10 +323,11 @@ func (g *game) Draw(screen *ebiten.Image) {
%s %s
IsFocused?: %s IsFocused?: %s
Windows Position: (%d, %d) Windows Position: (%d, %d)
Window size limitation: (%d, %d) - (%d, %d)
Cursor: (%d, %d) Cursor: (%d, %d)
TPS: Current: %0.2f / Max: %s TPS: Current: %0.2f / Max: %s
FPS: %0.2f FPS: %0.2f
Device Scale Factor: %0.2f`, msgM, msgR, fg, wx, wy, cx, cy, ebiten.CurrentTPS(), tpsStr, ebiten.CurrentFPS(), ebiten.DeviceScaleFactor()) Device Scale Factor: %0.2f`, msgM, msgR, fg, wx, wy, minw, minh, maxw, maxh, cx, cy, ebiten.CurrentTPS(), tpsStr, ebiten.CurrentFPS(), ebiten.DeviceScaleFactor())
ebitenutil.DebugPrint(screen, msg) ebitenutil.DebugPrint(screen, msg)
} }
@ -400,6 +405,21 @@ func main() {
ebiten.SetRunnableOnUnfocused(true) ebiten.SetRunnableOnUnfocused(true)
} }
minw, minh, maxw, maxh := -1, -1, -1, -1
reSize := regexp.MustCompile(`^(\d+)x(\d+)$`)
if m := reSize.FindStringSubmatch(*flagMinWindowSize); m != nil {
minw, _ = strconv.Atoi(m[1])
minh, _ = strconv.Atoi(m[2])
}
if m := reSize.FindStringSubmatch(*flagMaxWindowSize); m != nil {
maxw, _ = strconv.Atoi(m[1])
maxh, _ = strconv.Atoi(m[2])
}
if minw >= 0 && minh >= 0 && maxw >= 0 && maxh >= 0 {
ebiten.SetWindowSizeLimits(minw, minh, maxw, maxh)
ebiten.SetWindowResizable(true)
}
const title = "Window Size (Ebiten Demo)" const title = "Window Size (Ebiten Demo)"
ww := int(float64(g.width) * initScreenScale) ww := int(float64(g.width) * initScreenScale)
wh := int(float64(g.height) * initScreenScale) wh := int(float64(g.height) * initScreenScale)

View File

@ -78,6 +78,8 @@ type Window interface {
Size() (int, int) Size() (int, int)
SetSize(width, height int) SetSize(width, height int)
SizeLimits() (minw, minh, maxw, maxh int)
SetSizeLimits(minw, minh, maxw, maxh int)
IsFloating() bool IsFloating() bool
SetFloating(floating bool) SetFloating(floating bool)

View File

@ -34,8 +34,9 @@ type (
) )
const ( const (
False = 0 DontCare = -1
True = 1 False = 0
True = 1
) )
const ( const (

View File

@ -109,10 +109,6 @@ func (w *Window) GetAttrib(attrib Hint) int {
return w.w.GetAttrib(glfw.Hint(attrib)) return w.w.GetAttrib(glfw.Hint(attrib))
} }
func (w *Window) SetAttrib(attrib Hint, value int) {
w.w.SetAttrib(glfw.Hint(attrib), value)
}
func (w *Window) GetCursorPos() (x, y float64) { func (w *Window) GetCursorPos() (x, y float64) {
return w.w.GetCursorPos() return w.w.GetCursorPos()
} }
@ -161,6 +157,10 @@ func (w *Window) Restore() {
w.w.Restore() w.w.Restore()
} }
func (w *Window) SetAttrib(attrib Hint, value int) {
w.w.SetAttrib(glfw.Hint(attrib), value)
}
func (w *Window) SetCharModsCallback(cbfun CharModsCallback) (previous CharModsCallback) { func (w *Window) SetCharModsCallback(cbfun CharModsCallback) (previous CharModsCallback) {
var gcb glfw.CharModsCallback var gcb glfw.CharModsCallback
if cbfun != nil { if cbfun != nil {
@ -215,6 +215,10 @@ func (w *Window) SetSizeCallback(cbfun SizeCallback) (previous SizeCallback) {
return prev return prev
} }
func (w *Window) SetSizeLimits(minw, minh, maxw, maxh int) {
w.w.SetSizeLimits(minw, minh, maxw, maxh)
}
func (w *Window) SetIcon(images []image.Image) { func (w *Window) SetIcon(images []image.Image) {
w.w.SetIcon(images) w.w.SetIcon(images)
} }

View File

@ -54,8 +54,12 @@ type UserInterface struct {
// windowWidth and windowHeight represents a window size. // windowWidth and windowHeight represents a window size.
// The unit is device-dependent pixels. // The unit is device-dependent pixels.
windowWidth int windowWidth int
windowHeight int windowHeight int
minWindowWidth int
minWindowHeight int
maxWindowWidth int
maxWindowHeight int
running uint32 running uint32
toChangeSize bool toChangeSize bool
@ -110,6 +114,10 @@ const (
var ( var (
theUI = &UserInterface{ theUI = &UserInterface{
runnableOnUnfocused: true, runnableOnUnfocused: true,
minWindowWidth: glfw.DontCare,
minWindowHeight: glfw.DontCare,
maxWindowWidth: glfw.DontCare,
maxWindowHeight: glfw.DontCare,
origPosX: invalidPos, origPosX: invalidPos,
origPosY: invalidPos, origPosY: invalidPos,
initVsync: true, initVsync: true,
@ -235,6 +243,25 @@ func (u *UserInterface) setRunning(running bool) {
} }
} }
func (u *UserInterface) getWindowSizeLimits() (minw, minh, maxw, maxh int) {
u.m.RLock()
defer u.m.RUnlock()
return u.minWindowWidth, u.minWindowHeight, u.maxWindowWidth, u.maxWindowHeight
}
func (u *UserInterface) setWindowSizeLimits(minw, minh, maxw, maxh int) bool {
u.m.RLock()
defer u.m.RUnlock()
if u.minWindowWidth == minw && u.minWindowHeight == minh && u.maxWindowWidth == maxw && u.maxWindowHeight == maxh {
return false
}
u.minWindowWidth = minw
u.minWindowHeight = minh
u.maxWindowWidth = maxw
u.maxWindowHeight = maxh
return true
}
func (u *UserInterface) getInitTitle() string { func (u *UserInterface) getInitTitle() string {
u.m.RLock() u.m.RLock()
v := u.initTitle v := u.initTitle
@ -782,6 +809,8 @@ func (u *UserInterface) init() error {
setPosition() setPosition()
} }
u.updateWindowSizeLimits()
// Maximizing a window requires a proper size and position. Call Maximize here (#1117). // Maximizing a window requires a proper size and position. Call Maximize here (#1117).
if u.isInitWindowMaximized() { if u.isInitWindowMaximized() {
u.window.Maximize() u.window.Maximize()
@ -973,8 +1002,45 @@ func (u *UserInterface) swapBuffers() {
} }
} }
// updateWindowSizeLimits must be called from the main thread.
func (u *UserInterface) updateWindowSizeLimits() {
minw, minh, maxw, maxh := u.getWindowSizeLimits()
if minw < 0 {
minw = glfw.DontCare
}
if minh < 0 {
minh = glfw.DontCare
}
if maxw < 0 {
maxw = glfw.DontCare
}
if maxh < 0 {
maxh = glfw.DontCare
}
u.window.SetSizeLimits(minw, minh, maxw, maxh)
}
func (u *UserInterface) adjustWindowSizeBasedOnSizeLimits(width, height int) (int, int) {
minw, minh, maxw, maxh := u.getWindowSizeLimits()
if minw >= 0 && width < minw {
width = minw
}
if minh >= 0 && height < minh {
height = minh
}
if maxw >= 0 && width > maxw {
width = maxw
}
if maxh >= 0 && height > maxh {
height = maxh
}
return width, height
}
// setWindowSize must be called from the main thread. // setWindowSize must be called from the main thread.
func (u *UserInterface) setWindowSize(width, height int, fullscreen bool) { func (u *UserInterface) setWindowSize(width, height int, fullscreen bool) {
width, height = u.adjustWindowSizeBasedOnSizeLimits(width, height)
if u.windowWidth == width && u.windowHeight == height && u.isFullscreen() == fullscreen && u.lastDeviceScaleFactor == u.deviceScaleFactor() { if u.windowWidth == width && u.windowHeight == height && u.isFullscreen() == fullscreen && u.lastDeviceScaleFactor == u.deviceScaleFactor() {
return return
} }
@ -1045,6 +1111,8 @@ func (u *UserInterface) setWindowSize(width, height int, fullscreen bool) {
// TODO: This should return an error. // TODO: This should return an error.
panic(fmt.Sprintf("glfw: failed to recreate window: %v", err)) panic(fmt.Sprintf("glfw: failed to recreate window: %v", err))
} }
// Reset the size limits explicitly.
u.updateWindowSizeLimits()
u.window.Show() u.window.Show()
windowRecreated = true windowRecreated = true
} }

View File

@ -228,7 +228,8 @@ func (w *window) setPosition(x, y int) {
func (w *window) Size() (int, int) { func (w *window) Size() (int, int) {
if !w.ui.isRunning() { if !w.ui.isRunning() {
return w.ui.getInitWindowSize() ww, wh := w.ui.getInitWindowSize()
return w.ui.adjustWindowSizeBasedOnSizeLimits(ww, wh)
} }
ww := int(w.ui.fromGLFWPixel(float64(w.ui.windowWidth))) ww := int(w.ui.fromGLFWPixel(float64(w.ui.windowWidth)))
wh := int(w.ui.fromGLFWPixel(float64(w.ui.windowHeight))) wh := int(w.ui.fromGLFWPixel(float64(w.ui.windowHeight)))
@ -248,6 +249,49 @@ func (w *window) SetSize(width, height int) {
}) })
} }
func (w *window) SizeLimits() (minw, minh, maxw, maxh int) {
minw, minh, maxw, maxh = w.ui.getWindowSizeLimits()
if minw >= 0 {
minw = int(w.ui.fromGLFWPixel(float64(minw)))
}
if minh >= 0 {
minh = int(w.ui.fromGLFWPixel(float64(minh)))
}
if maxw >= 0 {
maxw = int(w.ui.fromGLFWPixel(float64(maxw)))
}
if maxh >= 0 {
maxh = int(w.ui.fromGLFWPixel(float64(maxh)))
}
return
}
func (w *window) SetSizeLimits(minw, minh, maxw, maxh int) {
if minw >= 0 {
minw = int(w.ui.toGLFWPixel(float64(minw)))
}
if minh >= 0 {
minh = int(w.ui.toGLFWPixel(float64(minh)))
}
if maxw >= 0 {
maxw = int(w.ui.toGLFWPixel(float64(maxw)))
}
if maxh >= 0 {
maxh = int(w.ui.toGLFWPixel(float64(maxh)))
}
if !w.ui.setWindowSizeLimits(minw, minh, maxw, maxh) {
return
}
if !w.ui.isRunning() {
return
}
_ = w.ui.t.Call(func() error {
w.ui.updateWindowSizeLimits()
return nil
})
}
func (w *window) SetIcon(iconImages []image.Image) { func (w *window) SetIcon(iconImages []image.Image) {
// The icons are actually set at (*UserInterface).loop. // The icons are actually set at (*UserInterface).loop.
w.ui.setIconImages(iconImages) w.ui.setIconImages(iconImages)

View File

@ -235,6 +235,27 @@ func SetWindowSize(width, height int) {
} }
} }
// WindowSizeLimist returns the limitation of the window size on desktops.
// A negative value indicates the size is not limited.
//
// WindowMaxSize is concurrent-safe.
func WindowSizeLimits() (minw, minh, maxw, maxh int) {
if w := uiDriver().Window(); w != nil {
return w.SizeLimits()
}
return -1, -1, -1, -1
}
// SetWindowSizeLimits sets the limitation of the window size on desktops.
// A negative value indicates the size is not limited.
//
// SetWindowMaxSize is concurrent-safe.
func SetWindowSizeLimits(minw, minh, maxw, maxh int) {
if w := uiDriver().Window(); w != nil {
w.SetSizeLimits(minw, minh, maxw, maxh)
}
}
// IsWindowFloating reports whether the window is always shown above all the other windows. // IsWindowFloating reports whether the window is always shown above all the other windows.
// //
// IsWindowFloating returns false on browsers and mobiles. // IsWindowFloating returns false on browsers and mobiles.