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.
This commit is contained in:
parent
5f75168235
commit
47cca8c7c6
|
@ -3,7 +3,6 @@ package main
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"runtime"
|
||||
|
@ -15,6 +14,7 @@ import (
|
|||
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||
"git.kirsle.net/apps/doodle/pkg/bindata"
|
||||
"git.kirsle.net/apps/doodle/pkg/branding"
|
||||
"git.kirsle.net/apps/doodle/pkg/log"
|
||||
"git.kirsle.net/apps/doodle/pkg/sound"
|
||||
"git.kirsle.net/go/render/sdl"
|
||||
"github.com/urfave/cli"
|
||||
|
@ -71,7 +71,7 @@ func main() {
|
|||
&cli.StringFlag{
|
||||
Name: "window",
|
||||
Aliases: []string{"w"},
|
||||
Usage: "set the window size (e.g. -w 1024x768) or special value: desktop, mobile, landscape",
|
||||
Usage: "set the window size (e.g. -w 1024x768) or special value: desktop, mobile, landscape, maximized",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "guitest",
|
||||
|
@ -127,6 +127,13 @@ func main() {
|
|||
game.PlayLevel(filename)
|
||||
}
|
||||
}
|
||||
|
||||
// Maximizing the window? with `-w maximized`
|
||||
if c.String("window") == "maximized" {
|
||||
log.Info("Maximize main window")
|
||||
engine.Maximize()
|
||||
}
|
||||
|
||||
game.Run()
|
||||
return nil
|
||||
}
|
||||
|
@ -136,13 +143,13 @@ func main() {
|
|||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
log.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func setResolution(value string) error {
|
||||
switch value {
|
||||
case "desktop":
|
||||
case "desktop", "maximized":
|
||||
return nil
|
||||
case "mobile":
|
||||
balance.Width = 375
|
||||
|
|
|
@ -23,7 +23,7 @@ var (
|
|||
}
|
||||
TitleFont = render.Text{
|
||||
FontFilename: "DejaVuSans-Bold.ttf",
|
||||
Size: 12,
|
||||
Size: 9,
|
||||
Padding: 4,
|
||||
Color: render.White,
|
||||
Stroke: render.Red,
|
||||
|
@ -37,6 +37,11 @@ var (
|
|||
Size: 12,
|
||||
PadX: 4,
|
||||
}
|
||||
MenuFontBold = render.Text{
|
||||
FontFilename: "DejaVuSans-Bold.ttf",
|
||||
Size: 12,
|
||||
PadX: 4,
|
||||
}
|
||||
|
||||
// StatusFont is the font for the status bar.
|
||||
StatusFont = render.Text{
|
||||
|
@ -83,4 +88,10 @@ var (
|
|||
Color: render.RGBA(255, 255, 0, 255),
|
||||
Stroke: render.RGBA(100, 100, 0, 255),
|
||||
}
|
||||
|
||||
// Doodad Dropper Window settings.
|
||||
DoodadButtonBackground = render.RGBA(255, 255, 200, 255)
|
||||
DoodadButtonSize = 64
|
||||
DoodadDropperCols = 6 // rows/columns of buttons
|
||||
DoodadDropperRows = 3
|
||||
)
|
||||
|
|
|
@ -61,6 +61,7 @@ func New(debug bool, engine render.Engine) *Doodle {
|
|||
// Make the render engine globally available. TODO: for wasm/ToBitmap
|
||||
shmem.CurrentRenderEngine = engine
|
||||
shmem.Flash = d.Flash
|
||||
shmem.Prompt = d.Prompt
|
||||
|
||||
if debug {
|
||||
log.Logger.Config.Level = golog.DebugLevel
|
||||
|
|
151
pkg/editor_ui.go
151
pkg/editor_ui.go
|
@ -20,9 +20,6 @@ import (
|
|||
"git.kirsle.net/go/ui"
|
||||
)
|
||||
|
||||
// Width of the panel frame.
|
||||
var paletteWidth = 160
|
||||
|
||||
// EditorUI manages the user interface for the Editor Scene.
|
||||
type EditorUI struct {
|
||||
d *Doodle
|
||||
|
@ -50,6 +47,8 @@ type EditorUI struct {
|
|||
// Popup windows.
|
||||
levelSettingsWindow *ui.Window
|
||||
aboutWindow *ui.Window
|
||||
doodadWindow *ui.Window
|
||||
paletteEditor *ui.Window
|
||||
|
||||
// Palette window.
|
||||
Palette *ui.Window
|
||||
|
@ -107,6 +106,9 @@ func NewEditorUI(d *Doodle, s *EditorScene) *EditorUI {
|
|||
u.ToolBar = u.SetupToolbar(d)
|
||||
u.Workspace = u.SetupWorkspace(d) // important that this is last!
|
||||
|
||||
// Preload pop-up windows before they're needed.
|
||||
u.SetupPopups(d)
|
||||
|
||||
log.Error("menu size: %s", u.MenuBar.Rect())
|
||||
u.screen.Pack(u.MenuBar, ui.Pack{
|
||||
Side: ui.N,
|
||||
|
@ -175,8 +177,6 @@ func (u *EditorUI) Resized(d *Doodle) {
|
|||
menuHeight,
|
||||
))
|
||||
u.Palette.Compute(d.Engine)
|
||||
|
||||
u.scrollDoodadFrame(0)
|
||||
}
|
||||
|
||||
var innerHeight = u.d.height - menuHeight - u.StatusBar.Size().H
|
||||
|
@ -312,6 +312,9 @@ func (u *EditorUI) Present(e render.Engine) {
|
|||
|
||||
u.screen.Present(e, render.Origin)
|
||||
|
||||
// Draw any windows being managed by Supervisor.
|
||||
u.Supervisor.Present(e)
|
||||
|
||||
// Are we dragging a Doodad canvas?
|
||||
if u.Supervisor.IsDragging() {
|
||||
if actor := u.DraggableActor; actor != nil {
|
||||
|
@ -322,9 +325,6 @@ func (u *EditorUI) Present(e render.Engine) {
|
|||
))
|
||||
}
|
||||
}
|
||||
|
||||
// Draw any windows being managed by Supervisor.
|
||||
u.Supervisor.Present(e)
|
||||
}
|
||||
|
||||
// SetupWorkspace configures the main Workspace frame that takes up the full
|
||||
|
@ -359,6 +359,7 @@ func (u *EditorUI) SetupCanvas(d *Doodle) *uix.Canvas {
|
|||
// mode when you click an existing Doodad and it "pops" out of the canvas
|
||||
// and onto the cursor to be repositioned.
|
||||
drawing.OnDragStart = func(actor *level.Actor) {
|
||||
log.Warn("drawing.OnDragStart: grab actor %s", actor)
|
||||
u.startDragActor(nil, actor)
|
||||
}
|
||||
|
||||
|
@ -384,11 +385,22 @@ func (u *EditorUI) SetupCanvas(d *Doodle) *uix.Canvas {
|
|||
// Was it an actor from the Doodad Palette?
|
||||
if actor := u.DraggableActor; actor != nil {
|
||||
log.Info("Actor is a %s", actor.doodad.Filename)
|
||||
|
||||
// The actor has been dropped so null it out.
|
||||
defer func() {
|
||||
u.DraggableActor = nil
|
||||
}()
|
||||
|
||||
if u.Scene.Level == nil {
|
||||
u.d.Flash("Can't drop doodads onto doodad drawings!")
|
||||
return nil
|
||||
}
|
||||
|
||||
// If they dropped it onto a UI window, ignore it.
|
||||
if u.Supervisor.IsPointInWindow(ed.Point) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
// Uncenter the drawing from the cursor.
|
||||
size = actor.canvas.Size()
|
||||
|
@ -530,38 +542,13 @@ func (u *EditorUI) SetupMenuBar(d *Doodle) *ui.MenuBar {
|
|||
})
|
||||
editMenu.AddSeparator()
|
||||
editMenu.AddItem("Level options", func() {
|
||||
scene, _ := d.Scene.(*EditorScene)
|
||||
log.Info("Opening the window")
|
||||
|
||||
// Open the New Level window in edit-settings mode.
|
||||
if u.levelSettingsWindow == nil {
|
||||
u.levelSettingsWindow = windows.NewAddEditLevel(windows.AddEditLevel{
|
||||
Supervisor: u.Supervisor,
|
||||
Engine: d.Engine,
|
||||
EditLevel: scene.Level,
|
||||
|
||||
OnChangePageTypeAndWallpaper: func(pageType level.PageType, wallpaper string) {
|
||||
log.Info("OnChangePageTypeAndWallpaper called: %+v, %+v", pageType, wallpaper)
|
||||
scene.Level.PageType = pageType
|
||||
scene.Level.Wallpaper = wallpaper
|
||||
u.Canvas.LoadLevel(d.Engine, scene.Level)
|
||||
},
|
||||
OnCancel: func() {
|
||||
u.levelSettingsWindow.Hide()
|
||||
},
|
||||
})
|
||||
|
||||
u.levelSettingsWindow.Compute(d.Engine)
|
||||
u.levelSettingsWindow.Supervise(u.Supervisor)
|
||||
|
||||
// Center the window.
|
||||
u.levelSettingsWindow.MoveTo(render.Point{
|
||||
X: (d.width / 2) - (u.levelSettingsWindow.Size().W / 2),
|
||||
Y: 60,
|
||||
})
|
||||
} else {
|
||||
u.levelSettingsWindow = nil
|
||||
u.SetupPopups(u.d)
|
||||
u.levelSettingsWindow.Show()
|
||||
}
|
||||
})
|
||||
|
||||
////////
|
||||
|
@ -585,6 +572,10 @@ func (u *EditorUI) SetupMenuBar(d *Doodle) *ui.MenuBar {
|
|||
toolMenu.AddItemAccel("Command shell", "Enter", func() {
|
||||
d.shell.Open = true
|
||||
})
|
||||
toolMenu.AddItemAccel("Doodads", "d", func() {
|
||||
log.Info("Open the DoodadDropper")
|
||||
u.doodadWindow.Show()
|
||||
})
|
||||
|
||||
////////
|
||||
// Help menu
|
||||
|
@ -618,6 +609,96 @@ func (u *EditorUI) SetupMenuBar(d *Doodle) *ui.MenuBar {
|
|||
return menu
|
||||
}
|
||||
|
||||
// SetupPopups preloads popup windows like the DoodadDropper.
|
||||
func (u *EditorUI) SetupPopups(d *Doodle) {
|
||||
// Common window configure function.
|
||||
var configure = func(window *ui.Window) {
|
||||
var size = window.Size()
|
||||
window.Compute(d.Engine)
|
||||
window.Supervise(u.Supervisor)
|
||||
|
||||
// Center the window.
|
||||
window.MoveTo(render.Point{
|
||||
X: (d.width / 2) - (size.W / 2),
|
||||
Y: (d.height / 2) - (size.H / 2),
|
||||
})
|
||||
}
|
||||
|
||||
// Doodad Dropper.
|
||||
if u.doodadWindow == nil {
|
||||
u.doodadWindow = windows.NewDoodadDropper(windows.DoodadDropper{
|
||||
Supervisor: u.Supervisor,
|
||||
Engine: d.Engine,
|
||||
|
||||
OnStartDragActor: u.startDragActor,
|
||||
OnCancel: func() {
|
||||
u.doodadWindow.Hide()
|
||||
},
|
||||
})
|
||||
configure(u.doodadWindow)
|
||||
}
|
||||
|
||||
// Page Settings
|
||||
if u.levelSettingsWindow == nil {
|
||||
scene, _ := d.Scene.(*EditorScene)
|
||||
|
||||
u.levelSettingsWindow = windows.NewAddEditLevel(windows.AddEditLevel{
|
||||
Supervisor: u.Supervisor,
|
||||
Engine: d.Engine,
|
||||
EditLevel: scene.Level,
|
||||
|
||||
OnChangePageTypeAndWallpaper: func(pageType level.PageType, wallpaper string) {
|
||||
log.Info("OnChangePageTypeAndWallpaper called: %+v, %+v", pageType, wallpaper)
|
||||
scene.Level.PageType = pageType
|
||||
scene.Level.Wallpaper = wallpaper
|
||||
u.Canvas.LoadLevel(d.Engine, scene.Level)
|
||||
},
|
||||
OnCancel: func() {
|
||||
u.levelSettingsWindow.Hide()
|
||||
},
|
||||
})
|
||||
configure(u.levelSettingsWindow)
|
||||
}
|
||||
|
||||
// Palette Editor.
|
||||
if u.paletteEditor == nil {
|
||||
scene, _ := d.Scene.(*EditorScene)
|
||||
|
||||
u.paletteEditor = windows.NewPaletteEditor(windows.PaletteEditor{
|
||||
Supervisor: u.Supervisor,
|
||||
Engine: d.Engine,
|
||||
EditLevel: scene.Level,
|
||||
|
||||
OnChange: func() {
|
||||
// Reload the level.
|
||||
log.Warn("RELOAD LEVEL")
|
||||
u.Canvas.LoadLevel(d.Engine, scene.Level)
|
||||
scene.Level.Chunker.Redraw()
|
||||
|
||||
// Reload the palette frame to reflect the changed data.
|
||||
u.Palette.Hide()
|
||||
u.Palette = u.SetupPalette(d)
|
||||
u.Resized(d)
|
||||
},
|
||||
OnAddColor: func() {
|
||||
// Adding a new color to the palette.
|
||||
sw := scene.Level.Palette.AddSwatch()
|
||||
log.Info("Added new palette color: %+v", sw)
|
||||
|
||||
// Awkward but... reload this very same window.
|
||||
u.paletteEditor.Hide()
|
||||
u.paletteEditor = nil
|
||||
u.SetupPopups(d)
|
||||
u.paletteEditor.Show()
|
||||
},
|
||||
OnCancel: func() {
|
||||
u.paletteEditor.Hide()
|
||||
},
|
||||
})
|
||||
configure(u.paletteEditor)
|
||||
}
|
||||
}
|
||||
|
||||
// SetupStatusBar sets up the status bar widget along the bottom of the window.
|
||||
func (u *EditorUI) SetupStatusBar(d *Doodle) *ui.Frame {
|
||||
frame := ui.NewFrame("Status Bar")
|
||||
|
|
|
@ -5,15 +5,12 @@ package doodle
|
|||
// refactor into a subpackage if EditorUI itself can ever be decoupled.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"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/apps/doodle/pkg/uix"
|
||||
"git.kirsle.net/go/render"
|
||||
"git.kirsle.net/go/ui"
|
||||
)
|
||||
|
||||
// DraggableActor is a Doodad being dragged from the Doodad palette.
|
||||
|
@ -54,225 +51,3 @@ func (u *EditorUI) startDragActor(doodad *doodads.Doodad, actor *level.Actor) {
|
|||
actor: actor,
|
||||
}
|
||||
}
|
||||
|
||||
// setupDoodadFrame configures the Doodad Palette tab for Edit Mode.
|
||||
// This is a subroutine of editor_ui.go#SetupPalette()
|
||||
//
|
||||
// Can return an error if userdir.ListDoodads() returns an error (like directory
|
||||
// not found), but it will *ALWAYS* return a valid ui.Frame -- it will just be
|
||||
// empty and uninitialized.
|
||||
func (u *EditorUI) setupDoodadFrame(e render.Engine, window *ui.Window) (*ui.Frame, error) {
|
||||
var (
|
||||
frame = ui.NewFrame("Doodad Tab")
|
||||
perRow = balance.UIDoodadsPerRow
|
||||
)
|
||||
|
||||
// Pager buttons on top of the doodad list.
|
||||
pager := ui.NewFrame("Doodad Pager")
|
||||
pager.SetBackground(render.RGBA(255, 0, 0, 20)) // TODO: if I don't set a background color,
|
||||
// this frame will light up the same color as the Link button on mouse
|
||||
// over. somewhere some memory might be shared between the recent widgets
|
||||
{
|
||||
leftBtn := ui.NewButton("Prev Page", ui.NewLabel(ui.Label{
|
||||
Text: "<",
|
||||
Font: balance.MenuFont,
|
||||
}))
|
||||
leftBtn.Handle(ui.Click, func(ed ui.EventData) error {
|
||||
u.scrollDoodadFrame(-1)
|
||||
return nil
|
||||
})
|
||||
u.Supervisor.Add(leftBtn)
|
||||
pager.Pack(leftBtn, ui.Pack{
|
||||
Side: ui.W,
|
||||
})
|
||||
|
||||
scroller := ui.NewFrame("Doodad Scroll Progressbar")
|
||||
scroller.Configure(ui.Config{
|
||||
Width: 20,
|
||||
Height: 20,
|
||||
Background: render.RGBA(128, 128, 128, 128),
|
||||
})
|
||||
pager.Pack(scroller, ui.Pack{
|
||||
Side: ui.W,
|
||||
})
|
||||
u.doodadScroller = scroller
|
||||
|
||||
rightBtn := ui.NewButton("Next Page", ui.NewLabel(ui.Label{
|
||||
Text: ">",
|
||||
Font: balance.MenuFont,
|
||||
}))
|
||||
rightBtn.Handle(ui.Click, func(ed ui.EventData) error {
|
||||
u.scrollDoodadFrame(1)
|
||||
return nil
|
||||
})
|
||||
u.Supervisor.Add(rightBtn)
|
||||
pager.Pack(rightBtn, ui.Pack{
|
||||
Side: ui.E,
|
||||
})
|
||||
}
|
||||
u.doodadPager = pager
|
||||
frame.Pack(pager, ui.Pack{
|
||||
Side: ui.N,
|
||||
Fill: true,
|
||||
PadY: 5,
|
||||
})
|
||||
|
||||
doodadsAvailable, err := doodads.ListDoodads()
|
||||
if err != nil {
|
||||
return frame, fmt.Errorf(
|
||||
"setupDoodadFrame: doodads.ListDoodads: %s",
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
var buttonSize = (paletteWidth - window.BoxThickness(2)) / perRow
|
||||
u.doodadButtonSize = buttonSize
|
||||
|
||||
// Load all the doodads, skip hidden ones.
|
||||
var items []*doodads.Doodad
|
||||
for _, filename := range doodadsAvailable {
|
||||
doodad, err := doodads.LoadFile(filename)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
doodad = doodads.New(balance.DoodadSize)
|
||||
}
|
||||
|
||||
// Skip hidden doodads.
|
||||
if doodad.Hidden && !balance.ShowHiddenDoodads {
|
||||
log.Info("skip %s: hidden doodad", filename)
|
||||
continue
|
||||
}
|
||||
|
||||
doodad.Filename = filename
|
||||
items = append(items, doodad)
|
||||
}
|
||||
|
||||
// Draw the doodad buttons in a grid `perRow` buttons wide.
|
||||
var (
|
||||
row *ui.Frame
|
||||
rowCount int // for labeling the ui.Frame for each row
|
||||
btnRows = []*ui.Frame{} // Collect the row frames for the buttons.
|
||||
)
|
||||
for i, doodad := range items {
|
||||
doodad := doodad
|
||||
|
||||
if row == nil || i%perRow == 0 {
|
||||
rowCount++
|
||||
row = ui.NewFrame(fmt.Sprintf("Doodad Row %d", rowCount))
|
||||
row.SetBackground(balance.WindowBackground)
|
||||
btnRows = append(btnRows, row)
|
||||
frame.Pack(row, ui.Pack{
|
||||
Side: ui.N,
|
||||
Fill: true,
|
||||
})
|
||||
}
|
||||
|
||||
can := uix.NewCanvas(int(buttonSize), true)
|
||||
can.Name = doodad.Title
|
||||
can.SetBackground(render.White)
|
||||
can.LoadDoodad(doodad)
|
||||
|
||||
btn := ui.NewButton(doodad.Title, can)
|
||||
btn.Resize(render.NewRect(
|
||||
buttonSize-2, // TODO: without the -2 the button border
|
||||
buttonSize-2, // rests on top of the window border.
|
||||
))
|
||||
row.Pack(btn, ui.Pack{
|
||||
Side: ui.W,
|
||||
})
|
||||
|
||||
// Tooltip hover to show the doodad's name.
|
||||
ui.NewTooltip(btn, ui.Tooltip{
|
||||
Text: doodad.Title,
|
||||
Edge: ui.Top,
|
||||
})
|
||||
|
||||
// Begin the drag event to grab this Doodad.
|
||||
// NOTE: The drag target is the EditorUI.Canvas in
|
||||
// editor_ui.go#SetupCanvas()
|
||||
btn.Handle(ui.MouseDown, func(ed ui.EventData) error {
|
||||
log.Warn("MouseDown on doodad %s (%s)", doodad.Filename, doodad.Title)
|
||||
u.startDragActor(doodad, nil)
|
||||
return nil
|
||||
})
|
||||
u.Supervisor.Add(btn)
|
||||
|
||||
// Resize the canvas to fill the button interior.
|
||||
btnSize := btn.Size()
|
||||
can.Resize(render.NewRect(
|
||||
btnSize.W-btn.BoxThickness(2),
|
||||
btnSize.H-btn.BoxThickness(2),
|
||||
),
|
||||
)
|
||||
|
||||
btn.Compute(e)
|
||||
}
|
||||
|
||||
u.doodadRows = btnRows
|
||||
u.scrollDoodadFrame(0)
|
||||
|
||||
return frame, nil
|
||||
}
|
||||
|
||||
// scrollDoodadFrame handles the Page Up/Down buttons to adjust the number of
|
||||
// Doodads visible on screen.
|
||||
//
|
||||
// rows is the number of rows to scroll. Positive values mean scroll *down*
|
||||
// the list.
|
||||
func (u *EditorUI) scrollDoodadFrame(rows int) {
|
||||
u.doodadSkip += rows
|
||||
if u.doodadSkip < 0 {
|
||||
u.doodadSkip = 0
|
||||
}
|
||||
|
||||
// Calculate about how many rows we can see given our current window size.
|
||||
var (
|
||||
maxVisibleHeight = u.d.height - 86
|
||||
calculatedHeight int
|
||||
rowsBefore int // count of rows hidden before
|
||||
rowsVisible int
|
||||
rowsAfter int // count of rows hidden after
|
||||
rowsEstimated = maxVisibleHeight / u.doodadButtonSize // estimated number rows shown
|
||||
maxSkip = ((len(u.doodadRows) * int(u.doodadButtonSize)) - int(u.doodadButtonSize*rowsEstimated)) / int(u.doodadButtonSize)
|
||||
)
|
||||
|
||||
if maxSkip < 0 {
|
||||
maxSkip = 0
|
||||
}
|
||||
|
||||
if u.doodadSkip > maxSkip {
|
||||
u.doodadSkip = maxSkip
|
||||
}
|
||||
|
||||
// If the window is big enough to encompass all the doodads, don't show
|
||||
// the pager toolbar, its just confusing.
|
||||
if maxSkip == 0 {
|
||||
u.doodadPager.Hide()
|
||||
} else {
|
||||
u.doodadPager.Show()
|
||||
}
|
||||
|
||||
// Comb through the doodads and show/hide the relevant buttons.
|
||||
for i, row := range u.doodadRows {
|
||||
if i < u.doodadSkip {
|
||||
row.Hide()
|
||||
rowsBefore++
|
||||
continue
|
||||
}
|
||||
|
||||
calculatedHeight += u.doodadButtonSize
|
||||
if calculatedHeight > maxVisibleHeight {
|
||||
row.Hide()
|
||||
rowsAfter++
|
||||
} else {
|
||||
row.Show()
|
||||
rowsVisible++
|
||||
}
|
||||
}
|
||||
|
||||
var viewPercent = float64(rowsBefore+rowsVisible) / float64(len(u.doodadRows))
|
||||
u.doodadScroller.Configure(ui.Config{
|
||||
Width: int(float64(paletteWidth-50) * viewPercent), // TODO: hacky magic number
|
||||
})
|
||||
|
||||
}
|
||||
|
|
|
@ -8,34 +8,20 @@ import (
|
|||
"git.kirsle.net/go/ui"
|
||||
)
|
||||
|
||||
// Width of the panel frame.
|
||||
var paletteWidth = 50
|
||||
|
||||
// SetupPalette sets up the palette panel.
|
||||
func (u *EditorUI) SetupPalette(d *Doodle) *ui.Window {
|
||||
window := ui.NewWindow("Palette")
|
||||
window.ConfigureTitle(balance.TitleConfig)
|
||||
// window.TitleBar().Font = balance.TitleFont
|
||||
_, label := window.TitleBar()
|
||||
label.Font = balance.TitleFont
|
||||
window.Configure(ui.Config{
|
||||
Background: balance.WindowBackground,
|
||||
BorderColor: balance.WindowBorder,
|
||||
})
|
||||
|
||||
// Doodad frame.
|
||||
{
|
||||
frame, err := u.setupDoodadFrame(d.Engine, window)
|
||||
if err != nil {
|
||||
d.Flash(err.Error())
|
||||
}
|
||||
|
||||
// Even if there was an error (userdir.ListDoodads couldn't read the
|
||||
// config folder on disk or whatever) the Frame is still valid but
|
||||
// empty, which is still the intended behavior.
|
||||
u.DoodadTab = frame
|
||||
u.DoodadTab.Hide()
|
||||
window.Pack(u.DoodadTab, ui.Pack{
|
||||
Side: ui.N,
|
||||
Fill: true,
|
||||
})
|
||||
}
|
||||
|
||||
// Color Palette Frame.
|
||||
u.PaletteTab = u.setupPaletteFrame(window)
|
||||
window.Pack(u.PaletteTab, ui.Pack{
|
||||
|
@ -65,30 +51,16 @@ func (u *EditorUI) setupPaletteFrame(window *ui.Window) *ui.Frame {
|
|||
return nil
|
||||
}
|
||||
|
||||
var buttonSize = 32
|
||||
|
||||
// Draw the radio buttons for the palette.
|
||||
if u.Canvas != nil && u.Canvas.Palette != nil {
|
||||
for _, swatch := range u.Canvas.Palette.Swatches {
|
||||
swFrame := ui.NewFrame(fmt.Sprintf("Swatch(%s) Button Frame", swatch.Name))
|
||||
|
||||
colorFrame := ui.NewFrame(fmt.Sprintf("Swatch(%s) Color Box", swatch.Name))
|
||||
colorFrame.Configure(ui.Config{
|
||||
Width: 16,
|
||||
Height: 16,
|
||||
swFrame.Configure(ui.Config{
|
||||
Width: buttonSize,
|
||||
Height: buttonSize,
|
||||
Background: swatch.Color,
|
||||
BorderSize: 1,
|
||||
BorderStyle: ui.BorderSunken,
|
||||
})
|
||||
swFrame.Pack(colorFrame, ui.Pack{
|
||||
Side: ui.W,
|
||||
})
|
||||
|
||||
label := ui.NewLabel(ui.Label{
|
||||
Text: swatch.Name,
|
||||
Font: balance.StatusFont,
|
||||
})
|
||||
label.Font.Color = swatch.Color.Darken(128)
|
||||
swFrame.Pack(label, ui.Pack{
|
||||
Side: ui.W,
|
||||
})
|
||||
|
||||
btn := ui.NewRadioButton("palette", &u.selectedSwatch, swatch.Name, swFrame)
|
||||
|
@ -97,18 +69,11 @@ func (u *EditorUI) setupPaletteFrame(window *ui.Window) *ui.Frame {
|
|||
|
||||
// Add a tooltip showing the swatch attributes.
|
||||
ui.NewTooltip(btn, ui.Tooltip{
|
||||
Text: "Attributes: " + swatch.Attributes(),
|
||||
Text: fmt.Sprintf("Name: %s\nAttributes: %s", swatch.Name, swatch.Attributes()),
|
||||
Edge: ui.Left,
|
||||
})
|
||||
|
||||
btn.Compute(u.d.Engine)
|
||||
swFrame.Configure(ui.Config{
|
||||
Height: label.Size().H,
|
||||
|
||||
// TODO: magic number, trying to left-align
|
||||
// the label by making the frame as wide as possible.
|
||||
Width: paletteWidth - 16,
|
||||
})
|
||||
|
||||
frame.Pack(btn, ui.Pack{
|
||||
Side: ui.N,
|
||||
|
@ -118,5 +83,27 @@ func (u *EditorUI) setupPaletteFrame(window *ui.Window) *ui.Frame {
|
|||
}
|
||||
}
|
||||
|
||||
// Draw the Edit Palette button.
|
||||
btn := ui.NewButton("Edit Palette", ui.NewLabel(ui.Label{
|
||||
Text: "Edit",
|
||||
Font: balance.MenuFont,
|
||||
}))
|
||||
btn.Handle(ui.Click, func(ed ui.EventData) error {
|
||||
// TODO: recompute the window so the actual loaded level palette gets in
|
||||
u.paletteEditor.Hide()
|
||||
u.paletteEditor = nil
|
||||
u.SetupPopups(u.d)
|
||||
u.paletteEditor.Show()
|
||||
return nil
|
||||
})
|
||||
u.Supervisor.Add(btn)
|
||||
|
||||
btn.Compute(u.d.Engine)
|
||||
frame.Pack(btn, ui.Pack{
|
||||
Side: ui.N,
|
||||
Fill: true,
|
||||
PadY: 4,
|
||||
})
|
||||
|
||||
return frame
|
||||
}
|
||||
|
|
|
@ -27,18 +27,6 @@ func (u *EditorUI) SetupToolbar(d *Doodle) *ui.Frame {
|
|||
Side: ui.N,
|
||||
})
|
||||
|
||||
// Helper functions to toggle the correct palette panel.
|
||||
var (
|
||||
showSwatchPalette = func() {
|
||||
u.DoodadTab.Hide()
|
||||
u.PaletteTab.Show()
|
||||
}
|
||||
showDoodadPalette = func() {
|
||||
u.PaletteTab.Hide()
|
||||
u.DoodadTab.Show()
|
||||
}
|
||||
)
|
||||
|
||||
// Buttons.
|
||||
var buttons = []struct {
|
||||
Value string
|
||||
|
@ -52,7 +40,6 @@ func (u *EditorUI) SetupToolbar(d *Doodle) *ui.Frame {
|
|||
Tooltip: "Pencil Tool",
|
||||
Click: func() {
|
||||
u.Canvas.Tool = drawtool.PencilTool
|
||||
showSwatchPalette()
|
||||
d.Flash("Pencil Tool selected.")
|
||||
},
|
||||
},
|
||||
|
@ -63,7 +50,6 @@ func (u *EditorUI) SetupToolbar(d *Doodle) *ui.Frame {
|
|||
Tooltip: "Line Tool",
|
||||
Click: func() {
|
||||
u.Canvas.Tool = drawtool.LineTool
|
||||
showSwatchPalette()
|
||||
d.Flash("Line Tool selected.")
|
||||
},
|
||||
},
|
||||
|
@ -74,7 +60,6 @@ func (u *EditorUI) SetupToolbar(d *Doodle) *ui.Frame {
|
|||
Tooltip: "Rectangle Tool",
|
||||
Click: func() {
|
||||
u.Canvas.Tool = drawtool.RectTool
|
||||
showSwatchPalette()
|
||||
d.Flash("Rectangle Tool selected.")
|
||||
},
|
||||
},
|
||||
|
@ -85,7 +70,6 @@ func (u *EditorUI) SetupToolbar(d *Doodle) *ui.Frame {
|
|||
Tooltip: "Ellipse Tool",
|
||||
Click: func() {
|
||||
u.Canvas.Tool = drawtool.EllipseTool
|
||||
showSwatchPalette()
|
||||
d.Flash("Ellipse Tool selected.")
|
||||
},
|
||||
},
|
||||
|
@ -96,7 +80,7 @@ func (u *EditorUI) SetupToolbar(d *Doodle) *ui.Frame {
|
|||
Tooltip: "Doodad Tool\nDrag-and-drop objects into your map",
|
||||
Click: func() {
|
||||
u.Canvas.Tool = drawtool.ActorTool
|
||||
showDoodadPalette()
|
||||
u.doodadWindow.Show()
|
||||
d.Flash("Actor Tool selected. Drag a Doodad from the drawer into your level.")
|
||||
},
|
||||
},
|
||||
|
@ -107,7 +91,7 @@ func (u *EditorUI) SetupToolbar(d *Doodle) *ui.Frame {
|
|||
Tooltip: "Link Tool\nConnect doodads to each other",
|
||||
Click: func() {
|
||||
u.Canvas.Tool = drawtool.LinkTool
|
||||
showDoodadPalette()
|
||||
u.doodadWindow.Show()
|
||||
d.Flash("Link Tool selected. Click a doodad in your level to link it to another.")
|
||||
},
|
||||
},
|
||||
|
@ -126,7 +110,6 @@ func (u *EditorUI) SetupToolbar(d *Doodle) *ui.Frame {
|
|||
u.Canvas.BrushSize = balance.MaxEraserBrushSize
|
||||
}
|
||||
|
||||
showSwatchPalette()
|
||||
d.Flash("Eraser Tool selected.")
|
||||
},
|
||||
},
|
||||
|
|
|
@ -104,6 +104,12 @@ func (c *Chunk) TextureMasked(e render.Engine, mask render.Color) render.Texture
|
|||
return c.textureMasked
|
||||
}
|
||||
|
||||
// SetDirty sets the `dirty` flag to true and forces the texture to be
|
||||
// re-computed next frame.
|
||||
func (c *Chunk) SetDirty() {
|
||||
c.dirty = true
|
||||
}
|
||||
|
||||
// toBitmap puts the texture in a well named bitmap path in the cache folder.
|
||||
func (c *Chunk) toBitmap(mask render.Color) (render.Texturer, error) {
|
||||
// Generate a unique name for this chunk cache.
|
||||
|
|
|
@ -180,6 +180,14 @@ func (c *Chunker) GetChunk(p render.Point) (*Chunk, bool) {
|
|||
return chunk, ok
|
||||
}
|
||||
|
||||
// Redraw marks every chunk as dirty and invalidates all their texture caches,
|
||||
// forcing the drawing to re-generate from scratch.
|
||||
func (c *Chunker) Redraw() {
|
||||
for _, chunk := range c.Chunks {
|
||||
chunk.SetDirty()
|
||||
}
|
||||
}
|
||||
|
||||
// Get a pixel at the given coordinate. Returns the Palette entry for that
|
||||
// pixel or else returns an error if not found.
|
||||
func (c *Chunker) Get(p render.Point) (*Swatch, error) {
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
package level
|
||||
|
||||
import "git.kirsle.net/go/render"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.kirsle.net/go/render"
|
||||
)
|
||||
|
||||
// DefaultPalette returns a sensible default palette.
|
||||
func DefaultPalette() *Palette {
|
||||
|
@ -85,6 +89,25 @@ func (p *Palette) Inflate() {
|
|||
p.update()
|
||||
}
|
||||
|
||||
// AddSwatch adds a new swatch to the palette.
|
||||
func (p *Palette) AddSwatch() *Swatch {
|
||||
p.update()
|
||||
|
||||
var (
|
||||
index = len(p.Swatches)
|
||||
name = fmt.Sprintf("color %d", len(p.Swatches)+1)
|
||||
)
|
||||
|
||||
p.Swatches = append(p.Swatches, &Swatch{
|
||||
Name: name,
|
||||
Color: render.Magenta,
|
||||
index: index,
|
||||
})
|
||||
p.byName[name] = index
|
||||
|
||||
return p.Swatches[index]
|
||||
}
|
||||
|
||||
// Get a swatch by name.
|
||||
func (p *Palette) Get(name string) (result *Swatch, exists bool) {
|
||||
p.update()
|
||||
|
|
|
@ -146,6 +146,8 @@ func (s *MenuScene) setupNewWindow(d *Doodle) error {
|
|||
},
|
||||
})
|
||||
s.window = window
|
||||
window.SetButtons(0)
|
||||
window.Show()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,9 @@ var (
|
|||
// Globally available Flash() function so we can emit text to the Doodle UI.
|
||||
Flash func(string, ...interface{})
|
||||
|
||||
// Globally available Prompt() function.
|
||||
Prompt func(string, func(string))
|
||||
|
||||
// Ajax file cache for WASM use.
|
||||
AjaxCache map[string][]byte
|
||||
)
|
||||
|
|
|
@ -351,6 +351,11 @@ func (w *Canvas) loopEditable(ev *event.State) error {
|
|||
if err := w.LinkAdd(actor); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: reset the Button1 state so we don't finish a
|
||||
// link and then LinkAdd the clicked doodad immediately
|
||||
// (causing link chaining)
|
||||
ev.Button1 = false
|
||||
break
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -42,8 +42,8 @@ func NewAddEditLevel(config AddEditLevel) *ui.Window {
|
|||
window := ui.NewWindow(title)
|
||||
window.SetButtons(ui.CloseButton)
|
||||
window.Configure(ui.Config{
|
||||
Width: 540,
|
||||
Height: 350,
|
||||
Width: 400,
|
||||
Height: 180,
|
||||
Background: render.Grey,
|
||||
})
|
||||
|
||||
|
@ -234,5 +234,6 @@ func NewAddEditLevel(config AddEditLevel) *ui.Window {
|
|||
}
|
||||
}
|
||||
|
||||
window.Hide()
|
||||
return window
|
||||
}
|
||||
|
|
244
pkg/windows/doodad_dropper.go
Normal file
244
pkg/windows/doodad_dropper.go
Normal file
|
@ -0,0 +1,244 @@
|
|||
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/level"
|
||||
"git.kirsle.net/apps/doodle/pkg/log"
|
||||
"git.kirsle.net/apps/doodle/pkg/uix"
|
||||
"git.kirsle.net/go/render"
|
||||
"git.kirsle.net/go/ui"
|
||||
)
|
||||
|
||||
// DoodadDropper is the doodad palette pop-up window for Editor Mode.
|
||||
type DoodadDropper struct {
|
||||
Supervisor *ui.Supervisor
|
||||
Engine render.Engine
|
||||
|
||||
// Editing settings for an existing level?
|
||||
EditLevel *level.Level
|
||||
|
||||
// Callback functions.
|
||||
OnStartDragActor func(doodad *doodads.Doodad, actor *level.Actor)
|
||||
OnCancel func()
|
||||
}
|
||||
|
||||
// NewDoodadDropper initializes the window.
|
||||
func NewDoodadDropper(config DoodadDropper) *ui.Window {
|
||||
// Default options.
|
||||
var (
|
||||
title = "Doodads"
|
||||
|
||||
buttonSize = balance.DoodadButtonSize
|
||||
columns = balance.DoodadDropperCols
|
||||
rows = balance.DoodadDropperRows
|
||||
|
||||
// size of the doodad window
|
||||
width = buttonSize * columns
|
||||
height = (buttonSize * rows) + 64 // account for button borders :(
|
||||
|
||||
// pagination values
|
||||
page = 1
|
||||
pages int
|
||||
perPage = 20
|
||||
)
|
||||
|
||||
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,
|
||||
})
|
||||
|
||||
/*******
|
||||
* Display the Doodads in rows of buttons
|
||||
*******/
|
||||
|
||||
doodadsAvailable, err := doodads.ListDoodads()
|
||||
if err != nil {
|
||||
log.Error("NewDoodadDropper: doodads.ListDoodads: %s", err)
|
||||
}
|
||||
|
||||
// Load all the doodads, skip hidden ones.
|
||||
var items []*doodads.Doodad
|
||||
for _, filename := range doodadsAvailable {
|
||||
doodad, err := doodads.LoadFile(filename)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
doodad = doodads.New(balance.DoodadSize)
|
||||
}
|
||||
|
||||
// Skip hidden doodads.
|
||||
if doodad.Hidden && !balance.ShowHiddenDoodads {
|
||||
continue
|
||||
}
|
||||
|
||||
doodad.Filename = filename
|
||||
items = append(items, doodad)
|
||||
}
|
||||
|
||||
// Compute the number of pages for the pager widget.
|
||||
pages = int(
|
||||
math.Ceil(
|
||||
float64(len(items)) / float64(columns*rows),
|
||||
),
|
||||
)
|
||||
|
||||
// Draw the doodad buttons in rows.
|
||||
var btnRows = []*ui.Frame{}
|
||||
{
|
||||
var (
|
||||
row *ui.Frame
|
||||
rowCount int // for labeling the ui.Frame for each row
|
||||
|
||||
// TODO: pre-size btnRows by calculating how many needed
|
||||
)
|
||||
|
||||
for i, doodad := range items {
|
||||
doodad := doodad
|
||||
|
||||
if row == nil || i%columns == 0 {
|
||||
var hidden = rowCount >= rows
|
||||
rowCount++
|
||||
|
||||
row = ui.NewFrame(fmt.Sprintf("Doodad Row %d", rowCount))
|
||||
row.SetBackground(balance.DoodadButtonBackground)
|
||||
btnRows = append(btnRows, row)
|
||||
frame.Pack(row, ui.Pack{
|
||||
Side: ui.N,
|
||||
// Fill: true,
|
||||
})
|
||||
|
||||
// Hide overflowing rows until we scroll to them.
|
||||
if hidden {
|
||||
row.Hide()
|
||||
}
|
||||
}
|
||||
|
||||
can := uix.NewCanvas(int(buttonSize), true)
|
||||
can.Name = doodad.Title
|
||||
can.SetBackground(balance.DoodadButtonBackground)
|
||||
can.LoadDoodad(doodad)
|
||||
|
||||
btn := ui.NewButton(doodad.Title, can)
|
||||
btn.Resize(render.NewRect(
|
||||
buttonSize-2, // TODO: without the -2 the button border
|
||||
buttonSize-2, // rests on top of the window border
|
||||
))
|
||||
row.Pack(btn, ui.Pack{
|
||||
Side: ui.W,
|
||||
})
|
||||
|
||||
// Tooltip hover to show the doodad's name.
|
||||
ui.NewTooltip(btn, ui.Tooltip{
|
||||
Text: doodad.Title,
|
||||
Edge: ui.Top,
|
||||
})
|
||||
|
||||
// Begin the drag event to grab this Doodad.
|
||||
// NOTE: The drag target is the EditorUI.Canvas in
|
||||
// editor_ui.go#SetupCanvas()
|
||||
btn.Handle(ui.MouseDown, func(ed ui.EventData) error {
|
||||
log.Warn("MouseDown on doodad %s (%s)", doodad.Filename, doodad.Title)
|
||||
config.OnStartDragActor(doodad, nil)
|
||||
return nil
|
||||
})
|
||||
config.Supervisor.Add(btn)
|
||||
|
||||
// Resize the canvas to fill the button interior.
|
||||
btnSize := btn.Size()
|
||||
can.Resize(render.NewRect(
|
||||
btnSize.W-btn.BoxThickness(2),
|
||||
btnSize.H-btn.BoxThickness(2),
|
||||
))
|
||||
|
||||
btn.Compute(config.Engine)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
/******************
|
||||
* 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: pages,
|
||||
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 the page we're on.
|
||||
var (
|
||||
minRow = (page - 1) * rows
|
||||
visible = 0
|
||||
)
|
||||
for i, row := range btnRows {
|
||||
if visible >= rows {
|
||||
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,
|
||||
})
|
||||
|
||||
var buttons = []struct {
|
||||
Label string
|
||||
F func(ui.EventData) error
|
||||
}{
|
||||
// OK button is for editing an existing level.
|
||||
{"Close", func(ed ui.EventData) error {
|
||||
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)
|
||||
config.Supervisor.Add(btn)
|
||||
bottomFrame.Place(btn, ui.Place{
|
||||
Top: 20,
|
||||
Right: 20,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
window.Hide()
|
||||
return window
|
||||
}
|
365
pkg/windows/palette_editor.go
Normal file
365
pkg/windows/palette_editor.go
Normal file
|
@ -0,0 +1,365 @@
|
|||
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
|
||||
}
|
Loading…
Reference in New Issue
Block a user