New TabFrame Widget for Doodads and Settings

* Install the new ui.TabFrame widget into the Settings and Doodad
  Dropper windows to give them properly tabbed interfaces.
* Doodad Dropper's new tabs divide the list of doodads into categories
  to make them easier to find.
* The officially defined categories so far are:
  - Objects (Start/End Flags and Box)
  - Doors (All locked doors and keys, Warp Doors, and Electric Door)
  - Gizmos (All buttons, switches, state blocks/doors, Electric Door)
  - Creatures (Blue/Red Azulian, Bird, Boy)
* The "All" tab of the Doodad Dropper will show every doodad regardless
  of its category or whether it fit one of the official categories.
* How doodads are assigned categories is by a special "category" tag in
  their metadata, e.g. "category=doors,gizmos" - multiple supported.
zoom-hell
Noah 2021-07-25 21:46:55 -07:00
parent fbeeb207a8
commit 49876c4fdf
8 changed files with 146 additions and 97 deletions

View File

@ -33,6 +33,9 @@ buttons() {
doodad convert -t "Button Type B" typeB1.png typeB2.png button-typeB.doodad
doodad install-script button.js button-typeB.doodad
# Tag the category for these doodads
for i in *.doodad; do doodad edit-doodad --tag "category=gizmos" $i; done
cp button*.doodad ../../../assets/doodads/
cd ..
}
@ -50,6 +53,9 @@ switches() {
doodad install-script switch.js switch-left.doodad
doodad install-script switch.js switch-right.doodad
# Tag the category for these doodads
for i in *.doodad; do doodad edit-doodad --tag "category=gizmos" $i; done
cp *.doodad ../../../assets/doodads/
cd ..
}
@ -77,6 +83,9 @@ trapdoors() {
doodad edit-doodad -q --tag direction=right trapdoor-right.doodad
doodad edit-doodad -q --tag direction=up trapdoor-up.doodad
# Tag the category for these doodads
for i in *.doodad; do doodad edit-doodad --tag "category=doors" $i; done
cp trapdoor-*.doodad ../../../assets/doodads/
cd ..
@ -93,6 +102,9 @@ azulians() {
red-wr{1,2,3,4}.png red-wl{1,2,3,4}.png azu-red.doodad
doodad install-script azulian-red.js azu-red.doodad
# Tag the category for these doodads
for i in *.doodad; do doodad edit-doodad --tag "category=creatures" $i; done
cp azu-*.doodad ../../../assets/doodads/
cd ..
@ -105,6 +117,9 @@ mobs() {
dive-left.png dive-right.png bird-red.doodad
doodad install-script bird.js bird-red.doodad
# Tag the category for these doodads
for i in *.doodad; do doodad edit-doodad --tag "category=creatures" $i; done
cp *.doodad ../../../assets/doodads/
cd ..
}
@ -117,6 +132,7 @@ objects() {
doodad convert -t "Start Flag" start-flag.png start-flag.doodad
for i in *.doodad; do doodad edit-doodad --tag "category=objects" $i; done
cp *.doodad ../../../assets/doodads/
cd ../crumbly-floor
@ -125,12 +141,14 @@ objects() {
fall1.png fall2.png fall3.png fall4.png fallen.png \
crumbly-floor.doodad
doodad install-script crumbly-floor.js crumbly-floor.doodad
for i in *.doodad; do doodad edit-doodad --tag "category=objects" $i; done
cp *.doodad ../../../assets/doodads/
cd ../box
doodad convert -t "Box" box-1.png box-2.png box-3.png box-4.png box.doodad
doodad install-script box.js box.doodad
for i in *.doodad; do doodad edit-doodad --tag "category=objects" $i; done
cp *.doodad ../../../assets/doodads/
cd ..
@ -148,6 +166,8 @@ onoff() {
doodad convert -t "State Block (Orange)" orange-off.png orange-on.png state-block-orange.doodad
doodad install-script state-block-orange.js state-block-orange.doodad
for i in *.doodad; do doodad edit-doodad --tag "category=gizmos" $i; done
cp *.doodad ../../../assets/doodads/
cd ..
@ -170,6 +190,9 @@ warpdoor() {
doodad edit-doodad -q --tag color=orange warp-door-orange.doodad
doodad install-script warp-door.js warp-door-orange.doodad
for i in *.doodad; do doodad edit-doodad --tag "category=doors" $i; done
for i in warp-door-*.doodad; do doodad edit-doodad --tag "category=doors,gizmos" $i; done
cp *.doodad ../../../assets/doodads/
cd ..

View File

@ -45,4 +45,8 @@ doodad install-script keys.js small-key.doodad
doodad convert -t "Electric Door" electric{1,2,3,4}.png door-electric.doodad
doodad install-script electric-door.js door-electric.doodad
# Tag the category for these doodads
for i in *.doodad; do doodad edit-doodad --tag "category=doors" $i; done
doodad edit-doodad --tag "category=doors,gizmos" door-electric.doodad
cp door-*.doodad key-*.doodad small-*.doodad ../../../assets/doodads/

8
go.mod
View File

@ -5,9 +5,9 @@ go 1.16
require (
git.kirsle.net/go/audio v0.0.0-20200429055451-ae3b0695ba6f
git.kirsle.net/go/log v0.0.0-20200902035305-70ac2848949b
git.kirsle.net/go/render v0.0.0-20210104010442-b4a1979a8ba1
git.kirsle.net/go/render v0.0.0-20210614025954-d77f5056b782
git.kirsle.net/go/ui v0.0.0-20200710023146-e2a561fbd04c
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/fsnotify/fsnotify v1.4.9
github.com/gen2brain/dlgs v0.0.0-20210406143744-f512297a108e
github.com/google/uuid v1.1.2
@ -16,9 +16,9 @@ require (
github.com/robertkrimen/otto v0.0.0-20200922221731-ef014fd054ac
github.com/tomnomnom/xtermcolor v0.0.0-20160428124646-b78803f00a7e // indirect
github.com/urfave/cli/v2 v2.3.0
github.com/veandco/go-sdl2 v0.4.7
github.com/veandco/go-sdl2 v0.4.8
github.com/vmihailenco/msgpack v3.3.3+incompatible
golang.org/x/image v0.0.0-20210504121937-7319ad40d33e
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d
google.golang.org/appengine v1.6.7 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/sourcemap.v1 v1.0.5 // indirect

9
go.sum
View File

@ -191,8 +191,8 @@ github.com/tomnomnom/xtermcolor v0.0.0-20160428124646-b78803f00a7e/go.mod h1:fSI
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/veandco/go-sdl2 v0.4.1/go.mod h1:FB+kTpX9YTE+urhYiClnRzpOXbiWgaU3+5F2AB78DPg=
github.com/veandco/go-sdl2 v0.4.7 h1:VfpCM+LfEGDbHdByglCo2bcBsevjFvzl8W0f6VLNitg=
github.com/veandco/go-sdl2 v0.4.7/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY=
github.com/veandco/go-sdl2 v0.4.8 h1:A26KeX6R1CGt/BQGEov6oxYmVGMMEWDVqTvK1tXvahE=
github.com/veandco/go-sdl2 v0.4.8/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY=
github.com/vmihailenco/msgpack v3.3.3+incompatible h1:wapg9xDUZDzGCNFlwc5SqI1rvcciqcxEHac4CYj89xI=
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
@ -220,8 +220,8 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20210504121937-7319ad40d33e h1:PzJMNfFQx+QO9hrC1GwZ4BoPGeNGhfeQEgcQFArEjPk=
golang.org/x/image v0.0.0-20210504121937-7319ad40d33e/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs=
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -285,6 +285,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@ -73,6 +73,13 @@ var (
PadX: 4,
}
// TabFrame styles.
TabFont = render.Text{
Size: 12,
PadX: 8,
PadY: 4,
}
// Modal backdrop color.
ModalBackdrop = render.RGBA(1, 1, 1, 42)

10
pkg/doodads/sort.go Normal file
View File

@ -0,0 +1,10 @@
package doodads
import "sort"
// SortByName orders an array of loaded Doodads by their titles.
func SortByName(list []*Doodad) {
sort.SliceStable(list, func(i, j int) bool {
return list[i].Title < list[j].Title
})
}

View File

@ -3,6 +3,7 @@ package windows
import (
"fmt"
"math"
"strings"
"git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/doodads"
@ -40,33 +41,9 @@ func NewDoodadDropper(config DoodadDropper) *ui.Window {
// size of the doodad window
width = buttonSize * columns
height = (buttonSize * rows) + 64 // account for button borders :(
// pagination values
page = 1
pages int
perPage = 20
maxPageButtons = 10
)
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
*******/
// Get all the doodads.
doodadsAvailable, err := doodads.ListDoodads()
if err != nil {
log.Error("NewDoodadDropper: doodads.ListDoodads: %s", err)
@ -90,6 +67,76 @@ func NewDoodadDropper(config DoodadDropper) *ui.Window {
items = append(items, doodad)
}
window := ui.NewWindow(title)
window.SetButtons(ui.CloseButton)
window.Configure(ui.Config{
Width: width,
Height: height + 30,
Background: render.Grey,
})
tabFrame := ui.NewTabFrame("Category Tabs")
window.Pack(tabFrame, ui.Pack{
Side: ui.N,
Fill: true,
Expand: true,
})
// The Category Tabs.
categories := []struct {
ID string
Name string
}{
{"objects", "Objects"},
{"doors", "Doors"},
{"gizmos", "Gizmos"},
{"creatures", "Creatures"},
{"", "All"},
}
for _, category := range categories {
tab1 := tabFrame.AddTab(category.Name, ui.NewLabel(ui.Label{
Text: category.Name,
Font: balance.TabFont,
}))
makeDoodadTab(config, tab1, render.NewRect(width-4, height-60), category.ID, items)
}
tabFrame.Supervise(config.Supervisor)
window.Hide()
return window
}
// Function to generate the TabFrame frame of the Doodads window.
func makeDoodadTab(config DoodadDropper, frame *ui.Frame, size render.Rect, category string, available []*doodads.Doodad) {
var (
buttonSize = balance.DoodadButtonSize
columns = balance.DoodadDropperCols
rows = balance.DoodadDropperRows
// pagination values
page = 1
pages int
perPage = 20
maxPageButtons = 10
)
frame.Resize(size)
// Trim the available doodads to those fitting the category.
var items = []*doodads.Doodad{}
for _, candidate := range available {
if value, ok := candidate.Tags["category"]; ok {
if category != "" && !strings.Contains(value, category) {
continue
}
} else if category != "" {
continue
}
items = append(items, candidate)
}
doodads.SortByName(items)
// Compute the number of pages for the pager widget.
pages = int(
math.Ceil(
@ -176,7 +223,7 @@ func NewDoodadDropper(config DoodadDropper) *ui.Window {
bottomFrame := ui.NewFrame("Button Frame")
frame.Pack(bottomFrame, ui.Pack{
Side: ui.S,
Side: ui.N,
FillX: true,
})
@ -242,7 +289,4 @@ func NewDoodadDropper(config DoodadDropper) *ui.Window {
})
}
}
window.Hide()
return window
}

View File

@ -50,17 +50,9 @@ func MakeSettingsWindow(windowWidth, windowHeight int, cfg Settings) *ui.Window
// NewSettingsWindow initializes the window.
func NewSettingsWindow(cfg Settings) *ui.Window {
var (
Width = 400
Height = 400
ActiveTab = "index"
// The tab frames
TabOptions *ui.Frame // index
TabControls *ui.Frame // controls
Width = 400
Height = 400
)
if cfg.ActiveTab != "" {
ActiveTab = cfg.ActiveTab
}
window := ui.NewWindow("Settings")
window.SetButtons(ui.CloseButton)
@ -72,63 +64,22 @@ func NewSettingsWindow(cfg Settings) *ui.Window {
///////////
// Tab Bar
tabFrame := ui.NewFrame("Tab Bar")
tabFrame := ui.NewTabFrame("Tab Frame")
tabFrame.SetBackground(render.DarkGrey)
window.Pack(tabFrame, ui.Pack{
Side: ui.N,
FillX: true,
})
for _, tab := range []struct {
label string
value string
}{
{"Options", "index"},
{"Controls", "controls"},
} {
radio := ui.NewRadioButton(tab.label, &ActiveTab, tab.value, ui.NewLabel(ui.Label{
Text: tab.label,
Font: balance.UIFont,
}))
radio.SetStyle(&balance.ButtonBabyBlue)
radio.Handle(ui.Click, func(ed ui.EventData) error {
switch ActiveTab {
case "index":
TabOptions.Show()
TabControls.Hide()
case "controls":
TabOptions.Hide()
TabControls.Show()
}
return nil
})
cfg.Supervisor.Add(radio)
tabFrame.Pack(radio, ui.Pack{
Side: ui.W,
Expand: true,
})
}
///////////
// Options (index) Tab
TabOptions = cfg.makeOptionsTab(Width, Height)
if ActiveTab != "index" {
TabOptions.Hide()
}
window.Pack(TabOptions, ui.Pack{
Side: ui.N,
FillX: true,
})
cfg.makeOptionsTab(tabFrame, Width, Height)
///////////
// Controls Tab
TabControls = cfg.makeControlsTab(Width, Height)
if ActiveTab != "controls" {
TabControls.Hide()
}
window.Pack(TabControls, ui.Pack{
Side: ui.N,
FillX: true,
})
cfg.makeControlsTab(tabFrame, Width, Height)
tabFrame.Supervise(cfg.Supervisor)
return window
}
@ -143,8 +94,12 @@ func saveGameSettings() {
}
// Settings Window "Options" Tab
func (c Settings) makeOptionsTab(Width, Height int) *ui.Frame {
tab := ui.NewFrame("Options Tab")
func (c Settings) makeOptionsTab(tabFrame *ui.TabFrame, Width, Height int) *ui.Frame {
tab := tabFrame.AddTab("Options", ui.NewLabel(ui.Label{
Text: "Options",
Font: balance.TabFont,
}))
tab.Resize(render.NewRect(Width-4, Height-tab.Size().H-46))
// Common click handler for all settings,
// so we can write the updated info to disk.
@ -307,7 +262,13 @@ func (c Settings) makeOptionsTab(Width, Height int) *ui.Frame {
}
// Settings Window "Controls" Tab
func (c Settings) makeControlsTab(Width, Height int) *ui.Frame {
func (c Settings) makeControlsTab(tabFrame *ui.TabFrame, Width, Height int) *ui.Frame {
frame := tabFrame.AddTab("Controls", ui.NewLabel(ui.Label{
Text: "Controls",
Font: balance.TabFont,
}))
frame.Resize(render.NewRect(Width-4, Height-frame.Size().H-46))
var (
halfWidth = (Width - 4) / 2 // the 4 is for window borders, TODO
shortcutTabWidth = float64(halfWidth) * 0.5
@ -317,7 +278,6 @@ func (c Settings) makeControlsTab(Width, Height int) *ui.Frame {
shortcutTabSize = render.NewRect(int(shortcutTabWidth), rowHeight)
infoTabSize = render.NewRect(int(infoTabWidth), rowHeight)
)
frame := ui.NewFrame("Controls Tab")
controls := []struct {
Header string