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 (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -15,6 +14,7 @@ import (
|
||||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||||
"git.kirsle.net/apps/doodle/pkg/bindata"
|
"git.kirsle.net/apps/doodle/pkg/bindata"
|
||||||
"git.kirsle.net/apps/doodle/pkg/branding"
|
"git.kirsle.net/apps/doodle/pkg/branding"
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/log"
|
||||||
"git.kirsle.net/apps/doodle/pkg/sound"
|
"git.kirsle.net/apps/doodle/pkg/sound"
|
||||||
"git.kirsle.net/go/render/sdl"
|
"git.kirsle.net/go/render/sdl"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
@ -71,7 +71,7 @@ func main() {
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "window",
|
Name: "window",
|
||||||
Aliases: []string{"w"},
|
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{
|
&cli.BoolFlag{
|
||||||
Name: "guitest",
|
Name: "guitest",
|
||||||
|
@ -127,6 +127,13 @@ func main() {
|
||||||
game.PlayLevel(filename)
|
game.PlayLevel(filename)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Maximizing the window? with `-w maximized`
|
||||||
|
if c.String("window") == "maximized" {
|
||||||
|
log.Info("Maximize main window")
|
||||||
|
engine.Maximize()
|
||||||
|
}
|
||||||
|
|
||||||
game.Run()
|
game.Run()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -136,13 +143,13 @@ func main() {
|
||||||
|
|
||||||
err := app.Run(os.Args)
|
err := app.Run(os.Args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Error(err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setResolution(value string) error {
|
func setResolution(value string) error {
|
||||||
switch value {
|
switch value {
|
||||||
case "desktop":
|
case "desktop", "maximized":
|
||||||
return nil
|
return nil
|
||||||
case "mobile":
|
case "mobile":
|
||||||
balance.Width = 375
|
balance.Width = 375
|
||||||
|
|
|
@ -23,7 +23,7 @@ var (
|
||||||
}
|
}
|
||||||
TitleFont = render.Text{
|
TitleFont = render.Text{
|
||||||
FontFilename: "DejaVuSans-Bold.ttf",
|
FontFilename: "DejaVuSans-Bold.ttf",
|
||||||
Size: 12,
|
Size: 9,
|
||||||
Padding: 4,
|
Padding: 4,
|
||||||
Color: render.White,
|
Color: render.White,
|
||||||
Stroke: render.Red,
|
Stroke: render.Red,
|
||||||
|
@ -37,6 +37,11 @@ var (
|
||||||
Size: 12,
|
Size: 12,
|
||||||
PadX: 4,
|
PadX: 4,
|
||||||
}
|
}
|
||||||
|
MenuFontBold = render.Text{
|
||||||
|
FontFilename: "DejaVuSans-Bold.ttf",
|
||||||
|
Size: 12,
|
||||||
|
PadX: 4,
|
||||||
|
}
|
||||||
|
|
||||||
// StatusFont is the font for the status bar.
|
// StatusFont is the font for the status bar.
|
||||||
StatusFont = render.Text{
|
StatusFont = render.Text{
|
||||||
|
@ -83,4 +88,10 @@ var (
|
||||||
Color: render.RGBA(255, 255, 0, 255),
|
Color: render.RGBA(255, 255, 0, 255),
|
||||||
Stroke: render.RGBA(100, 100, 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
|
// Make the render engine globally available. TODO: for wasm/ToBitmap
|
||||||
shmem.CurrentRenderEngine = engine
|
shmem.CurrentRenderEngine = engine
|
||||||
shmem.Flash = d.Flash
|
shmem.Flash = d.Flash
|
||||||
|
shmem.Prompt = d.Prompt
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
log.Logger.Config.Level = golog.DebugLevel
|
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"
|
"git.kirsle.net/go/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Width of the panel frame.
|
|
||||||
var paletteWidth = 160
|
|
||||||
|
|
||||||
// EditorUI manages the user interface for the Editor Scene.
|
// EditorUI manages the user interface for the Editor Scene.
|
||||||
type EditorUI struct {
|
type EditorUI struct {
|
||||||
d *Doodle
|
d *Doodle
|
||||||
|
@ -50,6 +47,8 @@ type EditorUI struct {
|
||||||
// Popup windows.
|
// Popup windows.
|
||||||
levelSettingsWindow *ui.Window
|
levelSettingsWindow *ui.Window
|
||||||
aboutWindow *ui.Window
|
aboutWindow *ui.Window
|
||||||
|
doodadWindow *ui.Window
|
||||||
|
paletteEditor *ui.Window
|
||||||
|
|
||||||
// Palette window.
|
// Palette window.
|
||||||
Palette *ui.Window
|
Palette *ui.Window
|
||||||
|
@ -107,6 +106,9 @@ func NewEditorUI(d *Doodle, s *EditorScene) *EditorUI {
|
||||||
u.ToolBar = u.SetupToolbar(d)
|
u.ToolBar = u.SetupToolbar(d)
|
||||||
u.Workspace = u.SetupWorkspace(d) // important that this is last!
|
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())
|
log.Error("menu size: %s", u.MenuBar.Rect())
|
||||||
u.screen.Pack(u.MenuBar, ui.Pack{
|
u.screen.Pack(u.MenuBar, ui.Pack{
|
||||||
Side: ui.N,
|
Side: ui.N,
|
||||||
|
@ -175,8 +177,6 @@ func (u *EditorUI) Resized(d *Doodle) {
|
||||||
menuHeight,
|
menuHeight,
|
||||||
))
|
))
|
||||||
u.Palette.Compute(d.Engine)
|
u.Palette.Compute(d.Engine)
|
||||||
|
|
||||||
u.scrollDoodadFrame(0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var innerHeight = u.d.height - menuHeight - u.StatusBar.Size().H
|
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)
|
u.screen.Present(e, render.Origin)
|
||||||
|
|
||||||
|
// Draw any windows being managed by Supervisor.
|
||||||
|
u.Supervisor.Present(e)
|
||||||
|
|
||||||
// Are we dragging a Doodad canvas?
|
// Are we dragging a Doodad canvas?
|
||||||
if u.Supervisor.IsDragging() {
|
if u.Supervisor.IsDragging() {
|
||||||
if actor := u.DraggableActor; actor != nil {
|
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
|
// 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
|
// mode when you click an existing Doodad and it "pops" out of the canvas
|
||||||
// and onto the cursor to be repositioned.
|
// and onto the cursor to be repositioned.
|
||||||
drawing.OnDragStart = func(actor *level.Actor) {
|
drawing.OnDragStart = func(actor *level.Actor) {
|
||||||
|
log.Warn("drawing.OnDragStart: grab actor %s", actor)
|
||||||
u.startDragActor(nil, 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?
|
// Was it an actor from the Doodad Palette?
|
||||||
if actor := u.DraggableActor; actor != nil {
|
if actor := u.DraggableActor; actor != nil {
|
||||||
log.Info("Actor is a %s", actor.doodad.Filename)
|
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 {
|
if u.Scene.Level == nil {
|
||||||
u.d.Flash("Can't drop doodads onto doodad drawings!")
|
u.d.Flash("Can't drop doodads onto doodad drawings!")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If they dropped it onto a UI window, ignore it.
|
||||||
|
if u.Supervisor.IsPointInWindow(ed.Point) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Uncenter the drawing from the cursor.
|
// Uncenter the drawing from the cursor.
|
||||||
size = actor.canvas.Size()
|
size = actor.canvas.Size()
|
||||||
|
@ -530,38 +542,13 @@ func (u *EditorUI) SetupMenuBar(d *Doodle) *ui.MenuBar {
|
||||||
})
|
})
|
||||||
editMenu.AddSeparator()
|
editMenu.AddSeparator()
|
||||||
editMenu.AddItem("Level options", func() {
|
editMenu.AddItem("Level options", func() {
|
||||||
scene, _ := d.Scene.(*EditorScene)
|
|
||||||
log.Info("Opening the window")
|
log.Info("Opening the window")
|
||||||
|
|
||||||
// Open the New Level window in edit-settings mode.
|
// 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.Hide()
|
||||||
},
|
u.levelSettingsWindow = nil
|
||||||
})
|
u.SetupPopups(u.d)
|
||||||
|
|
||||||
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.Show()
|
u.levelSettingsWindow.Show()
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
////////
|
////////
|
||||||
|
@ -585,6 +572,10 @@ func (u *EditorUI) SetupMenuBar(d *Doodle) *ui.MenuBar {
|
||||||
toolMenu.AddItemAccel("Command shell", "Enter", func() {
|
toolMenu.AddItemAccel("Command shell", "Enter", func() {
|
||||||
d.shell.Open = true
|
d.shell.Open = true
|
||||||
})
|
})
|
||||||
|
toolMenu.AddItemAccel("Doodads", "d", func() {
|
||||||
|
log.Info("Open the DoodadDropper")
|
||||||
|
u.doodadWindow.Show()
|
||||||
|
})
|
||||||
|
|
||||||
////////
|
////////
|
||||||
// Help menu
|
// Help menu
|
||||||
|
@ -618,6 +609,96 @@ func (u *EditorUI) SetupMenuBar(d *Doodle) *ui.MenuBar {
|
||||||
return menu
|
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.
|
// SetupStatusBar sets up the status bar widget along the bottom of the window.
|
||||||
func (u *EditorUI) SetupStatusBar(d *Doodle) *ui.Frame {
|
func (u *EditorUI) SetupStatusBar(d *Doodle) *ui.Frame {
|
||||||
frame := ui.NewFrame("Status Bar")
|
frame := ui.NewFrame("Status Bar")
|
||||||
|
|
|
@ -5,15 +5,12 @@ package doodle
|
||||||
// refactor into a subpackage if EditorUI itself can ever be decoupled.
|
// refactor into a subpackage if EditorUI itself can ever be decoupled.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||||
"git.kirsle.net/apps/doodle/pkg/doodads"
|
"git.kirsle.net/apps/doodle/pkg/doodads"
|
||||||
"git.kirsle.net/apps/doodle/pkg/level"
|
"git.kirsle.net/apps/doodle/pkg/level"
|
||||||
"git.kirsle.net/apps/doodle/pkg/log"
|
"git.kirsle.net/apps/doodle/pkg/log"
|
||||||
"git.kirsle.net/apps/doodle/pkg/uix"
|
"git.kirsle.net/apps/doodle/pkg/uix"
|
||||||
"git.kirsle.net/go/render"
|
"git.kirsle.net/go/render"
|
||||||
"git.kirsle.net/go/ui"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// DraggableActor is a Doodad being dragged from the Doodad palette.
|
// 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,
|
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"
|
"git.kirsle.net/go/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Width of the panel frame.
|
||||||
|
var paletteWidth = 50
|
||||||
|
|
||||||
// SetupPalette sets up the palette panel.
|
// SetupPalette sets up the palette panel.
|
||||||
func (u *EditorUI) SetupPalette(d *Doodle) *ui.Window {
|
func (u *EditorUI) SetupPalette(d *Doodle) *ui.Window {
|
||||||
window := ui.NewWindow("Palette")
|
window := ui.NewWindow("Palette")
|
||||||
window.ConfigureTitle(balance.TitleConfig)
|
window.ConfigureTitle(balance.TitleConfig)
|
||||||
// window.TitleBar().Font = balance.TitleFont
|
_, label := window.TitleBar()
|
||||||
|
label.Font = balance.TitleFont
|
||||||
window.Configure(ui.Config{
|
window.Configure(ui.Config{
|
||||||
Background: balance.WindowBackground,
|
Background: balance.WindowBackground,
|
||||||
BorderColor: balance.WindowBorder,
|
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.
|
// Color Palette Frame.
|
||||||
u.PaletteTab = u.setupPaletteFrame(window)
|
u.PaletteTab = u.setupPaletteFrame(window)
|
||||||
window.Pack(u.PaletteTab, ui.Pack{
|
window.Pack(u.PaletteTab, ui.Pack{
|
||||||
|
@ -65,30 +51,16 @@ func (u *EditorUI) setupPaletteFrame(window *ui.Window) *ui.Frame {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var buttonSize = 32
|
||||||
|
|
||||||
// Draw the radio buttons for the palette.
|
// Draw the radio buttons for the palette.
|
||||||
if u.Canvas != nil && u.Canvas.Palette != nil {
|
if u.Canvas != nil && u.Canvas.Palette != nil {
|
||||||
for _, swatch := range u.Canvas.Palette.Swatches {
|
for _, swatch := range u.Canvas.Palette.Swatches {
|
||||||
swFrame := ui.NewFrame(fmt.Sprintf("Swatch(%s) Button Frame", swatch.Name))
|
swFrame := ui.NewFrame(fmt.Sprintf("Swatch(%s) Button Frame", swatch.Name))
|
||||||
|
swFrame.Configure(ui.Config{
|
||||||
colorFrame := ui.NewFrame(fmt.Sprintf("Swatch(%s) Color Box", swatch.Name))
|
Width: buttonSize,
|
||||||
colorFrame.Configure(ui.Config{
|
Height: buttonSize,
|
||||||
Width: 16,
|
|
||||||
Height: 16,
|
|
||||||
Background: swatch.Color,
|
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)
|
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.
|
// Add a tooltip showing the swatch attributes.
|
||||||
ui.NewTooltip(btn, ui.Tooltip{
|
ui.NewTooltip(btn, ui.Tooltip{
|
||||||
Text: "Attributes: " + swatch.Attributes(),
|
Text: fmt.Sprintf("Name: %s\nAttributes: %s", swatch.Name, swatch.Attributes()),
|
||||||
Edge: ui.Left,
|
Edge: ui.Left,
|
||||||
})
|
})
|
||||||
|
|
||||||
btn.Compute(u.d.Engine)
|
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{
|
frame.Pack(btn, ui.Pack{
|
||||||
Side: ui.N,
|
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
|
return frame
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,18 +27,6 @@ func (u *EditorUI) SetupToolbar(d *Doodle) *ui.Frame {
|
||||||
Side: ui.N,
|
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.
|
// Buttons.
|
||||||
var buttons = []struct {
|
var buttons = []struct {
|
||||||
Value string
|
Value string
|
||||||
|
@ -52,7 +40,6 @@ func (u *EditorUI) SetupToolbar(d *Doodle) *ui.Frame {
|
||||||
Tooltip: "Pencil Tool",
|
Tooltip: "Pencil Tool",
|
||||||
Click: func() {
|
Click: func() {
|
||||||
u.Canvas.Tool = drawtool.PencilTool
|
u.Canvas.Tool = drawtool.PencilTool
|
||||||
showSwatchPalette()
|
|
||||||
d.Flash("Pencil Tool selected.")
|
d.Flash("Pencil Tool selected.")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -63,7 +50,6 @@ func (u *EditorUI) SetupToolbar(d *Doodle) *ui.Frame {
|
||||||
Tooltip: "Line Tool",
|
Tooltip: "Line Tool",
|
||||||
Click: func() {
|
Click: func() {
|
||||||
u.Canvas.Tool = drawtool.LineTool
|
u.Canvas.Tool = drawtool.LineTool
|
||||||
showSwatchPalette()
|
|
||||||
d.Flash("Line Tool selected.")
|
d.Flash("Line Tool selected.")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -74,7 +60,6 @@ func (u *EditorUI) SetupToolbar(d *Doodle) *ui.Frame {
|
||||||
Tooltip: "Rectangle Tool",
|
Tooltip: "Rectangle Tool",
|
||||||
Click: func() {
|
Click: func() {
|
||||||
u.Canvas.Tool = drawtool.RectTool
|
u.Canvas.Tool = drawtool.RectTool
|
||||||
showSwatchPalette()
|
|
||||||
d.Flash("Rectangle Tool selected.")
|
d.Flash("Rectangle Tool selected.")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -85,7 +70,6 @@ func (u *EditorUI) SetupToolbar(d *Doodle) *ui.Frame {
|
||||||
Tooltip: "Ellipse Tool",
|
Tooltip: "Ellipse Tool",
|
||||||
Click: func() {
|
Click: func() {
|
||||||
u.Canvas.Tool = drawtool.EllipseTool
|
u.Canvas.Tool = drawtool.EllipseTool
|
||||||
showSwatchPalette()
|
|
||||||
d.Flash("Ellipse Tool selected.")
|
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",
|
Tooltip: "Doodad Tool\nDrag-and-drop objects into your map",
|
||||||
Click: func() {
|
Click: func() {
|
||||||
u.Canvas.Tool = drawtool.ActorTool
|
u.Canvas.Tool = drawtool.ActorTool
|
||||||
showDoodadPalette()
|
u.doodadWindow.Show()
|
||||||
d.Flash("Actor Tool selected. Drag a Doodad from the drawer into your level.")
|
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",
|
Tooltip: "Link Tool\nConnect doodads to each other",
|
||||||
Click: func() {
|
Click: func() {
|
||||||
u.Canvas.Tool = drawtool.LinkTool
|
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.")
|
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
|
u.Canvas.BrushSize = balance.MaxEraserBrushSize
|
||||||
}
|
}
|
||||||
|
|
||||||
showSwatchPalette()
|
|
||||||
d.Flash("Eraser Tool selected.")
|
d.Flash("Eraser Tool selected.")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -104,6 +104,12 @@ func (c *Chunk) TextureMasked(e render.Engine, mask render.Color) render.Texture
|
||||||
return c.textureMasked
|
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.
|
// toBitmap puts the texture in a well named bitmap path in the cache folder.
|
||||||
func (c *Chunk) toBitmap(mask render.Color) (render.Texturer, error) {
|
func (c *Chunk) toBitmap(mask render.Color) (render.Texturer, error) {
|
||||||
// Generate a unique name for this chunk cache.
|
// Generate a unique name for this chunk cache.
|
||||||
|
|
|
@ -180,6 +180,14 @@ func (c *Chunker) GetChunk(p render.Point) (*Chunk, bool) {
|
||||||
return chunk, ok
|
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
|
// Get a pixel at the given coordinate. Returns the Palette entry for that
|
||||||
// pixel or else returns an error if not found.
|
// pixel or else returns an error if not found.
|
||||||
func (c *Chunker) Get(p render.Point) (*Swatch, error) {
|
func (c *Chunker) Get(p render.Point) (*Swatch, error) {
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package level
|
package level
|
||||||
|
|
||||||
import "git.kirsle.net/go/render"
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.kirsle.net/go/render"
|
||||||
|
)
|
||||||
|
|
||||||
// DefaultPalette returns a sensible default palette.
|
// DefaultPalette returns a sensible default palette.
|
||||||
func DefaultPalette() *Palette {
|
func DefaultPalette() *Palette {
|
||||||
|
@ -85,6 +89,25 @@ func (p *Palette) Inflate() {
|
||||||
p.update()
|
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.
|
// Get a swatch by name.
|
||||||
func (p *Palette) Get(name string) (result *Swatch, exists bool) {
|
func (p *Palette) Get(name string) (result *Swatch, exists bool) {
|
||||||
p.update()
|
p.update()
|
||||||
|
|
|
@ -146,6 +146,8 @@ func (s *MenuScene) setupNewWindow(d *Doodle) error {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
s.window = window
|
s.window = window
|
||||||
|
window.SetButtons(0)
|
||||||
|
window.Show()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,9 @@ var (
|
||||||
// Globally available Flash() function so we can emit text to the Doodle UI.
|
// Globally available Flash() function so we can emit text to the Doodle UI.
|
||||||
Flash func(string, ...interface{})
|
Flash func(string, ...interface{})
|
||||||
|
|
||||||
|
// Globally available Prompt() function.
|
||||||
|
Prompt func(string, func(string))
|
||||||
|
|
||||||
// Ajax file cache for WASM use.
|
// Ajax file cache for WASM use.
|
||||||
AjaxCache map[string][]byte
|
AjaxCache map[string][]byte
|
||||||
)
|
)
|
||||||
|
|
|
@ -351,6 +351,11 @@ func (w *Canvas) loopEditable(ev *event.State) error {
|
||||||
if err := w.LinkAdd(actor); err != nil {
|
if err := w.LinkAdd(actor); err != nil {
|
||||||
return err
|
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
|
break
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -42,8 +42,8 @@ func NewAddEditLevel(config AddEditLevel) *ui.Window {
|
||||||
window := ui.NewWindow(title)
|
window := ui.NewWindow(title)
|
||||||
window.SetButtons(ui.CloseButton)
|
window.SetButtons(ui.CloseButton)
|
||||||
window.Configure(ui.Config{
|
window.Configure(ui.Config{
|
||||||
Width: 540,
|
Width: 400,
|
||||||
Height: 350,
|
Height: 180,
|
||||||
Background: render.Grey,
|
Background: render.Grey,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -234,5 +234,6 @@ func NewAddEditLevel(config AddEditLevel) *ui.Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.Hide()
|
||||||
return window
|
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