ui: Add SetWindowResizable

This change also fixes example/windowsize.

Fixes #320
This commit is contained in:
Hajime Hoshi 2019-12-22 18:08:02 +09:00
parent 4aa80de34d
commit 049aa552d4
5 changed files with 96 additions and 28 deletions

View File

@ -50,11 +50,14 @@ var (
// * ebiten.RunGame // * ebiten.RunGame
// * ebiten.SetWindowSize // * ebiten.SetWindowSize
// * ebiten.WindowSize // * ebiten.WindowSize
//
// A resizable window is available only when flagLegacy is false.
flagLegacy = flag.Bool("legacy", false, "use the legacy API") flagLegacy = flag.Bool("legacy", false, "use the legacy API")
flagFullscreen = flag.Bool("fullscreen", false, "fullscreen") flagFullscreen = flag.Bool("fullscreen", false, "fullscreen")
flagWindowPosition = flag.String("windowposition", "", "window position (e.g., 100,200)") flagWindowPosition = flag.String("windowposition", "", "window position (e.g., 100,200)")
flagScreenTransparent = flag.Bool("screentransparent", false, "screen transparent") flagScreenTransparent = flag.Bool("screentransparent", false, "screen transparent")
flagAutoAdjusting = flag.Bool("autoadjusting", false, "make the game screen auto-adjusting")
) )
func init() { func init() {
@ -98,6 +101,10 @@ type game struct {
} }
func (g *game) Layout(outsideWidth, outsideHeight int) (int, int) { func (g *game) Layout(outsideWidth, outsideHeight int) (int, int) {
if *flagAutoAdjusting {
g.width, g.height = outsideWidth, outsideHeight
return outsideWidth, outsideHeight
}
// Ignore the outside size. This means that the offscreen is not adjusted with the outside world. // Ignore the outside size. This means that the offscreen is not adjusted with the outside world.
return g.width, g.height return g.width, g.height
} }
@ -114,9 +121,8 @@ func (g *game) Update(screen *ebiten.Image) error {
} else { } else {
screenWidth = g.width screenWidth = g.width
screenHeight = g.height screenHeight = g.height
ww, _ := ebiten.WindowSize() if ww, wh := ebiten.WindowSize(); ww > 0 && wh > 0 {
if ww > 0 { screenScale = math.Min(float64(ww)/float64(g.width), float64(wh)/float64(g.height))
screenScale = float64(ww) / float64(g.width)
} else { } else {
// ebiten.WindowSize can return (0, 0) on browsers or mobiles. // ebiten.WindowSize can return (0, 0) on browsers or mobiles.
screenScale = 1 screenScale = 1
@ -131,24 +137,30 @@ func (g *game) Update(screen *ebiten.Image) error {
decorated := ebiten.IsWindowDecorated() decorated := ebiten.IsWindowDecorated()
positionX, positionY := ebiten.WindowPosition() positionX, positionY := ebiten.WindowPosition()
transparent := ebiten.IsScreenTransparent() transparent := ebiten.IsScreenTransparent()
resizable := ebiten.IsWindowResizable()
const d = 16 const d = 16
toUpdateWindowSize := false
if ebiten.IsKeyPressed(ebiten.KeyShift) { if ebiten.IsKeyPressed(ebiten.KeyShift) {
if inpututil.IsKeyJustPressed(ebiten.KeyUp) { if inpututil.IsKeyJustPressed(ebiten.KeyUp) {
screenHeight += d screenHeight += d
toUpdateWindowSize = true
} }
if inpututil.IsKeyJustPressed(ebiten.KeyDown) { if inpututil.IsKeyJustPressed(ebiten.KeyDown) {
if 16 < screenHeight && d < screenHeight { if 16 < screenHeight && d < screenHeight {
screenHeight -= d screenHeight -= d
toUpdateWindowSize = true
} }
} }
if inpututil.IsKeyJustPressed(ebiten.KeyLeft) { if inpututil.IsKeyJustPressed(ebiten.KeyLeft) {
if 16 < screenWidth && d < screenWidth { if 16 < screenWidth && d < screenWidth {
screenWidth -= d screenWidth -= d
toUpdateWindowSize = true
} }
} }
if inpututil.IsKeyJustPressed(ebiten.KeyRight) { if inpututil.IsKeyJustPressed(ebiten.KeyRight) {
screenWidth += d screenWidth += d
toUpdateWindowSize = true
} }
} else { } else {
if inpututil.IsKeyJustPressed(ebiten.KeyUp) { if inpututil.IsKeyJustPressed(ebiten.KeyUp) {
@ -164,19 +176,18 @@ func (g *game) Update(screen *ebiten.Image) error {
positionX += d positionX += d
} }
} }
if inpututil.IsKeyJustPressed(ebiten.KeyS) { if inpututil.IsKeyJustPressed(ebiten.KeyS) && !*flagAutoAdjusting {
switch screenScale { switch {
case 0.75: case screenScale < 1:
screenScale = 1 screenScale = 1
case 1: case screenScale < 1.5:
screenScale = 1.5 screenScale = 1.5
case 1.5: case screenScale < 2:
screenScale = 2 screenScale = 2
case 2:
screenScale = 0.75
default: default:
panic("not reached") screenScale = 0.75
} }
toUpdateWindowSize = true
} }
if inpututil.IsKeyJustPressed(ebiten.KeyF) { if inpututil.IsKeyJustPressed(ebiten.KeyF) {
fullscreen = !fullscreen fullscreen = !fullscreen
@ -207,14 +218,19 @@ func (g *game) Update(screen *ebiten.Image) error {
if inpututil.IsKeyJustPressed(ebiten.KeyD) { if inpututil.IsKeyJustPressed(ebiten.KeyD) {
decorated = !decorated decorated = !decorated
} }
if inpututil.IsKeyJustPressed(ebiten.KeyR) {
resizable = !resizable
}
if *flagLegacy { if toUpdateWindowSize {
ebiten.SetScreenSize(screenWidth, screenHeight) if *flagLegacy {
ebiten.SetScreenScale(screenScale) ebiten.SetScreenSize(screenWidth, screenHeight)
} else { ebiten.SetScreenScale(screenScale)
g.width = screenWidth } else {
g.height = screenHeight g.width = screenWidth
ebiten.SetWindowSize(int(float64(screenWidth)*screenScale), int(float64(screenHeight)*screenScale)) g.height = screenHeight
ebiten.SetWindowSize(int(float64(screenWidth)*screenScale), int(float64(screenHeight)*screenScale))
}
} }
ebiten.SetFullscreen(fullscreen) ebiten.SetFullscreen(fullscreen)
ebiten.SetRunnableInBackground(runnableInBackground) ebiten.SetRunnableInBackground(runnableInBackground)
@ -223,6 +239,10 @@ func (g *game) Update(screen *ebiten.Image) error {
ebiten.SetMaxTPS(tps) ebiten.SetMaxTPS(tps)
ebiten.SetWindowDecorated(decorated) ebiten.SetWindowDecorated(decorated)
ebiten.SetWindowPosition(positionX, positionY) ebiten.SetWindowPosition(positionX, positionY)
if !*flagLegacy {
// A resizable window is available only with RunGame.
ebiten.SetWindowResizable(resizable)
}
if inpututil.IsKeyJustPressed(ebiten.KeyI) { if inpututil.IsKeyJustPressed(ebiten.KeyI) {
ebiten.SetWindowIcon([]image.Image{createRandomIconImage()}) ebiten.SetWindowIcon([]image.Image{createRandomIconImage()})
@ -252,21 +272,29 @@ func (g *game) Update(screen *ebiten.Image) error {
if t := ebiten.MaxTPS(); t != ebiten.UncappedTPS { if t := ebiten.MaxTPS(); t != ebiten.UncappedTPS {
tpsStr = fmt.Sprintf("%d", t) tpsStr = fmt.Sprintf("%d", t)
} }
var msgS string
var msgR string
if *flagLegacy {
msgS = "Press S key to change the window scale (only for desktops)\n"
} else {
msgR = "Press R key to switch the window resizable state (only for desktops)\n"
}
msg := fmt.Sprintf(`Press arrow keys to move the window msg := fmt.Sprintf(`Press arrow keys to move the window
Press shift + arrow keys to change the window size Press shift + arrow keys to change the window size
Press S key to change the window scale (only for desktops) %sPress F key to switch the fullscreen state (only for desktops)
Press F key to switch the fullscreen state (only for desktops)
Press B key to switch the run-in-background state Press B key to switch the run-in-background state
Press C key to switch the cursor visibility Press C key to switch the cursor visibility
Press I key to change the window icon (only for desktops) Press I key to change the window icon (only for desktops)
Press V key to switch vsync Press V key to switch vsync
Press T key to switch TPS (ticks per second) Press T key to switch TPS (ticks per second)
Press D key to switch the window decoration Press D key to switch the window decoration (only for desktops)
Windows Position: (%d, %d) %sWindows Position: (%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`, wx, wy, cx, cy, ebiten.CurrentTPS(), tpsStr, ebiten.CurrentFPS(), ebiten.DeviceScaleFactor()) Device Scale Factor: %0.2f`, msgS, msgR, wx, wy, cx, cy, ebiten.CurrentTPS(), tpsStr, ebiten.CurrentFPS(), ebiten.DeviceScaleFactor())
ebitenutil.DebugPrint(screen, msg) ebitenutil.DebugPrint(screen, msg)
return nil return nil
} }
@ -295,6 +323,10 @@ func main() {
w, h := ebiten.ScreenSizeInFullscreen() w, h := ebiten.ScreenSizeInFullscreen()
fmt.Printf("Screen size in fullscreen: %d, %d\n", w, h) fmt.Printf("Screen size in fullscreen: %d, %d\n", w, h)
if !*flagLegacy {
fmt.Println("Tip: With -autoadjusting flag, you can make an adjustable game screen.")
}
// Decode image from a byte slice instead of a file so that // Decode image from a byte slice instead of a file so that
// this example works in any working directory. // this example works in any working directory.
// If you want to use a file, there are some options: // If you want to use a file, there are some options:
@ -325,6 +357,12 @@ func main() {
if *flagFullscreen { if *flagFullscreen {
ebiten.SetFullscreen(true) ebiten.SetFullscreen(true)
} }
if *flagAutoAdjusting {
if *flagLegacy {
log.Println("-autoadjusting flag cannot work with -legacy flag")
}
ebiten.SetWindowResizable(true)
}
const title = "Window Size (Ebiten Demo)" const title = "Window Size (Ebiten Demo)"
if *flagLegacy { if *flagLegacy {

View File

@ -552,10 +552,14 @@ func (u *UserInterface) SetWindowResizable(resizable bool) {
u.setInitWindowResizable(resizable) u.setInitWindowResizable(resizable)
return return
} }
_ = u.t.Call(func() error {
panic("glfw: SetWindowResizable can't be called after the main loop so far.") v := glfw.False
if resizable {
// TODO: Now SetAttrib doesn't exist on GLFW 3.2. Revisit later (#556). v = glfw.True
}
u.window.SetAttrib(glfw.Resizable, v)
return nil
})
} }
func (u *UserInterface) DeviceScaleFactor() float64 { func (u *UserInterface) DeviceScaleFactor() float64 {

3
run.go
View File

@ -153,6 +153,9 @@ func IsRunningSlowly() bool {
// //
// Don't call Run twice or more in one process. // Don't call Run twice or more in one process.
func Run(f func(*Image) error, width, height int, scale float64, title string) error { func Run(f func(*Image) error, width, height int, scale float64, title string) error {
if IsWindowResizable() {
panic("ebiten: a resizable window works with RunGame, not Run")
}
game := &defaultGame{ game := &defaultGame{
update: (&imageDumper{f: f}).update, update: (&imageDumper{f: f}).update,
width: width, width: width,

View File

@ -184,6 +184,18 @@ func (c *uiContext) updateOffscreen() {
// scale. This is fine since ebiten.ScreenScale will be deprecated. // scale. This is fine since ebiten.ScreenScale will be deprecated.
} }
func (c *uiContext) setWindowResizable(resizable bool) {
c.m.Lock()
defer c.m.Unlock()
if resizable && c.game != nil {
if _, ok := c.game.(*defaultGame); ok {
panic("ebiten: a resizable window works with RunGame, not Run")
}
}
uiDriver().SetWindowResizable(resizable)
}
func (c *uiContext) screenScale() float64 { func (c *uiContext) screenScale() float64 {
if c.offscreen == nil { if c.offscreen == nil {
return 0 return 0

View File

@ -62,13 +62,24 @@ func setWindowResizable(resizable bool) {
uiDriver().SetWindowResizable(resizable) uiDriver().SetWindowResizable(resizable)
} }
// IsWindowResizable reports whether the window is resizable. // IsWindowResizable reports whether the window is resizable by the user's dragging on desktops.
// On the other environments, IsWindowResizable always returns false.
// //
// IsWindowResizable is concurrent-safe. // IsWindowResizable is concurrent-safe.
func IsWindowResizable() bool { func IsWindowResizable() bool {
return uiDriver().IsWindowResizable() return uiDriver().IsWindowResizable()
} }
// SetWindowResizable sets whether the window is resizable by the user's dragging on desktops.
// On the other environments, SetWindowResizable does nothing.
//
// If SetWindowResizable is called with true and Run is used, SetWindowResizable panics. Use RunGame instead.
//
// SetWindowResizable is concurrent-safe.
func SetWindowResizable(resizable bool) {
theUIContext.setWindowResizable(resizable)
}
// SetWindowTitle sets the title of the window. // SetWindowTitle sets the title of the window.
// //
// SetWindowTitle does nothing on mobiles. // SetWindowTitle does nothing on mobiles.