doodle/pkg/windows/layers_window.go
Noah Petherbridge 190d4be1b6 Layer Selection Window for Doodad Editor
* When editing a doodad in the Editor Mode, the toolbar has a "Lyr."
  button that opens the Layers window.
* The Layers window allows switching the active doodad layer that you
  are drawing on, as well as create and rename layers.
* With this feature, Doodads may be fully drawn in-game, including
  adding alternate named layers for animations and multiple-state
  doodads.
* Update the Pager component to have a configurable MaxPageButtons.
  Controls that have more pages than this limit will stop having buttons
  drawn after the limit. The "Forward" and "Next" buttons can still
  navigate into the extra pages.
* Refactored and centralized the various popup windows in Editor Mode
  into editor_ui_popups.go; the SetupPopups() and various methods such
  as ShowPaletteWindow() and ShowDoodadDropper() make management of
  popups simple for the editor_ui!
* The Menu Bar in Editor Mode now has context-specific tools in the
  Tools menu: the Doodad Dropper for levels and Layers for doodads.
* Bugfix the Palette Editor window to work equally between Levels and
  Doodads, by only having it care about the Palette and not the Level
  that owns it.
2020-11-16 23:23:21 -08:00

297 lines
6.1 KiB
Go

package windows
import (
"fmt"
"math"
"git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/doodads"
"git.kirsle.net/apps/doodle/pkg/log"
"git.kirsle.net/apps/doodle/pkg/shmem"
"git.kirsle.net/go/render"
"git.kirsle.net/go/ui"
)
// Layers shows the layers when editing a doodad file.
type Layers struct {
Supervisor *ui.Supervisor
Engine render.Engine
// Pointer to the currently edited level.
EditDoodad *doodads.Doodad
ActiveLayer int // pointer to selected layer
activeLayer string // cached string for radio button
// Callback functions.
OnChange func(*doodads.Doodad) // Doodad data was modified, reload the Canvas etc.
OnAddLayer func() // "Add Layer" button was clicked
OnCancel func() // Close button was clicked.
// Editor should change the active layer
OnChangeLayer func(index int)
}
// NewLayerWindow initializes the window.
func NewLayerWindow(config Layers) *ui.Window {
// Default options.
var (
title = "Layers"
rows = []*ui.Frame{}
// size of the popup window
width = 320
height = 300
// Column sizes of the palette table.
col1 = 40 // Index
col3 = 120 // Name
col4 = 60 // Edit button
// col5 = 150 // Delete
// pagination values
page = 1
perPage = 5
)
config.activeLayer = fmt.Sprintf("%d", config.ActiveLayer)
log.Warn("config.activeLayer=%s", config.activeLayer)
window := ui.NewWindow(title)
window.SetButtons(ui.CloseButton)
window.Configure(ui.Config{
Width: width,
Height: height,
Background: render.Grey,
})
frame := ui.NewFrame("Window Body Frame")
window.Pack(frame, ui.Pack{
Side: ui.N,
Fill: true,
Expand: true,
})
log.Info("SETUP PALETTE WINDOW")
// Draw the header row.
headers := []struct {
Name string
Size int
}{
{"Index", col1},
{"Name", col3},
{"Edit", col4},
}
header := ui.NewFrame("Header")
for _, col := range headers {
labelFrame := ui.NewFrame(col.Name)
labelFrame.Configure(ui.Config{
Width: col.Size,
Height: 24,
})
label := ui.NewLabel(ui.Label{
Text: col.Name,
Font: balance.MenuFontBold,
})
labelFrame.Pack(label, ui.Pack{
Side: ui.N,
})
header.Pack(labelFrame, ui.Pack{
Side: ui.W,
Padding: 2,
})
}
header.Compute(config.Engine)
frame.Pack(header, ui.Pack{
Side: ui.N,
})
// Draw the rows for each Layer in the given doodad.
if doodad := config.EditDoodad; doodad != nil {
for i, _ := range doodad.Layers {
i := i // rescope
var idStr = fmt.Sprintf("%d", i)
row := ui.NewFrame("Layer " + idStr)
rows = append(rows, row)
// Off the end of the first page?
if i >= perPage {
row.Hide()
}
// ID label.
idLabel := ui.NewLabel(ui.Label{
Text: idStr + ".",
Font: balance.MenuFont,
})
idLabel.Configure(ui.Config{
Width: col1,
Height: 24,
})
// Name button (click to rename the swatch)
btnName := ui.NewButton("Name", ui.NewLabel(ui.Label{
TextVariable: &doodad.Layers[i].Name,
}))
btnName.Configure(ui.Config{
Width: col3,
Height: 24,
})
btnName.Handle(ui.Click, func(ed ui.EventData) error {
shmem.Prompt("New layer name ["+doodad.Layers[i].Name+"]: ", func(answer string) {
log.Warn("Answer: %s", answer)
if answer != "" {
doodad.Layers[i].Name = answer
if config.OnChange != nil {
config.OnChange(config.EditDoodad)
}
}
})
return nil
})
config.Supervisor.Add(btnName)
// Edit button (open layer for editing)
// btnEdit := ui.NewButton("Edit", ui.NewLabel(ui.Label{
// Text: "Edit",
// }))
btnEdit := ui.NewRadioButton("Edit",
&config.activeLayer, idStr, ui.NewLabel(ui.Label{
Text: "Edit",
}))
btnEdit.Configure(ui.Config{
Width: col4,
Height: 24,
})
btnEdit.Handle(ui.Click, func(ed ui.EventData) error {
if config.OnChangeLayer != nil {
config.OnChangeLayer(i)
}
return nil
})
config.Supervisor.Add(btnEdit)
// Pack all the widgets.
row.Pack(idLabel, ui.Pack{
Side: ui.W,
PadX: 2,
})
row.Pack(btnName, ui.Pack{
Side: ui.W,
PadX: 2,
})
row.Pack(btnEdit, ui.Pack{
Side: ui.W,
PadX: 2,
})
row.Compute(config.Engine)
frame.Pack(row, ui.Pack{
Side: ui.N,
PadY: 2,
})
}
}
{
/******************
* Confirm/cancel buttons.
******************/
bottomFrame := ui.NewFrame("Button Frame")
frame.Pack(bottomFrame, ui.Pack{
Side: ui.S,
FillX: true,
})
// Pager for the doodads.
pager := ui.NewPager(ui.Pager{
Name: "Layers Window Pager",
Page: page,
Pages: int(math.Ceil(
float64(len(rows)) / float64(perPage),
)),
PerPage: perPage,
MaxPageButtons: 10,
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 this page.
var (
minRow = (page - 1) * perPage
visible = 0
)
for i, row := range rows {
if visible >= perPage {
row.Hide()
continue
}
if i < minRow {
row.Hide()
} else {
row.Show()
visible++
}
}
},
})
pager.Compute(config.Engine)
pager.Supervise(config.Supervisor)
bottomFrame.Place(pager, ui.Place{
Top: 20,
Left: 20,
})
btnFrame := ui.NewFrame("Window Buttons")
var buttons = []struct {
Label string
F func(ui.EventData) error
}{
{"Add Layer", func(ed ui.EventData) error {
if config.OnAddLayer != nil {
config.OnAddLayer()
}
if config.OnChange != nil {
config.OnChange(config.EditDoodad)
}
return nil
}},
{"Close", func(ed ui.EventData) error {
if config.OnCancel != nil {
config.OnCancel()
}
return nil
}},
}
for _, t := range buttons {
btn := ui.NewButton(t.Label, ui.NewLabel(ui.Label{
Text: t.Label,
Font: balance.MenuFont,
}))
btn.Handle(ui.Click, t.F)
btn.Compute(config.Engine)
config.Supervisor.Add(btn)
btnFrame.Pack(btn, ui.Pack{
Side: ui.W,
PadX: 4,
})
}
bottomFrame.Place(btnFrame, ui.Place{
Top: 60,
Center: true,
})
}
window.Hide()
return window
}