doodle/pkg/windows/palette_editor.go
Noah Petherbridge 47cca8c7c6 Palette Editor and Doodad Dropper Windows
* Start the program window maximized with the `-w maximized` CLI option.
* Move the Doodad Palette off the right-side dock of the Editor Scene and
  into its own pop-up window: the DoodadDropper.
* Shrink the width of the Color Palette panel and show only the colors in
  the buttons. The name of the swatch is available in the mouse-over tooltip.
* Added an "Edit" button to the Color Palette. It opens a Palette Editor
  window where you can rename, change colors and attributes of existing colors
  OR insert new colors into your palette. (Deleting colors not yet supported).
* level.Chunker gets a Redraw method: invalidates all cached textures of all
  chunks forcing the level to redraw itself, possibly with an updated palette.
2020-07-09 19:38:37 -07:00

366 lines
7.5 KiB
Go

package windows
import (
"fmt"
"math"
"git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/level"
"git.kirsle.net/apps/doodle/pkg/log"
"git.kirsle.net/apps/doodle/pkg/shmem"
"git.kirsle.net/go/render"
"git.kirsle.net/go/ui"
"git.kirsle.net/go/ui/style"
)
// PaletteEditor lets you customize the level palette in Edit Mode.
type PaletteEditor struct {
Supervisor *ui.Supervisor
Engine render.Engine
// Pointer to the currently edited level.
EditLevel *level.Level
// Callback functions.
OnChange func()
OnAddColor func()
OnCancel func()
}
// NewPaletteEditor initializes the window.
func NewPaletteEditor(config PaletteEditor) *ui.Window {
// Default options.
var (
title = "Level Palette"
buttonSize = balance.DoodadButtonSize
columns = balance.DoodadDropperCols
rows = []*ui.Frame{}
// size of the popup window
width = buttonSize * columns
height = (buttonSize * balance.DoodadDropperRows) + 64 // account for button borders :(
// Column sizes of the palette table.
col1 = 30 // ID no.
col2 = 24 // Color
col3 = 120 // Name
col4 = 140 // Attributes
// col5 = 150 // Delete
// pagination values
page = 1
perPage = 5
)
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
}{
{"ID", col1},
{"Col", col2},
{"Name", col3},
{"Attributes", col4},
// {"Delete", col5},
}
header := ui.NewFrame("Palette 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 main table of Palette rows.
if level := config.EditLevel; level != nil {
for i, swatch := range level.Palette.Swatches {
var idStr = fmt.Sprintf("%d", i)
swatch := swatch
row := ui.NewFrame("Swatch " + 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: &swatch.Name,
}))
btnName.Configure(ui.Config{
Width: col3,
Height: 24,
})
btnName.Handle(ui.Click, func(ed ui.EventData) error {
shmem.Prompt("New swatch name ["+swatch.Name+"]: ", func(answer string) {
log.Warn("Answer: %s", answer)
if answer != "" {
swatch.Name = answer
if config.OnChange != nil {
config.OnChange()
}
}
})
return nil
})
config.Supervisor.Add(btnName)
// Color Choice button.
btnColor := ui.NewButton("Color", ui.NewFrame("Color Frame"))
btnColor.SetStyle(&style.Button{
Background: swatch.Color,
HoverBackground: swatch.Color.Lighten(40),
OutlineColor: render.Black,
OutlineSize: 1,
BorderStyle: style.BorderRaised,
BorderSize: 2,
})
btnColor.Configure(ui.Config{
Background: swatch.Color,
Width: col2,
Height: 24,
})
btnColor.Handle(ui.Click, func(ed ui.EventData) error {
shmem.Prompt(fmt.Sprintf(
"New color in hex notation [%s]: ", swatch.Color.ToHex()), func(answer string) {
if answer != "" {
color, err := render.HexColor(answer)
if err != nil {
shmem.Flash("Error with that color code: %s", err)
return
}
swatch.Color = color
// TODO: redundant from above, consolidate these
fmt.Printf("Set button style to: %s\n", swatch.Color)
btnColor.SetStyle(&style.Button{
Background: swatch.Color,
HoverBackground: swatch.Color.Lighten(40),
OutlineColor: render.Black,
OutlineSize: 1,
BorderStyle: style.BorderRaised,
BorderSize: 2,
})
if config.OnChange != nil {
config.OnChange()
}
}
})
return nil
})
config.Supervisor.Add(btnColor)
// Attribute flags
attrFrame := ui.NewFrame("Attributes")
attrFrame.Configure(ui.Config{
Width: col4,
Height: 24,
})
attributes := []struct {
Label string
Var *bool
}{
{
Label: "Solid",
Var: &swatch.Solid,
},
{
Label: "Fire",
Var: &swatch.Fire,
},
{
Label: "Water",
Var: &swatch.Water,
},
}
for _, attr := range attributes {
attr := attr
btn := ui.NewCheckButton(attr.Label, attr.Var, ui.NewLabel(ui.Label{
Text: attr.Label,
Font: balance.MenuFont,
}))
btn.Handle(ui.Click, func(ed ui.EventData) error {
if config.OnChange != nil {
config.OnChange()
}
return nil
})
config.Supervisor.Add(btn)
attrFrame.Pack(btn, ui.Pack{
Side: ui.W,
})
}
// Pack all the widgets.
row.Pack(idLabel, ui.Pack{
Side: ui.W,
PadX: 2,
})
row.Pack(btnColor, ui.Pack{
Side: ui.W,
PadX: 2,
})
row.Pack(btnName, ui.Pack{
Side: ui.W,
PadX: 2,
})
row.Pack(attrFrame, 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{
Page: page,
Pages: int(math.Ceil(
float64(len(rows)) / float64(perPage),
)),
PerPage: perPage,
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 Color", func(ed ui.EventData) error {
if config.OnAddColor != nil {
config.OnAddColor()
}
if config.OnChange != nil {
config.OnChange()
}
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: 20,
Right: 20,
})
}
window.Hide()
return window
}