doodle/pkg/windows/levelpack_open.go
Noah Petherbridge 678326540b WIP LevelPack UI + Landscape Mode Title Screen
The title screen is now responsive to landscape mode. If the window is
not tall enough to show all the menu buttons (~600px) it will switch to
a horizontal layout with the title on the left and buttons on the right.

WIP "Story Mode" button that brings up a Level Packs selection window.
2021-12-23 21:11:45 -08:00

259 lines
5.4 KiB
Go

package windows
import (
"fmt"
"math"
"git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/levelpack"
"git.kirsle.net/apps/doodle/pkg/log"
"git.kirsle.net/go/render"
"git.kirsle.net/go/ui"
)
// LevelPack window lets the user open and play a level from a pack.
type LevelPack struct {
Supervisor *ui.Supervisor
Engine render.Engine
// Callback functions.
OnPlayLevel func(levelpack, filename string)
// Internal variables
window *ui.Window
gotoIndex func() // return to index screen
}
// NewLevelPackWindow initializes the window.
func NewLevelPackWindow(config LevelPack) *ui.Window {
// Default options.
var (
title = "Select a Level"
// size of the popup window
width = 320
height = 300
)
window := ui.NewWindow(title)
window.SetButtons(ui.CloseButton)
window.Configure(ui.Config{
Width: width,
Height: height,
Background: render.Grey,
})
config.window = window
frame := ui.NewFrame("Window Body Frame")
window.Pack(frame, ui.Pack{
Side: ui.N,
Fill: true,
Expand: true,
})
// We'll divide this window into "Screens", where the default
// screen shows the available level packs and then each level
// pack gets its own screen showing its levels.
var indexScreen *ui.Frame
config.gotoIndex = func() {
indexScreen.Show()
}
indexScreen = config.makeIndexScreen(width, height, func(screen *ui.Frame) {
// Callback for user choosing a level pack.
// Hide the index screen and show the screen for this pack.
indexScreen.Hide()
screen.Show()
})
window.Pack(indexScreen, ui.Pack{
Side: ui.N,
Fill: true,
Expand: true,
})
window.Supervise(config.Supervisor)
window.Hide()
return window
}
// Index screen for the LevelPack window.
func (config LevelPack) makeIndexScreen(width, height int, onChoose func(*ui.Frame)) *ui.Frame {
var (
buttonHeight = 60 // height of each LevelPack button
buttonWidth = width - 40
// pagination values
page = 1
pages int
perPage = 3
maxPageButtons = 10
)
frame := ui.NewFrame("Index Screen")
label := ui.NewLabel(ui.Label{
Text: "Select from a Level Pack below:",
Font: balance.LabelFont,
})
frame.Pack(label, ui.Pack{
Side: ui.N,
PadX: 8,
PadY: 8,
})
// Get the available .levelpack files.
lpFiles, err := levelpack.ListFiles()
if err != nil {
log.Error("Couldn't list levelpack files: %s", err)
}
pages = int(
math.Ceil(
float64(len(lpFiles)) / float64(perPage),
),
)
var buttons []*ui.Button
for i, filename := range lpFiles {
lp, err := levelpack.LoadFile(filename)
if err != nil {
log.Error("Couldn't read %s: %s", filename, err)
continue
}
_ = lp
// Make a frame to hold a complex button layout.
btnFrame := ui.NewFrame("Frame")
btnFrame.Resize(render.Rect{
W: buttonWidth,
H: buttonHeight,
})
// Draw labels...
label := ui.NewLabel(ui.Label{
Text: lp.Title,
Font: balance.LabelFont,
})
btnFrame.Pack(label, ui.Pack{
Side: ui.N,
})
description := lp.Description
if description == "" {
description = "(No description)"
}
byline := ui.NewLabel(ui.Label{
Text: description,
Font: balance.MenuFont,
})
btnFrame.Pack(byline, ui.Pack{
Side: ui.N,
})
numLevels := ui.NewLabel(ui.Label{
Text: fmt.Sprintf("[%d levels]", len(lp.Levels)),
Font: balance.MenuFont,
})
btnFrame.Pack(numLevels, ui.Pack{
Side: ui.N,
})
// Generate the detail screen (Frame) for this level pack.
// Should the user click our button, this screen is shown.
screen := config.makeDetailScreen(width, height, lp)
screen.Hide()
config.window.Pack(screen, ui.Pack{
Side: ui.N,
Fill: true,
Expand: true,
})
button := ui.NewButton(filename, btnFrame)
button.Handle(ui.Click, func(ed ui.EventData) error {
onChoose(screen)
return nil
})
frame.Pack(button, ui.Pack{
Side: ui.N,
PadY: 2,
})
config.Supervisor.Add(button)
if i > perPage {
button.Hide()
}
buttons = append(buttons, button)
}
pager := ui.NewPager(ui.Pager{
Name: "LevelPack Pager",
Page: page,
Pages: pages,
MaxPageButtons: maxPageButtons,
Font: balance.MenuFont,
OnChange: func(newPage, perPage int) {
page = newPage
log.Info("Page: %d, %d", page, perPage)
// Re-evaluate which rows are shown/hidden for the page we're on.
var (
minRow = (page - 1) * perPage
visible = 0
)
for i, row := range buttons {
if visible >= perPage {
row.Hide()
continue
}
if i < minRow {
row.Hide()
} else {
row.Show()
visible++
}
}
},
})
pager.Compute(config.Engine)
pager.Supervise(config.Supervisor)
frame.Pack(pager, ui.Pack{
Side: ui.N,
PadY: 2,
})
return frame
}
// Detail screen for a given levelpack.
func (config LevelPack) makeDetailScreen(width, height int, lp levelpack.LevelPack) *ui.Frame {
frame := ui.NewFrame("Detail Screen")
label := ui.NewLabel(ui.Label{
Text: "HELLO " + lp.Title,
Font: balance.LabelFont,
})
frame.Pack(label, ui.Pack{
Side: ui.N,
PadX: 8,
PadY: 8,
})
backButton := ui.NewButton("Back", ui.NewLabel(ui.Label{
Text: "< Back to Level Packs",
Font: ui.MenuFont,
}))
backButton.Handle(ui.Click, func(ed ui.EventData) error {
frame.Hide()
config.gotoIndex()
return nil
})
config.Supervisor.Add(backButton)
frame.Pack(backButton, ui.Pack{
Side: ui.N,
PadY: 2,
})
return frame
}