Files
ZacharyZhang-NY 51af14709d Add code components 366-385; ScrollArea Follow, editor sync
A terminal with history, a Go-colored code block, a log viewer that
follows, a hex viewer, a memory map, a JSON tree, a line diff, a GAME
OVER crash screen, an FPS meter, profiler bars, breakpoints,
variables, command history, a shortcut list, a debug grid, a pixel
editor that exports string art, a palette editor, a sprite sheet, a
glyph map and a 6502 register view.

layout.ScrollArea takes Follow, and ChatLog now uses it. data.TreeNode
takes a Value drawn in its Slot. pixel gains Has, and Sprite.Art
writes MustSprite art. pixel.Editor takes the caller's value at each
input event, so a value changed between frames is never typed onto.
2026-10-12 07:18:30 -04:00

162 lines
5.7 KiB
Go

package gallery
import (
"fmt"
"strings"
"github.com/egoist/mygo/ui"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/code"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/input"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
)
var debugEntries = []Entry{
{376, "Breakpoints", "断点", "code", "Click a line, or press Enter on it, to stop there next time.", demoBreakpoints},
{377, "Variables", "变量", "code", "Names, types and values; open a struct to see inside.", demoVariables},
{378, "CommandHistory", "命令历史", "code", "Everything you typed, numbered. Enter runs it again.", demoCommandHistory},
{379, "ShortcutList", "快捷键表", "code", "Actions beside their keycaps. Do not trust the last one.", demoShortcutList},
{380, "DebugGrid", "调试网格", "code", "The dot grid laid over anything, with rulers in dots.", demoDebugGrid},
{381, "PixelEditor", "像素编辑器", "code", "Pen, fill, eraser, picker. Arrows and Enter paint too. The art below is live.", demoPixelEditor},
{382, "PaletteEditor", "调色板编辑", "code", "Sixteen colors, sixteen levels a channel, like the old chips. The preview keeps up.", demoPaletteEditor},
{383, "SpriteSheet", "精灵表", "code", "Every frame in a grid; PLAY spins the coin.", demoSpriteSheet},
{384, "GlyphMap", "字形表", "code", "128 code points a page; PageUp and PageDown turn it. Dots mark what the font lacks.", demoGlyphMap},
{385, "RegisterView", "寄存器", "code", "A 6502's registers and flags. STEP runs a line; what it changed lights up.", demoRegisterView},
}
const routine = `func jump(h *Hero) {
if h.OnGround {
h.VY = -4
}
h.Y += h.VY
}`
func demoBreakpoints(c *ui.Context, log func(string)) {
s := codeState(c)
if code.Breakpoints(c, "jump.go", routine, s.marks, code.BreakpointsOptions{}).Changed {
log(fmt.Sprintf("breakpoints on %d lines", len(s.marks)))
}
}
func demoVariables(c *ui.Context, log func(string)) {
s := codeState(c)
vars := []code.Var{
{Name: "hero", Type: "Hero", Value: "{…}", Fields: []code.Var{
{Name: "Name", Type: "string", Value: `"Aria"`}, {Name: "HP", Type: "int", Value: "42"},
{Name: "Pos", Type: "Vec", Value: "{…}", Fields: []code.Var{{Name: "X", Type: "float64", Value: "12.5"}, {Name: "Y", Type: "float64", Value: "-3"}}},
{Name: "Flying", Type: "bool", Value: "false"},
}},
{Name: "target", Type: "*Enemy", Value: "nil"},
{Name: "lives", Type: "uint8", Value: "3"},
}
if r := code.Variables(c, "Locals", vars, &s.vr, code.VariablesOptions{Width: 220}); r.Changed || r.Toggled {
log("at " + s.vr)
}
}
func demoCommandHistory(c *ui.Context, log func(string)) {
s := codeState(c)
r := code.CommandHistory(c, "History", s.history, &s.pick, code.CommandHistoryOptions{Height: 70})
if r.Rerun {
log("rerun " + s.history[s.pick])
} else if r.Changed {
log("picked " + s.history[s.pick])
}
}
func demoShortcutList(c *ui.Context, log func(string)) {
code.ShortcutList(c, "Controls", []code.Shortcut{
{Action: "Save", Keys: []string{"Ctrl", "S"}}, {Action: "Jump", Keys: []string{"Space"}},
{Action: "Pause", Keys: []string{"Esc"}},
{Action: "Free gold", Keys: []string{"Alt", "F4"}},
}, code.ShortcutListOptions{Width: 300})
}
func demoDebugGrid(c *ui.Context, log func(string)) {
k := pixel.K(c)
code.DebugGrid(c, code.DebugGridOptions{}, func() {
ui.Column(c).Padding(k.D(8)).Gap(k.D(6)).Children(func() {
if input.Button(c, "ALIGNED?", input.ButtonOptions{}).Clicked {
log("pressed on the grid")
}
pixel.Text(c, "Every edge on a dot.", pixel.TextOptions{})
})
})
}
const heartArt = `..rr..rr..
.rrrrrrrr.
rrlrrrrrrr
rrrrrrrrrr
.rrrrrrrr.
..rrrrrr..
...rrrr...
....rr....
`
func demoPixelEditor(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := codeState(c)
ui.Column(c).Gap(k.D(6)).Children(func() {
r := code.PixelEditor(c, "Heart", &s.art, code.PixelEditorOptions{})
if r.Changed {
log("painted")
}
if r.Copied {
log("art copied")
}
for _, l := range strings.Split(strings.TrimSpace(s.art), "\n") {
pixel.Text(c, l, pixel.TextOptions{Slot: pixel.Faint})
}
})
}
func demoPaletteEditor(c *ui.Context, log func(string)) {
s := codeState(c)
if code.PaletteEditor(c, "Palette copy", &s.pal, code.PaletteEditorOptions{}).Changed {
log("palette edited")
}
}
func demoSpriteSheet(c *ui.Context, log func(string)) {
s := codeState(c)
if code.SpriteSheet(c, "Coin", coin, &s.frame, &s.playing, code.SpriteSheetOptions{Scale: 3}).Changed {
log(fmt.Sprintf("frame %d, playing %v", s.frame+1, s.playing))
}
}
func demoGlyphMap(c *ui.Context, log func(string)) {
s := codeState(c)
if code.GlyphMap(c, "Font", &s.glyph, code.GlyphMapOptions{}).Changed {
log(fmt.Sprintf("U+%04X", s.glyph))
}
}
// program is 6502 lines and the CPU after each.
var program = []struct {
line string
cpu code.CPU
}{
{"RESET", code.CPU{SP: 0xFD, P: 0x24, PC: 0xC000}},
{"LDA #$3F", code.CPU{A: 0x3F, SP: 0xFD, P: 0x24, PC: 0xC002}},
{"TAX", code.CPU{A: 0x3F, X: 0x3F, SP: 0xFD, P: 0x24, PC: 0xC003}},
{"INX", code.CPU{A: 0x3F, X: 0x40, SP: 0xFD, P: 0x24, PC: 0xC004}},
{"ADC #$C1", code.CPU{A: 0x00, X: 0x40, SP: 0xFD, P: 0x27, PC: 0xC006}},
{"PHA", code.CPU{A: 0x00, X: 0x40, SP: 0xFC, P: 0x27, PC: 0xC007}},
{"DEY", code.CPU{A: 0x00, X: 0x40, Y: 0xFF, SP: 0xFC, P: 0xA5, PC: 0xC008}},
}
func demoRegisterView(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := codeState(c)
p := program[s.step]
ui.Column(c).Gap(k.D(6)).Children(func() {
pixel.Text(c, fmt.Sprintf("$%04X %s", p.cpu.PC, p.line), pixel.TextOptions{Slot: pixel.Accent})
code.RegisterView(c, "CPU", p.cpu, code.RegisterViewOptions{})
if input.Button(c, "STEP", input.ButtonOptions{Variant: input.Primary}).Clicked {
s.step = (s.step + 1) % len(program)
log(program[s.step].line)
}
})
}