Files
8bitMyGoUI/gallery/demos_foundations.go
T
ZacharyZhang-NY b4b4d67b92 Audit keyboard, focus, veils, motion and clocks across all 500
A cross-cutting pass, each fix with a test that failed first.

Veils: MyGo still hands a key to a base that turned off this frame,
so every element key now goes through pixel.Shortcut, which an
archtest enforces. Input handlers record and the build applies them
only while enabled; the editor turns away a veil-frame letter or
copy; value bases get a copy and keep only enabled turns. Popups
give a held focus back to their opener when the veil lifts.

Motion: S5 is enforced at the clock. Every Tick, Since, Step,
Toward and Shot runs at 4-15 fps; blinks keep 1 Hz on 4 fps steps,
fast animations step at 15, and drawn time is quantized to frames.

Clocks stepping back: Since holds at zero without overflow, and
k.Elapsed moves a stored start to now, so nothing goes negative,
runs backward or indexes out of range.

Also: logs count typed text instead of holding it, gallery names
use glyphs the font has, removable rows are keyed, components take
their own Options (archtest), and sweeps over all 508 demos check
veiled keys, clicks, wheels, the clock stepping back and names.
2026-10-12 16:03:04 -04:00

212 lines
7.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package gallery
import (
"fmt"
"github.com/egoist/mygo/ui"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/icon"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/input"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/layout"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
)
var foundationEntries = []Entry{
{0, "Palettes", "调色板", "foundations", "Eight palettes of sixteen slots: four seasons, by day and by night. Components name slots; rotating the palette repaints everything.", demoPalettes},
{0, "Canvas", "画布", "foundations", "Rectangles, Bresenham lines, midpoint circles and six frame styles, all snapped to the dot grid.", demoCanvas},
{0, "Dither", "抖动", "foundations", "Ordered Bayer dither stands in for gradients, shadows and translucency: seventeen levels between two slots.", demoDither},
{0, "Pixel Font", "像素字体", "foundations", "Ark Pixel 12px with CJK, at integer scales, plus a hand-placed 3×5 micro font.", demoFont},
{0, "Icons", "图标", "foundations", "One hundred and nine icons placed dot by dot. Click one to name it.", demoIcons},
{0, "Sprites", "精灵", "foundations", "Sprites are string art in slot letters, with frames stepped by the clock. Click to turn around.", demoSprites},
{0, "Editor", "编辑器", "foundations", "One text editing core drawn in the pixel font: caret, selection, clipboard, undo and input methods.", demoEditor},
{0, "Focus", "焦点", "foundations", "Tab to see the focus ring and the pointing hand. Disabled controls dither out.", demoFocus},
}
func demoPalettes(c *ui.Context, log func(string)) {
k := pixel.K(c)
ui.Grid(c).Columns(2).Gap(k.D(6)).Children(func() {
for _, pal := range pixel.Palettes {
s := pixel.Current(c)
s.Palette = pal
pixel.Scope(c, s, func() {
kp := pixel.K(c)
shell, base, st := pixel.Shell(c, func() *ui.Element { return ui.ButtonBase(c).Label(pal.Name) })
shell.Height(kp.D(52)).Draw(func(p *ui.Painter, r ui.Rect) {
g := kp.Canvas(p, r)
g.Frame(0, 0, g.W, g.H, pixel.Window, pixel.Face)
g.Text(6, 4, pal.Name+" "+pal.Zh, pixel.Ink, 1)
g.Rect(5, 23, pixel.Slots*5+2, 10, pixel.Edge)
for i := range pixel.Slots {
g.Rect(6+i*5, 24, 5, 8, pixel.Slot(i))
}
g.Sprite(g.W-46, 4, icon.Heart, 0, pixel.Bad, 1)
g.Sprite(g.W-32, 4, icon.Star, 0, pixel.Warn, 1)
g.Sprite(g.W-18, 4, icon.Coin, 0, pixel.Ink, 1)
g.Text(6, 34, "HP 99 · 你好", pixel.Accent, 1)
if st.Focus {
g.FocusMark(0, 0, g.W, g.H)
}
})
if base.Clicked() {
log("palette " + pal.Name)
}
})
}
})
}
func demoCanvas(c *ui.Context, log func(string)) {
k := pixel.K(c)
styles := []string{"RAISED", "SUNKEN", "FLAT", "DOUBLE", "WINDOW", "NOTCHED"}
ui.Box(c).Height(k.D(132)).Draw(func(p *ui.Painter, r ui.Rect) {
g := k.Canvas(p, r)
for i, name := range styles {
x := (i % 3) * 64
y := (i / 3) * 34
g.Frame(x, y, 58, 28, pixel.FrameStyle(i), pixel.Face)
g.Micro(x+(58-pixel.MicroWidth(name))/2, y+12, name, pixel.Ink, 1)
}
for i := range 8 {
g.Line(220, 100, 220+(i-4)*12, 72, pixel.Slot(8+i))
}
g.Disc(30, 104, 20, pixel.Info)
g.Circle(30, 104, 24, pixel.Accent)
g.Disc(90, 104, 12, pixel.Warn)
g.Circle(90, 104, 16, pixel.Bad)
g.Bevel(120, 84, 40, 40, pixel.Lit, pixel.Shade)
})
}
func demoDither(c *ui.Context, log func(string)) {
k := pixel.K(c)
ui.Box(c).Height(k.D(118)).Draw(func(p *ui.Painter, r ui.Rect) {
g := k.Canvas(p, r)
for level := range 17 {
g.Dither(level*12, 0, 12, 24, pixel.Face, pixel.Accent, level)
g.Micro(level*12+2, 27, fmt.Sprint(level), pixel.Faint, 1)
}
for band := range 8 {
g.Dither(0, 40+band*6, 204, 6, pixel.Info, pixel.Back, band*2)
}
g.Sprite(220, 44, icon.Chest, 0, pixel.Ink, 3)
g.Checker(220, 44, 36, 36, pixel.Face)
g.Micro(220, 84, "VEIL", pixel.Faint, 1)
})
}
func demoFont(c *ui.Context, log func(string)) {
k := pixel.K(c)
ui.Column(c).Gap(k.D(4)).Children(func() {
pixel.Text(c, "The quick brown fox jumps over the lazy dog.", pixel.TextOptions{})
pixel.Text(c, "在限制中创造丰富。每个点都经过认真摆放。", pixel.TextOptions{})
pixel.Text(c, "LEVEL 99", pixel.TextOptions{Scale: 2, Slot: pixel.Accent})
pixel.Text(c, "像素", pixel.TextOptions{Scale: 3, Slot: pixel.Magic})
ui.Box(c).Height(k.D(7)).Draw(func(p *ui.Painter, r ui.Rect) {
k.Canvas(p, r).Micro(0, 0, "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ +-.:/%!", pixel.Ink, 1)
})
pixel.Text(c, "Long text wraps at the width the layout gives it, and Chinese 中文也会按字断行,标点不会出现在行首。", pixel.TextOptions{Wrap: true, Slot: pixel.Faint})
})
}
func demoIcons(c *ui.Context, log func(string)) {
k := pixel.K(c)
ui.Row(c).Wrap().Gap(k.D(2)).Children(func() {
for _, n := range icon.All {
if input.IconButton(c, n.Sprite, n.Name, input.IconButtonOptions{Variant: input.Ghost}).Clicked {
log("icon " + n.Name)
}
}
})
}
var slime = pixel.MustSprite(`
................
................
......eeee......
....eeggggee....
...egllgggggge..
..egllgggggggge.
..eglggkggkggge.
..egggggkkgggge.
..eggggggggggse.
..egggggggggsse.
...essssssssse..
....eeeeeeeee...
................
................
................
................
.....eeeeee.....
...eegllggggee..
..egllggkggkgge.
.eglggggkkgggsge
.egggggggggggsse
.eggggggggggssse
..esssssssssssse
...eeeeeeeeeeee.
`)
var slimeLeft = slime.Mirror()
type demoKey string
func demoSprites(c *ui.Context, log func(string)) {
left := ui.Local(c.Root(), demoKey("sprites"), func() bool { return false })
sp := slime
if *left {
sp = slimeLeft
}
k := pixel.K(c)
shell, base, st := pixel.Shell(c, func() *ui.Element { return ui.ButtonBase(c).Label("slime") })
shell.Size(k.D(120), k.D(48)).Draw(func(p *ui.Painter, r ui.Rect) {
g := k.Canvas(p, r)
g.Rect(0, 40, g.W, 8, pixel.Good)
g.Dither(0, 40, g.W, 8, pixel.Good, pixel.Shade, 4)
step := g.Step(4)
g.Sprite(8+step%8*2, 16, sp, step/2, pixel.Ink, 2)
if st.Focus {
g.FocusMark(0, 0, g.W, g.H)
}
})
if base.Clicked() {
*left = !*left
log(fmt.Sprintf("turned, facing left %v", *left))
}
}
type editorDemo struct{ name, secret, notes string }
func demoEditor(c *ui.Context, log func(string)) {
k := pixel.K(c)
st := ui.Local(c.Root(), demoKey("editor"), func() editorDemo { return editorDemo{notes: "Two lines\nof notes"} })
field := func(v *string, o pixel.EditorOptions) {
layout.Panel(c, layout.PanelOptions{Style: pixel.Sunken, Fill: pixel.Well, Padding: 1}, func() {
e := pixel.Editor(c, v, o)
if e.Changed {
log(o.Label + " changed")
}
if e.Submitted {
log(o.Label + " submitted")
}
})
}
ui.Column(c).Gap(k.D(6)).Children(func() {
field(&st.name, pixel.EditorOptions{Label: "Name", Placeholder: "Your name 你的名字"})
field(&st.secret, pixel.EditorOptions{Label: "Password", Placeholder: "Password", Mask: '★'})
field(&st.notes, pixel.EditorOptions{Label: "Notes", Multiline: true})
})
}
func demoFocus(c *ui.Context, log func(string)) {
k := pixel.K(c)
ui.Row(c).Gap(k.D(16)).Padding(0, k.D(14)).Children(func() {
for _, name := range []string{"ATTACK", "MAGIC", "ITEM"} {
if input.Button(c, name, input.ButtonOptions{}).Clicked {
log(name)
}
}
input.Button(c, "RUN", input.ButtonOptions{Disabled: true})
})
}