Files
8bitMyGoUI/gallery/demos_popups.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

190 lines
6.5 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"
"time"
"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/overlay"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
)
var popupEntries = []Entry{
{186, "ChoicePrompt", "选项提示", "overlay", "A question and a hand. Arrows and Enter, or press 1–9.", demoChoicePrompt},
{187, "Coachmark", "新手引导", "overlay", "A guided tour: a bubble points at each button in turn.", demoCoachmark},
{188, "Spotlight", "探照灯", "overlay", "Everything but one button sinks into dither.", demoSpotlight},
{189, "Notification", "通知", "overlay", "Cards stack at the top right and leave on their own. × sends one away.", demoNotification},
{190, "Popconfirm", "气泡确认", "overlay", "A small YES / NO bubble before the button acts.", demoPopconfirm},
{191, "PauseMenu", "暂停菜单", "overlay", "PAUSED blinks over the game. Escape resumes.", demoPauseMenu},
{192, "PromptDialog", "输入对话框", "overlay", "ENTER YOUR NAME, eight letters at most.", demoPromptDialog},
{193, "ProgressDialog", "进度对话框", "overlay", "Saving, with a way out.", demoProgressDialog},
{194, "KeyHints", "按键提示", "overlay", "Pad buttons and keycaps beside what they do.", demoKeyHints},
{195, "TitleCard", "关卡标题", "overlay", "WORLD 1-1 dithers in, waits, and dithers out.", demoTitleCard},
}
func demoChoicePrompt(c *ui.Context, log func(string)) {
s := overlayState(c)
choices := []string{"Fight", "Talk", "Flee"}
if input.Button(c, "A SLIME APPEARS", input.ButtonOptions{}).Clicked {
s.choice = true
log("open")
}
r := overlay.ChoicePrompt(c, "A wild slime blocks the road. It wobbles.", choices, &s.choice, overlay.ChoicePromptOptions{})
if r.Chosen >= 0 {
log(choices[r.Chosen])
}
if r.Canceled {
log("stared at the slime")
}
}
func demoCoachmark(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := overlayState(c)
tips := []string{"Your bag. Potions live here.", "The map. You are the red dot.", "Save often. Slimes are tricky."}
var targets [3]*ui.Element
ui.Row(c).Gap(k.D(6)).Children(func() {
for i, b := range []struct {
art *pixel.Sprite
name string
}{{icon.Chest, "Bag"}, {icon.Map, "Map"}, {icon.Save, "Save"}} {
targets[i] = input.Button(c, b.name, input.ButtonOptions{Icon: b.art}).Element
}
})
if s.step > len(tips) {
if input.Button(c, "TOUR AGAIN", input.ButtonOptions{Size: input.Small}).Clicked {
s.step = 1
log("tour again")
}
return
}
if overlay.Coachmark(c, targets[s.step-1], tips[s.step-1], s.step, len(tips), overlay.CoachmarkOptions{}).Next {
s.step++
log(fmt.Sprintf("step %d", s.step))
}
}
func demoSpotlight(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := overlayState(c)
var shop *ui.Element
ui.Row(c).Gap(k.D(6)).Children(func() {
input.Button(c, "INN", input.ButtonOptions{})
shop = input.Button(c, "SHOP", input.ButtonOptions{Icon: icon.Cart}).Element
input.Button(c, "GUILD", input.ButtonOptions{})
})
if input.Toggle(c, "SPOTLIGHT", &s.spot, input.ToggleOptions{Size: input.Small}).Changed {
log(fmt.Sprintf("spotlight %v", s.spot))
}
if s.spot {
overlay.Spotlight(c, shop, "Potions are on sale!", overlay.SpotlightOptions{})
}
}
func demoNotification(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := overlayState(c)
ui.Row(c).Gap(k.D(4)).Children(func() {
if input.Button(c, "NEW MAIL", input.ButtonOptions{Size: input.Small}).Clicked {
s.notes = append(s.notes, overlay.Notification{Icon: icon.Mail, Title: "Letter from Mom", Text: "Eat your vegetables."})
log("mail")
}
if input.Button(c, "LEVEL UP", input.ButtonOptions{Size: input.Small}).Clicked {
s.notes = append(s.notes, overlay.Notification{Icon: icon.Star, Title: "Level 8!", Text: "STR +2, LUCK +1.", For: 8 * time.Second})
log("level up")
}
})
if r := overlay.Notifications(c, &s.notes, overlay.NotificationsOptions{}); r.Dismissed != nil {
log("dismissed " + r.Dismissed.Title)
}
}
func demoPopconfirm(c *ui.Context, log func(string)) {
r := overlay.Popconfirm(c, "SELL CROWN", "Sell it for 5 gold?", overlay.PopconfirmOptions{})
if r.Confirmed {
log("sold. what a deal.")
}
if r.Canceled {
log("kept the crown")
}
}
func demoPauseMenu(c *ui.Context, log func(string)) {
s := overlayState(c)
if input.Button(c, "PAUSE", input.ButtonOptions{Icon: icon.Pause}).Clicked {
s.pause = true
log("open")
}
if r := overlay.PauseMenu(c, &s.pause, overlay.PauseMenuOptions{}); r.Chosen >= 0 {
log([]string{"resume", "options", "quit"}[r.Chosen])
}
}
func demoPromptDialog(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := overlayState(c)
ui.Row(c).Gap(k.D(6)).AlignItems(ui.Center).Children(func() {
if input.Button(c, "NEW GAME", input.ButtonOptions{}).Clicked {
s.prompt = true
log("open")
}
if s.name != "" {
pixel.Text(c, "Hero: "+s.name, pixel.TextOptions{})
}
})
r := overlay.PromptDialog(c, "ENTER YOUR NAME", &s.name, &s.prompt, overlay.PromptDialogOptions{Placeholder: "LINK", MaxLength: 8})
if r.Submitted {
log("named " + s.name)
}
if r.Canceled {
log("canceled")
}
}
func demoProgressDialog(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := overlayState(c)
if input.Button(c, "SAVE", input.ButtonOptions{Icon: icon.Save}).Clicked {
s.saving, s.since = true, k.Now()
log("saving")
}
// The pretend save takes three seconds.
done := 0
if s.saving {
done = k.Since(c, s.since, 10, 30)
}
if done == 30 {
s.saving = false
log("saved")
}
if overlay.ProgressDialog(c, "Saving", float64(done)/30, &s.saving, overlay.ProgressDialogOptions{}).Canceled {
log(fmt.Sprintf("canceled at %d%%", done*100/30))
}
}
func demoKeyHints(c *ui.Context, log func(string)) {
overlay.KeyHints(c, []overlay.KeyHint{{Key: "A", Action: "CONFIRM"}, {Key: "B", Action: "BACK"}, {Key: "START", Action: "MENU"}}, overlay.KeyHintsOptions{})
}
func demoTitleCard(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := overlayState(c)
layout.Layers(c, layout.LayersOptions{}, func() {
ui.Box(c).Fill().Draw(func(p *ui.Painter, r ui.Rect) {
g := k.Canvas(p, r)
g.Dither(0, 0, g.W, g.H, pixel.Well, pixel.Info, 4)
g.Rect(0, g.H-12, g.W, 12, pixel.Good)
})
}, func() {
overlay.TitleCard(c, "WORLD 1-1", overlay.TitleCardOptions{Cue: s.cue})
}).Size(k.D(200), k.D(90))
if input.Button(c, "ENTER WORLD", input.ButtonOptions{Size: input.Small}).Clicked {
s.cue++
log("world 1-1")
}
}