Level Thumbnails on Story Mode Select
* Rework the Story Mode UI to display level thumbnails. * Responsive UI: defaults to wide screen mode and shows 3 levels horizontally but on narrow/mobile display, shows 2 levels per page in portrait. * Add "Tiny" screenshot size (224x126) to fit the Story Mode UI. * Make the pager buttons bigger and more touchable. * Maximize the game window on startup unless the -w option with a specific window resolution is provided.
This commit is contained in:
parent
9cce93f431
commit
1a9706c09f
|
@ -3,7 +3,6 @@ package main
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"regexp"
|
||||
"runtime"
|
||||
|
@ -47,9 +46,6 @@ func init() {
|
|||
// Use all the CPU cores for collision detection and other load balanced
|
||||
// goroutine work in the app.
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
|
||||
// Seed the random number generator.
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
@ -154,10 +150,12 @@ func main() {
|
|||
}
|
||||
|
||||
// Setting a custom resolution?
|
||||
var maximize = true
|
||||
if c.String("window") != "" {
|
||||
if err := setResolution(c.String("window")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
maximize = false
|
||||
}
|
||||
|
||||
// Enable feature flags?
|
||||
|
@ -200,6 +198,12 @@ func main() {
|
|||
game := doodle.New(c.Bool("debug"), engine)
|
||||
game.SetupEngine()
|
||||
|
||||
// Start with maximized window unless -w was given.
|
||||
if maximize {
|
||||
log.Info("Maximize window")
|
||||
engine.Maximize()
|
||||
}
|
||||
|
||||
// Reload usercfg - if their settings.json doesn't exist, we try and pick a
|
||||
// default "hide touch hints" based on touch device presence - which is only
|
||||
// known after SetupEngine.
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//go:build shareware
|
||||
// +build shareware
|
||||
|
||||
package balance
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//go:build !shareware
|
||||
// +build !shareware
|
||||
|
||||
package balance
|
||||
|
|
|
@ -21,10 +21,6 @@ var (
|
|||
Width = 1024
|
||||
Height = 768
|
||||
|
||||
// Title screen height needed for the main menu. Phones in landscape
|
||||
// mode will switch to the horizontal layout if less than this height.
|
||||
TitleScreenResponsiveHeight = 600
|
||||
|
||||
// Speed to scroll a canvas with arrow keys in Edit Mode.
|
||||
CanvasScrollSpeed = 8
|
||||
FollowActorMaxScrollSpeed = 64
|
||||
|
@ -158,9 +154,11 @@ var (
|
|||
LevelScreenshotLargeFilename = "large.png"
|
||||
LevelScreenshotMediumFilename = "medium.png"
|
||||
LevelScreenshotSmallFilename = "small.png"
|
||||
LevelScreenshotTinyFilename = "tiny.png"
|
||||
LevelScreenshotLargeSize = render.NewRect(1280, 720)
|
||||
LevelScreenshotMediumSize = render.NewRect(640, 360)
|
||||
LevelScreenshotSmallSize = render.NewRect(320, 180)
|
||||
LevelScreenshotSmallSize = render.NewRect(320, 180) // Level Properties thumbnail size
|
||||
LevelScreenshotTinySize = render.NewRect(224, 126) // Story Mode thumbnail size
|
||||
)
|
||||
|
||||
// Edit Mode Values
|
||||
|
|
36
pkg/balance/responsive.go
Normal file
36
pkg/balance/responsive.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package balance
|
||||
|
||||
/*
|
||||
Responsive breakpoints and dimensions for Sketchy Maze.
|
||||
|
||||
Ideas for breakpoints (copying web CSS frameworks):
|
||||
- Mobile up to 768px
|
||||
- Tablet from 769px
|
||||
- Desktop from 1024px
|
||||
- Widescreen from 1216px
|
||||
- FullHD from 1408px
|
||||
*/
|
||||
const (
|
||||
// Title screen height needed for the main menu. Phones in landscape
|
||||
// mode will switch to the horizontal layout if less than this height.
|
||||
TitleScreenResponsiveHeight = 600
|
||||
|
||||
BreakpointMobile = 0 // 0-768
|
||||
BreakpointTablet = 769 // from 769
|
||||
BreakpointDesktop = 1024 // from 1024
|
||||
BreakpointWidescreen = 1216
|
||||
BreakpointFullHD = 1408
|
||||
)
|
||||
|
||||
// IsShortWide is a custom responsive breakpoint to mimic the mobile app in landscape mode like on a Pinephone.
|
||||
//
|
||||
// Parameters are the width and height of the application window (usually the screen if maximized).
|
||||
//
|
||||
// It is used on the MainScene to decide whether the main menu is drawn tall or wide.
|
||||
func IsShortWide(width, height int) bool {
|
||||
return height < TitleScreenResponsiveHeight
|
||||
}
|
||||
|
||||
func IsBreakpointTablet(width, height int) bool {
|
||||
return width >= BreakpointTablet
|
||||
}
|
|
@ -110,6 +110,7 @@ var (
|
|||
MenuFont = render.Text{
|
||||
Size: 12,
|
||||
PadX: 4,
|
||||
PadY: 2,
|
||||
}
|
||||
MenuFontBold = render.Text{
|
||||
FontFilename: SansBoldFont,
|
||||
|
@ -124,6 +125,14 @@ var (
|
|||
PadY: 4,
|
||||
}
|
||||
|
||||
// Pager styles.
|
||||
PagerLargeFont = render.Text{
|
||||
FontFilename: SansBoldFont,
|
||||
Size: 14,
|
||||
PadX: 6,
|
||||
PadY: 4,
|
||||
}
|
||||
|
||||
// Modal backdrop color.
|
||||
ModalBackdrop = render.RGBA(1, 1, 1, 42)
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@ EditFile opens a drawing file (Level or Doodad) in the EditorScene.
|
|||
|
||||
The filename can be one of the following:
|
||||
|
||||
- A simple filename with no path separators in it and/or no file extension.
|
||||
- An absolute path beginning with "/"
|
||||
- A relative path beginning with "./"
|
||||
- A simple filename with no path separators in it and/or no file extension.
|
||||
- An absolute path beginning with "/"
|
||||
- A relative path beginning with "./"
|
||||
|
||||
If the filename has an extension (`.level` or `.doodad`), that will disambiguate
|
||||
how to find the file and which mode to start the EditorMode in. Otherwise, the
|
||||
|
|
|
@ -7,12 +7,12 @@ Stroke holds temporary pixel data with a shape and color.
|
|||
|
||||
It is used for myriad purposes:
|
||||
|
||||
- As a staging area for drawing new pixels to the drawing without committing
|
||||
them until completed.
|
||||
- As a unit of work for the Undo/Redo History when editing a drawing.
|
||||
- As imaginary visual lines superimposed on top of a drawing, for example to
|
||||
visualize the link between two doodads or to draw collision hitboxes and other
|
||||
debug lines to the drawing.
|
||||
- As a staging area for drawing new pixels to the drawing without committing
|
||||
them until completed.
|
||||
- As a unit of work for the Undo/Redo History when editing a drawing.
|
||||
- As imaginary visual lines superimposed on top of a drawing, for example to
|
||||
visualize the link between two doodads or to draw collision hitboxes and other
|
||||
debug lines to the drawing.
|
||||
*/
|
||||
type Stroke struct {
|
||||
ID int // Unique ID per each stroke
|
||||
|
|
|
@ -133,7 +133,7 @@ func SaveCroppedScreenshot(level *level.Level, viewport render.Rect) (string, er
|
|||
// UpdateLevelScreenshots will generate and embed the screenshot PNGs into the level data.
|
||||
func UpdateLevelScreenshots(lvl *level.Level, scroll render.Point) error {
|
||||
// Take screenshots.
|
||||
large, medium, small, err := CreateLevelScreenshots(lvl, scroll)
|
||||
large, medium, small, tiny, err := CreateLevelScreenshots(lvl, scroll)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -143,6 +143,7 @@ func UpdateLevelScreenshots(lvl *level.Level, scroll render.Point) error {
|
|||
balance.LevelScreenshotLargeFilename: large,
|
||||
balance.LevelScreenshotMediumFilename: medium,
|
||||
balance.LevelScreenshotSmallFilename: small,
|
||||
balance.LevelScreenshotTinyFilename: tiny,
|
||||
} {
|
||||
var fh = bytes.NewBuffer([]byte{})
|
||||
if err := png.Encode(fh, img); err != nil {
|
||||
|
@ -165,7 +166,7 @@ func UpdateLevelScreenshots(lvl *level.Level, scroll render.Point) error {
|
|||
// will be embedded within the level data itself.
|
||||
//
|
||||
// Returns the large, medium and small images.
|
||||
func CreateLevelScreenshots(lvl *level.Level, scroll render.Point) (large, medium, small image.Image, err error) {
|
||||
func CreateLevelScreenshots(lvl *level.Level, scroll render.Point) (large, medium, small, tiny image.Image, err error) {
|
||||
// Viewport to screenshot.
|
||||
viewport := render.Rect{
|
||||
X: scroll.X,
|
||||
|
@ -183,7 +184,8 @@ func CreateLevelScreenshots(lvl *level.Level, scroll render.Point) (large, mediu
|
|||
// Scale the medium and small versions.
|
||||
medium = Scale(large, image.Rect(0, 0, balance.LevelScreenshotMediumSize.W, balance.LevelScreenshotMediumSize.H), draw.ApproxBiLinear)
|
||||
small = Scale(large, image.Rect(0, 0, balance.LevelScreenshotSmallSize.W, balance.LevelScreenshotSmallSize.H), draw.ApproxBiLinear)
|
||||
return large, medium, small, nil
|
||||
tiny = Scale(large, image.Rect(0, 0, balance.LevelScreenshotTinySize.W, balance.LevelScreenshotTinySize.H), draw.ApproxBiLinear)
|
||||
return large, medium, small, tiny, nil
|
||||
}
|
||||
|
||||
// Scale down an image. Example:
|
||||
|
|
|
@ -9,10 +9,10 @@ by its index number.
|
|||
|
||||
This function calls the following:
|
||||
|
||||
* Chunker.Inflate(Palette) to update references to the level's pixels to point
|
||||
to the Swatch entry.
|
||||
* Actors.Inflate()
|
||||
* Palette.Inflate() to load private instance values for the palette subsystem.
|
||||
- Chunker.Inflate(Palette) to update references to the level's pixels to point
|
||||
to the Swatch entry.
|
||||
- Actors.Inflate()
|
||||
- Palette.Inflate() to load private instance values for the palette subsystem.
|
||||
*/
|
||||
func (l *Level) Inflate() {
|
||||
// Inflate the chunk metadata to map the pixels to their palette indexes.
|
||||
|
|
|
@ -497,12 +497,11 @@ func (s *MainScene) Resized(width, height int) {
|
|||
log.Info("Resized to %dx%d", width, height)
|
||||
|
||||
// If the height is not tall enough for the menu, switch to the horizontal layout.
|
||||
if height < balance.TitleScreenResponsiveHeight {
|
||||
log.Error("Switch to landscape mode")
|
||||
s.landscapeMode = true
|
||||
} else {
|
||||
s.landscapeMode = false
|
||||
isLandscape := balance.IsShortWide(width, height)
|
||||
if isLandscape != s.landscapeMode {
|
||||
log.Info("Toggled LandscapeMode to: %+v", isLandscape)
|
||||
}
|
||||
s.landscapeMode = isLandscape
|
||||
|
||||
s.canvas.Resize(render.Rect{
|
||||
W: width,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//go:build !js
|
||||
// +build !js
|
||||
|
||||
package native
|
||||
|
@ -14,8 +15,8 @@ import (
|
|||
// OpenURL opens a web browser to the given URL.
|
||||
//
|
||||
// On Linux this will look for xdg-open or try a few common browser names.
|
||||
// On Windows this uses the ``start`` command.
|
||||
// On MacOS this uses the ``open`` command.
|
||||
// On Windows this uses the “start“ command.
|
||||
// On MacOS this uses the “open“ command.
|
||||
func OpenURL(url string) {
|
||||
if runtime.GOOS == "windows" {
|
||||
go windowsOpenURL(url)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//go:build js && wasm
|
||||
// +build js,wasm
|
||||
|
||||
package native
|
||||
|
|
|
@ -109,3 +109,10 @@ func TextToImage(e render.Engine, text render.Text) (image.Image, error) {
|
|||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// Set the window to maximized.
|
||||
func MaximizeWindow(e render.Engine) {
|
||||
if sdl, ok := e.(*sdl.Renderer); ok {
|
||||
sdl.Maximize()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,3 +25,5 @@ func CopyToClipboard(text string) error {
|
|||
func CountTextures(e render.Engine) string {
|
||||
return "n/a"
|
||||
}
|
||||
|
||||
func MaximizeWindow(e render.Engine) {}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//go:build js && wasm
|
||||
// +build js,wasm
|
||||
|
||||
package native
|
||||
|
@ -17,4 +18,3 @@ func OpenFile(title string, filter string) (string, error) {
|
|||
})
|
||||
return "", nil
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//go:build !js
|
||||
// +build !js
|
||||
|
||||
package native
|
||||
|
|
|
@ -7,8 +7,10 @@ RegisterEventHooks attaches the supervisor level event hooks into a JS VM.
|
|||
|
||||
Names registered:
|
||||
|
||||
- EndLevel(): for a doodad to exit the level. Panics if the OnLevelExit
|
||||
handler isn't defined.
|
||||
- EndLevel(): for a doodad to exit the level. Panics if the OnLevelExit
|
||||
handler isn't defined.
|
||||
- FailLevel(): for a doodad to cause a level failure.
|
||||
- SetCheckpoint(): update the player's respawn location.
|
||||
*/
|
||||
func RegisterEventHooks(s *Supervisor, vm *VM) {
|
||||
vm.Set("EndLevel", func() {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
//+build js,wasm
|
||||
//go:build js && wasm
|
||||
// +build js,wasm
|
||||
|
||||
package sound
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package wallpaper
|
||||
|
||||
import (
|
||||
"os"
|
||||
"io/ioutil"
|
||||
"encoding/base64"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//go:build !js
|
||||
// +build !js
|
||||
|
||||
package wasm
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//go:build js && wasm
|
||||
// +build js,wasm
|
||||
|
||||
package wasm
|
||||
|
|
|
@ -24,11 +24,16 @@ type LevelPack struct {
|
|||
OnCloseWindow func()
|
||||
|
||||
// Internal variables
|
||||
isLandscape bool // wide window rather than tall
|
||||
window *ui.Window
|
||||
tabFrame *ui.TabFrame
|
||||
savegame *savegame.SaveGame
|
||||
goldSprite *ui.Image
|
||||
silverSprite *ui.Image
|
||||
|
||||
// Button frames for the footer: one with Back+Close, other with Close only.
|
||||
footerWithBackButton *ui.Frame
|
||||
footerWithCloseButton *ui.Frame
|
||||
}
|
||||
|
||||
// NewLevelPackWindow initializes the window.
|
||||
|
@ -37,11 +42,18 @@ func NewLevelPackWindow(config LevelPack) *ui.Window {
|
|||
var (
|
||||
title = "Select a Level"
|
||||
|
||||
// size of the popup window
|
||||
// size of the popup window (vertical)
|
||||
width = 320
|
||||
height = 360
|
||||
height = 540
|
||||
)
|
||||
|
||||
// Are we horizontal?
|
||||
if balance.IsBreakpointTablet(config.Engine.WindowSize()) {
|
||||
width = 720
|
||||
height = 360
|
||||
config.isLandscape = true
|
||||
}
|
||||
|
||||
// Get the available .levelpack files.
|
||||
lpFiles, packmap, err := levelpack.LoadAllAvailable()
|
||||
if err != nil {
|
||||
|
@ -70,6 +82,15 @@ func NewLevelPackWindow(config LevelPack) *ui.Window {
|
|||
Height: height,
|
||||
Background: render.Grey,
|
||||
})
|
||||
window.Handle(ui.CloseWindow, func(ed ui.EventData) error {
|
||||
if config.OnCloseWindow != nil {
|
||||
// fn := config.OnCloseWindow
|
||||
// config.OnCloseWindow = nil
|
||||
// fn()
|
||||
config.OnCloseWindow()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
config.window = window
|
||||
|
||||
frame := ui.NewFrame("Window Body Frame")
|
||||
|
@ -99,6 +120,8 @@ func NewLevelPackWindow(config LevelPack) *ui.Window {
|
|||
config.makeIndexScreen(indexTab, width, height, lpFiles, packmap, func(screen string) {
|
||||
// Callback for user choosing a level pack.
|
||||
// Hide the index screen and show the screen for this pack.
|
||||
config.footerWithBackButton.Show()
|
||||
config.footerWithCloseButton.Hide()
|
||||
tabFrame.SetTab(screen)
|
||||
})
|
||||
for _, filename := range lpFiles {
|
||||
|
@ -109,20 +132,65 @@ func NewLevelPackWindow(config LevelPack) *ui.Window {
|
|||
config.makeDetailScreen(tab, width, height, packmap[filename])
|
||||
}
|
||||
|
||||
// Close button.
|
||||
// Button toolbar at the bottom (Back, Close)
|
||||
config.footerWithBackButton = ui.NewFrame("Button Bar w/ Back Button")
|
||||
config.footerWithCloseButton = ui.NewFrame("Button Bar w/ Close Button Only")
|
||||
window.Place(config.footerWithBackButton, ui.Place{
|
||||
Bottom: 15,
|
||||
Center: true,
|
||||
})
|
||||
window.Place(config.footerWithCloseButton, ui.Place{
|
||||
Bottom: 15,
|
||||
Center: true,
|
||||
})
|
||||
|
||||
// Back button hidden by default.
|
||||
config.footerWithBackButton.Hide()
|
||||
|
||||
// Back button (conditionally visible)
|
||||
backButton := ui.NewButton("Back", ui.NewLabel(ui.Label{
|
||||
Text: "« Back",
|
||||
Font: balance.MenuFont,
|
||||
}))
|
||||
backButton.SetStyle(&balance.ButtonBabyBlue)
|
||||
backButton.Handle(ui.Click, func(ed ui.EventData) error {
|
||||
tabFrame.SetTab("LevelPacks")
|
||||
config.footerWithBackButton.Hide()
|
||||
config.footerWithCloseButton.Show()
|
||||
return nil
|
||||
})
|
||||
config.Supervisor.Add(backButton)
|
||||
config.footerWithBackButton.Pack(backButton, ui.Pack{
|
||||
Side: ui.W,
|
||||
PadX: 4,
|
||||
})
|
||||
|
||||
// Close button (on both versions of the footer frame).
|
||||
if config.OnCloseWindow != nil {
|
||||
closeBtn := ui.NewButton("Close Window", ui.NewLabel(ui.Label{
|
||||
Text: "Close",
|
||||
Font: balance.MenuFont,
|
||||
}))
|
||||
closeBtn.Handle(ui.Click, func(ed ui.EventData) error {
|
||||
config.OnCloseWindow()
|
||||
return nil
|
||||
// Create two copies of the button, so we can parent one to each footer frame.
|
||||
makeCloseButton := func() *ui.Button {
|
||||
closeBtn := ui.NewButton("Close Window", ui.NewLabel(ui.Label{
|
||||
Text: "Close",
|
||||
Font: balance.MenuFont,
|
||||
}))
|
||||
closeBtn.Handle(ui.Click, func(ed ui.EventData) error {
|
||||
config.OnCloseWindow()
|
||||
return nil
|
||||
})
|
||||
config.Supervisor.Add(closeBtn)
|
||||
return closeBtn
|
||||
}
|
||||
var (
|
||||
button1 = makeCloseButton()
|
||||
button2 = makeCloseButton()
|
||||
)
|
||||
|
||||
// Add it to both frames.
|
||||
config.footerWithBackButton.Pack(button1, ui.Pack{
|
||||
Side: ui.W,
|
||||
})
|
||||
config.Supervisor.Add(closeBtn)
|
||||
window.Place(closeBtn, ui.Place{
|
||||
Bottom: 15,
|
||||
Center: true,
|
||||
config.footerWithCloseButton.Pack(button2, ui.Pack{
|
||||
Side: ui.W,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -235,7 +303,7 @@ func (config LevelPack) makeIndexScreen(frame *ui.Frame, width, height int,
|
|||
Pages: pages,
|
||||
PerPage: perPage,
|
||||
MaxPageButtons: maxPageButtons,
|
||||
Font: balance.MenuFont,
|
||||
Font: balance.PagerLargeFont,
|
||||
OnChange: func(newPage, perPage int) {
|
||||
page = newPage
|
||||
log.Info("Page: %d, %d", page, perPage)
|
||||
|
@ -271,18 +339,32 @@ func (config LevelPack) makeIndexScreen(frame *ui.Frame, width, height int,
|
|||
// Detail screen for a given levelpack.
|
||||
func (config LevelPack) makeDetailScreen(frame *ui.Frame, width, height int, lp *levelpack.LevelPack) *ui.Frame {
|
||||
var (
|
||||
buttonHeight = 40
|
||||
buttonWidth = width - 40
|
||||
|
||||
page = 1
|
||||
perPage = 4
|
||||
perPage = 2 // 2 for tall mobile, 3 for landscape
|
||||
pages = int(
|
||||
math.Ceil(
|
||||
float64(len(lp.Levels)) / float64(perPage),
|
||||
),
|
||||
)
|
||||
maxPageButtons = 10
|
||||
|
||||
buttonHeight = 172
|
||||
buttonWidth = 230
|
||||
thumbnailName = balance.LevelScreenshotTinyFilename
|
||||
thumbnailPadY = 46
|
||||
)
|
||||
if config.isLandscape {
|
||||
perPage = 3
|
||||
pages = int(
|
||||
math.Ceil(
|
||||
float64(len(lp.Levels)) / float64(perPage),
|
||||
),
|
||||
)
|
||||
buttonHeight = 172
|
||||
thumbnailName = balance.LevelScreenshotTinyFilename
|
||||
thumbnailPadY = 46
|
||||
buttonWidth = (width / perPage) - 16 // pixel-pushing
|
||||
}
|
||||
|
||||
// Load the padlock icon for locked levels.
|
||||
// If not loadable, won't be used in UI.
|
||||
|
@ -294,42 +376,13 @@ func (config LevelPack) makeDetailScreen(frame *ui.Frame, width, height int, lp
|
|||
numUnlocked = lp.FreeLevels + numCompleted
|
||||
)
|
||||
|
||||
/** Back Button */
|
||||
backButton := ui.NewButton("Back", ui.NewLabel(ui.Label{
|
||||
Text: "< Back",
|
||||
Font: ui.MenuFont,
|
||||
}))
|
||||
backButton.SetStyle(&balance.ButtonBabyBlue)
|
||||
backButton.Handle(ui.Click, func(ed ui.EventData) error {
|
||||
config.tabFrame.SetTab("LevelPacks")
|
||||
return nil
|
||||
})
|
||||
config.Supervisor.Add(backButton)
|
||||
frame.Pack(backButton, ui.Pack{
|
||||
Side: ui.NE,
|
||||
PadY: 2,
|
||||
PadX: 6,
|
||||
})
|
||||
|
||||
// Spacer: the back button is position NW and the rest against N
|
||||
// so may overlap.
|
||||
spacer := ui.NewFrame("Spacer")
|
||||
spacer.Configure(ui.Config{
|
||||
Width: 64,
|
||||
Height: 30,
|
||||
})
|
||||
frame.Pack(spacer, ui.Pack{
|
||||
Side: ui.N,
|
||||
})
|
||||
|
||||
// LevelPack Title label
|
||||
label := ui.NewLabel(ui.Label{
|
||||
Text: lp.Title,
|
||||
Font: balance.LabelFont,
|
||||
})
|
||||
frame.Pack(label, ui.Pack{
|
||||
Side: ui.NW,
|
||||
PadX: 8,
|
||||
Side: ui.N,
|
||||
PadY: 2,
|
||||
})
|
||||
|
||||
|
@ -359,12 +412,36 @@ func (config LevelPack) makeDetailScreen(frame *ui.Frame, width, height int, lp
|
|||
})
|
||||
}
|
||||
|
||||
// Arranging the buttons into groups of 3, vertical or horizontal.
|
||||
var packDir = ui.Pack{
|
||||
Side: ui.N,
|
||||
PadY: 2,
|
||||
}
|
||||
if config.isLandscape {
|
||||
packDir = ui.Pack{
|
||||
Side: ui.W,
|
||||
PadX: 2,
|
||||
}
|
||||
}
|
||||
buttonRow := ui.NewFrame("Level Buttons")
|
||||
frame.Pack(buttonRow, ui.Pack{
|
||||
Side: ui.N,
|
||||
PadY: 4,
|
||||
})
|
||||
|
||||
// Loop over all the levels in this pack.
|
||||
var buttons []*ui.Button
|
||||
for i, level := range lp.Levels {
|
||||
level := level
|
||||
score := config.savegame.GetLevelScore(lp.Filename, level.Filename, level.UUID)
|
||||
|
||||
// Load the level zip for its thumbnail image.
|
||||
lvl, err := lp.GetLevel(level.Filename)
|
||||
if err != nil {
|
||||
log.Error("Couldn't GetLevel(%s) from LevelPack %s: %s", level.Filename, lp.Filename, err)
|
||||
lvl = nil
|
||||
}
|
||||
|
||||
// Make a frame to hold a complex button layout.
|
||||
btnFrame := ui.NewFrame("Frame")
|
||||
btnFrame.Resize(render.Rect{
|
||||
|
@ -464,6 +541,16 @@ func (config LevelPack) makeDetailScreen(frame *ui.Frame, width, height int, lp
|
|||
})
|
||||
}
|
||||
|
||||
// Level screenshot.
|
||||
if lvl != nil {
|
||||
if img, err := lvl.GetScreenshotImageAsUIImage(thumbnailName); err == nil {
|
||||
btnFrame.Pack(img, ui.Pack{
|
||||
Side: ui.N,
|
||||
PadY: thumbnailPadY, // TODO: otherwise it overlaps the other labels :(
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
btn := ui.NewButton(level.Filename, btnFrame)
|
||||
btn.Handle(ui.Click, func(ed ui.EventData) error {
|
||||
// Is this level locked?
|
||||
|
@ -484,10 +571,7 @@ func (config LevelPack) makeDetailScreen(frame *ui.Frame, width, height int, lp
|
|||
return nil
|
||||
})
|
||||
|
||||
frame.Pack(btn, ui.Pack{
|
||||
Side: ui.N,
|
||||
PadY: 2,
|
||||
})
|
||||
buttonRow.Pack(btn, packDir)
|
||||
config.Supervisor.Add(btn)
|
||||
|
||||
if i > perPage-1 {
|
||||
|
@ -502,7 +586,7 @@ func (config LevelPack) makeDetailScreen(frame *ui.Frame, width, height int, lp
|
|||
Pages: pages,
|
||||
PerPage: perPage,
|
||||
MaxPageButtons: maxPageButtons,
|
||||
Font: balance.MenuFont,
|
||||
Font: balance.PagerLargeFont,
|
||||
OnChange: func(newPage, perPage int) {
|
||||
page = newPage
|
||||
log.Info("Page: %d, %d", page, perPage)
|
||||
|
|
Loading…
Reference in New Issue
Block a user