Files
8bitMyGoUI/account/charactercreator.go
T
ZacharyZhang-NYandClaude Opus 5.5 03e1f91706 Upgrade MyGo to v0.5.5
Elements are now pass-scoped value handles: tests that read bounds
after a frame bind a ui.Handle. pixel tells frames apart by the build
pass's root, since the Tester clock no longer moves on its own.

Fix the regressions the upgrade brought:
- Clipboard reads in input handlers go through c.Services().
- MyGo notes an overlay's opener after building it, so AutoFocus
  inside lost it; pixel.AutoFocus focuses through a Handle, and
  archtest bans the raw call. Dialogs hand the focus back again.
- Elements around a press stay hovered: Tooltip tells a press-given
  focus by focus arriving under the pointer.
- The web host moves the Tester clock with real time, at most 500 ms
  a call, and gives components the real clock.

The gallery's source index also accepts CRLF checkouts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 01:31:08 -04:00

159 lines
3.9 KiB
Go

package account
import (
"log/slog"
"strings"
"github.com/egoist/mygo/ui"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
)
// Look is a character's parts and colors, each an index.
type Look struct {
Hair, Eyes, Outfit, HairColor, OutfitColor int
}
// hues color hair and outfits, as "hues" names them.
var hues = []pixel.Slot{pixel.Ink, pixel.Warn, pixel.Bad, pixel.Magic, pixel.Info}
var (
head = pixel.MustSprite(`
................
................
................
.....eeeeee.....
....elllllle....
....elllllle....
....elllllle....
....elllllle....
.....elllle.....
......eeee......
................
................
................
................
................
................
`)
// hair is SPIKY, BOB, BALD; # takes the hair color.
hair = []*pixel.Sprite{pixel.MustSprite(`
....#..#..#.....
....##.##.##....
....########....
...##########...
...##......##...
...#........#...
`), pixel.MustSprite(`
................
................
.....######.....
....########....
...##########...
...##......##...
...##......##...
...##......##...
...##......##...
`), pixel.MustSprite(`
................
`)}
// eyes are DOTS, WIDE, SLEEPY, from the face's fifth column.
eyes = []*pixel.Sprite{pixel.MustSprite(`
.......
.......
.......
.......
.......
.......
..k..k.
`), pixel.MustSprite(`
.......
.......
.......
.......
.......
..k..k.
..k..k.
`), pixel.MustSprite(`
.......
.......
.......
.......
.......
.......
.kk..kk
`)}
// outfits are TUNIC, ROBE, ARMOR; # takes the outfit color.
outfits = []*pixel.Sprite{pixel.MustSprite(`
.....e####e.....
....e######e....
...e########e...
...l########l...
....########....
....##....##....
`), pixel.MustSprite(`
.....e####e.....
....e######e....
...e########e...
...l########l...
...##########...
..############..
`), pixel.MustSprite(`
.....e####e.....
....e#ssss#e....
...e#ssssss#e...
...l#ssssss#l...
....#ssssss#....
....##....##....
`)}
)
type CharacterCreatorOptions struct {
Disabled bool
}
type CharacterCreatorResult struct {
Element ui.Element
Changed bool
}
// CharacterCreator cycles hair, eyes, outfit and colors; a live avatar.
func CharacterCreator(c *ui.Context, text string, look *Look, o CharacterCreatorOptions) CharacterCreatorResult {
k := pixel.K(c)
styles, looks, wears, hueNames := words(k, "styles"), words(k, "looks"), words(k, "outfits"), words(k, "hues")
var res CharacterCreatorResult
res.Element = ui.Row(c).Gap(k.D(8)).AlignItems(ui.Center).Role(ui.RoleGroup).Label(text).Children(func() {
shown := *look
say := strings.Join([]string{styles[look.Hair], looks[look.Eyes], wears[look.Outfit], hueNames[look.HairColor], hueNames[look.OutfitColor]}, ", ")
ui.Box(c).Size(k.D(56), k.D(56)).Role(ui.RoleImage).Label(say).Draw(func(p *ui.Painter, r ui.Rect) {
g := k.Canvas(p, r)
g.Frame(0, 0, 56, 56, pixel.Sunken, pixel.Well)
g.Sprite(4, 4, head, 0, pixel.Ink, 3)
g.Sprite(4+3*4, 4, eyes[shown.Eyes], 0, pixel.Ink, 3)
g.Sprite(4, 4, hair[shown.Hair], 0, hues[shown.HairColor], 3)
g.Sprite(4, 4+3*10, outfits[shown.Outfit], 0, hues[shown.OutfitColor], 3)
})
ui.Column(c).Gap(k.D(2)).Children(func() {
for _, row := range []struct {
key string
names []string
at *int
}{
{"hair", styles, &look.Hair}, {"eyes", looks, &look.Eyes}, {"outfit", wears, &look.Outfit},
{"hair_color", hueNames, &look.HairColor}, {"outfit_color", hueNames, &look.OutfitColor},
} {
ui.Row(c).Gap(k.D(4)).AlignItems(ui.Center).Children(func() {
mark(c, k, k.Msg(row.key), pixel.Faint, 64)
if OptionCycler(c, k.Msg(row.key), row.names, row.at, OptionCyclerOptions{Width: 48, Disabled: o.Disabled}).Changed {
res.Changed = true
}
})
}
})
})
if res.Changed {
slog.Debug("8bit: character creator", "label", text, "look", *look)
}
return res
}