doodle/pkg/windows/publish_level.go

329 lines
6.6 KiB
Go
Raw Normal View History

package windows
import (
"fmt"
"math"
"sort"
"strings"
"git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/doodads"
"git.kirsle.net/apps/doodle/pkg/level"
"git.kirsle.net/apps/doodle/pkg/log"
"git.kirsle.net/go/render"
"git.kirsle.net/go/ui"
)
// Publish window.
type Publish struct {
// Settings passed in by doodle
Supervisor *ui.Supervisor
Engine render.Engine
Level *level.Level
OnPublish func()
OnCancel func()
// Private vars.
includeBuiltins bool // show built-in doodads in checkbox-list.
}
// NewPublishWindow initializes the window.
func NewPublishWindow(cfg Publish) *ui.Window {
var (
windowWidth = 400
windowHeight = 300
page = 1
perPage = 4
pages = 1
maxPageButtons = 8
// columns and sizes to draw the doodad list
columns = 3
btnWidth = 120
btnHeight = 14
)
window := ui.NewWindow("Publish Level")
window.SetButtons(ui.CloseButton)
window.Configure(ui.Config{
Width: windowWidth,
Height: windowHeight,
Background: render.RGBA(200, 200, 255, 255),
})
/////////////
// Intro text
introFrame := ui.NewFrame("Intro Frame")
window.Pack(introFrame, ui.Pack{
Side: ui.N,
FillX: true,
})
lines := []struct {
Text string
Font render.Text
}{
{
Text: "About",
Font: balance.LabelFont,
},
{
Text: "Share your level easily! If you are using custom doodads in\n" +
"your level, you may attach them directly to your\n" +
"level file -- so it can easily run on another computer!",
Font: balance.UIFont,
},
{
Text: "List of Doodads in Your Level",
Font: balance.LabelFont,
},
}
for n, row := range lines {
frame := ui.NewFrame(fmt.Sprintf("Intro Line %d", n))
introFrame.Pack(frame, ui.Pack{
Side: ui.N,
FillX: true,
})
label := ui.NewLabel(ui.Label{
Text: row.Text,
Font: row.Font,
})
frame.Pack(label, ui.Pack{
Side: ui.W,
})
}
/////////////
// Custom Doodads checkbox-list.
doodadFrame := ui.NewFrame("Doodads Frame")
doodadFrame.Resize(render.Rect{
W: windowWidth,
H: btnHeight*perPage + 100,
})
window.Pack(doodadFrame, ui.Pack{
Side: ui.N,
FillX: true,
})
// First, the checkbox to show built-in doodads or not.
builtinRow := ui.NewFrame("Show Builtins Frame")
doodadFrame.Pack(builtinRow, ui.Pack{
Side: ui.N,
FillX: true,
})
builtinCB := ui.NewCheckbox("Show Builtins", &cfg.includeBuiltins, ui.NewLabel(ui.Label{
Text: "Attach built-in* doodads too",
Font: balance.UIFont,
}))
builtinCB.Supervise(cfg.Supervisor)
builtinRow.Pack(builtinCB, ui.Pack{
Side: ui.W,
PadX: 2,
})
// Collect all the doodad names in use in this level.
unique := map[string]interface{}{}
names := []string{}
if cfg.Level != nil {
for _, actor := range cfg.Level.Actors {
if _, ok := unique[actor.Filename]; ok {
continue
}
unique[actor.Filename] = nil
names = append(names, actor.Filename)
}
}
sort.Strings(names)
// Identify which of the doodads are built-ins.
usedBuiltins := []string{}
builtinMap := map[string]interface{}{}
usedCustom := []string{}
if builtins, err := doodads.ListBuiltin(); err == nil {
for _, filename := range builtins {
if _, ok := unique[filename]; ok {
usedBuiltins = append(usedBuiltins, filename)
builtinMap[filename] = nil
}
}
}
for _, name := range names {
if _, ok := builtinMap[name]; ok {
continue
}
usedCustom = append(usedCustom, name)
}
// Helper function to draw the button rows for a set of doodads.
mkDoodadRows := func(filenames []string, builtin bool) []*ui.Frame {
var (
curRow *ui.Frame // = ui.NewFrame("mkDoodadRows 0")
frames = []*ui.Frame{}
)
for i, name := range filenames {
if i%columns == 0 {
curRow = ui.NewFrame(fmt.Sprintf("mkDoodadRows %d", i))
frames = append(frames, curRow)
}
font := balance.UIFont
if builtin {
font.Color = render.Blue
name += "*"
}
btn := ui.NewLabel(ui.Label{
Text: strings.Replace(name, ".doodad", "", 1),
Font: font,
})
btn.Configure(ui.Config{
Width: btnWidth,
Height: btnHeight,
})
curRow.Pack(btn, ui.Pack{
Side: ui.W,
PadX: 2,
PadY: 2,
})
}
return frames
}
// 1. Draw the built-in doodads in use.
var (
btnRows = []*ui.Frame{}
builtinRows = []*ui.Frame{}
customRows = []*ui.Frame{}
)
if len(names) > 0 {
customRows = mkDoodadRows(usedCustom, false)
btnRows = append(btnRows, customRows...)
}
if len(usedBuiltins) > 0 {
builtinRows = mkDoodadRows(usedBuiltins, true)
btnRows = append(btnRows, builtinRows...)
}
for i, row := range btnRows {
doodadFrame.Pack(row, ui.Pack{
Side: ui.N,
FillX: true,
})
// Hide if too long for 1st page.
if i >= perPage {
row.Hide()
}
}
/////////////
// Buttons at bottom of window
bottomFrame := ui.NewFrame("Button Frame")
window.Pack(bottomFrame, ui.Pack{
Side: ui.S,
FillX: true,
})
// Pager for the doodads.
pages = int(
math.Ceil(
float64(len(btnRows)) / float64(perPage),
),
)
pagerOnChange := 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 btnRows {
if visible >= perPage {
row.Hide()
continue
}
if i < minRow {
row.Hide()
} else {
row.Show()
visible++
}
}
}
pager := ui.NewPager(ui.Pager{
Name: "Doodads List Pager",
Page: page,
Pages: pages,
PerPage: perPage,
MaxPageButtons: maxPageButtons,
Font: balance.MenuFont,
OnChange: pagerOnChange,
})
pager.Compute(cfg.Engine)
pager.Supervise(cfg.Supervisor)
bottomFrame.Place(pager, ui.Place{
Top: 20,
Left: 20,
})
frame := ui.NewFrame("Button frame")
buttons := []struct {
label string
primary bool
f func()
}{
{"Export Level", true, func() {
if cfg.OnPublish != nil {
cfg.OnPublish()
}
}},
{"Close", false, func() {
if cfg.OnCancel != nil {
cfg.OnCancel()
}
}},
}
for _, button := range buttons {
button := button
btn := ui.NewButton(button.label, ui.NewLabel(ui.Label{
Text: button.label,
Font: balance.MenuFont,
}))
if button.primary {
btn.SetStyle(&balance.ButtonPrimary)
}
btn.Handle(ui.Click, func(ed ui.EventData) error {
button.f()
return nil
})
btn.Compute(cfg.Engine)
cfg.Supervisor.Add(btn)
frame.Pack(btn, ui.Pack{
Side: ui.W,
PadX: 4,
Expand: true,
Fill: true,
})
}
bottomFrame.Pack(frame, ui.Pack{
Side: ui.E,
Padding: 8,
})
return window
}