Add fx components 291-320 and the home scene; Kit.Shot, Kit.Toward
Palette cycling, sky, sun and moon, clouds, starfield, rain, snow, petals, leaves, fireflies, waterfall, ocean, campfire, lava, aurora, lightning, mountains and a season scene; scanlines, vignette, screen shake, flash, dither fades, iris wipes, dissolves, mosaics, glitches, sparkles, explosions and confetti. Home opens on a live scene in the palette's season and a strip of all eight palettes. pixel adds Canvas.Cycled, Sprite.At, SpriteOf, Kit.Toward and Kit.Shot, which spends a cue under Reduce Motion so it never replays; display exports Reduce. Gallery grids and the sidebar are one Tab stop each.
This commit is contained in:
56 files changed
+3324
-101
No files matched your search
+1
-1
@@ -85,7 +85,7 @@ func TestSearch(t *testing.T) {
|
||||
t.Fatal("/ did not focus search")
|
||||
}
|
||||
tt.Type("dith")
|
||||
if !tt.HasText("1 results") || !tt.HasText("Dither") {
|
||||
if !tt.HasText("2 results") || !tt.HasText("Dither") || !tt.HasText("FadeDither") {
|
||||
t.Fatalf("results: %q", tt.Texts())
|
||||
}
|
||||
tt.Key(0, ui.KeyP)
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ func (a *App) sidebar(c *ui.Context) {
|
||||
g.VLine(g.W-2, 0, g.H, pixel.Shade)
|
||||
g.VLine(g.W-1, 0, g.H, pixel.Edge)
|
||||
}).Children(func() {
|
||||
ui.Column(c).Padding(k.D(4)).Gap(k.D(1)).Children(func() {
|
||||
ui.Column(c).Padding(k.D(4)).Gap(k.D(1)).FocusGroup(ui.Vertical).Children(func() {
|
||||
for _, cat := range Categories {
|
||||
n := count(cat.Key)
|
||||
path := "/cat/" + cat.Key
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
package gallery
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/fx"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/input"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/layout"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
|
||||
)
|
||||
|
||||
var fxEntries = []Entry{
|
||||
{291, "PaletteCycle", "调色板循环", "fx", "Four slots take turns, so the rings seem to flow.", demoPaletteCycle},
|
||||
{292, "Sky", "天空", "fx", "Blue by day; by night, stars that blink.", demoSky},
|
||||
{293, "SunMoon", "日月", "fx", "Up at six, down at six. The moon takes the night shift.", demoSunMoon},
|
||||
{294, "Clouds", "云", "fx", "Far clouds dawdle, near clouds hurry.", demoClouds},
|
||||
{295, "Starfield", "星空", "fx", "Three layers of stars rush past. Hyperspace at 12 fps.", demoStarfield},
|
||||
{296, "Rain", "雨", "fx", "Slanted streaks, each landing with a tiny splash.", demoRain},
|
||||
{297, "Snow", "雪", "fx", "Flakes sway down; near ones are big and quick.", demoSnow},
|
||||
{298, "Petals", "樱花瓣", "fx", "Petals ride the breeze, turning as they fall.", demoPetals},
|
||||
{299, "Leaves", "落叶", "fx", "Leaves swing side to side on the way down.", demoLeaves},
|
||||
{300, "Fireflies", "萤火虫", "fx", "Little lights wander, glow, and rest.", demoFireflies},
|
||||
{301, "Waterfall", "瀑布", "fx", "Four slots cycling down make the water fall.", demoWaterfallFx},
|
||||
{302, "Ocean", "海浪", "fx", "Wave bands roll in by cycling, with glints on top.", demoOcean},
|
||||
{303, "Fire", "火焰", "fx", "Three flicker frames, heat rising through them.", demoFire},
|
||||
{304, "Lava", "熔岩", "fx", "Hot bands flow; bubbles swell and pop. Do not touch.", demoLava},
|
||||
{305, "Aurora", "极光", "fx", "Curtains of light, their colors sliding sideways.", demoAurora},
|
||||
{306, "Lightning", "闪电", "fx", "A flash, then a forked bolt. No two strikes alike.", demoLightning},
|
||||
{307, "Mountains", "山脉", "fx", "Three ridges scroll at three speeds. Parallax!", demoMountains},
|
||||
{308, "SeasonScene", "四季场景", "fx", "Pick a season and an hour; the world repaints itself.", demoSeasonScene},
|
||||
}
|
||||
|
||||
type fxDemo struct {
|
||||
season, hour int
|
||||
strike, shake, flash, mosaic, glitch, boom int
|
||||
fade, iris, dissolve bool
|
||||
}
|
||||
|
||||
func fxState(c *ui.Context) *fxDemo {
|
||||
p := pixel.K(c).Palette()
|
||||
return ui.Local(c.Root(), demoKey("fx"), func() fxDemo { return fxDemo{season: int(p.Season), hour: hourOf(p)} })
|
||||
}
|
||||
|
||||
// hourOf suits p: morning by day, late evening by night.
|
||||
func hourOf(p *pixel.Palette) int {
|
||||
if p.Night {
|
||||
return 21
|
||||
}
|
||||
return 10
|
||||
}
|
||||
|
||||
// paletteOf is the settings in force with season's palette.
|
||||
func paletteOf(c *ui.Context, s pixel.Season, night bool) pixel.Settings {
|
||||
set := pixel.Current(c)
|
||||
set.Palette = pixel.Find(s, night)
|
||||
return set
|
||||
}
|
||||
|
||||
// over stacks an effect on a same-sized sky.
|
||||
func over(c *ui.Context, w, h int, effect func()) {
|
||||
layout.Layers(c, func() { fx.Sky(c, fx.SkyOptions{Width: w, Height: h}) }, effect)
|
||||
}
|
||||
|
||||
// portalBand rings the portal, outside in.
|
||||
var portalBand = []pixel.Slot{pixel.Accent, pixel.AccentLit, pixel.Magic, pixel.Info}
|
||||
|
||||
var portal = pixel.SpriteOf(15, 15, func(x, y int) (pixel.Slot, bool) {
|
||||
d := math.Hypot(float64(x-7), float64(y-7))
|
||||
return portalBand[int(d)%len(portalBand)], d < 7.5
|
||||
})
|
||||
|
||||
func demoPaletteCycle(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
ui.Row(c).Gap(k.D(8)).AlignItems(ui.Center).Children(func() {
|
||||
fx.PaletteCycle(c, portal, portalBand, fx.PaletteCycleOptions{Scale: 3, Label: "portal"})
|
||||
fx.PaletteCycle(c, portal, portalBand, fx.PaletteCycleOptions{Scale: 2, FPS: 4, Label: "slow portal"})
|
||||
})
|
||||
}
|
||||
|
||||
func demoSky(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
ui.Row(c).Gap(k.D(4)).Children(func() {
|
||||
for _, night := range []bool{false, true} {
|
||||
pixel.Scope(c, paletteOf(c, k.Palette().Season, night), func() {
|
||||
fx.Sky(c, fx.SkyOptions{Width: 68, Height: 64, Label: map[bool]string{false: "day sky", true: "night sky"}[night]})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func demoSunMoon(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := fxState(c)
|
||||
ui.Column(c).Gap(k.D(6)).Children(func() {
|
||||
pixel.Scope(c, paletteOf(c, k.Palette().Season, s.hour < 6 || s.hour >= 18), func() {
|
||||
over(c, 140, 72, func() {
|
||||
fx.SunMoon(c, s.hour, fx.SunMoonOptions{Width: 140, Height: 72, Label: fmt.Sprintf("%02d:00", s.hour)})
|
||||
})
|
||||
})
|
||||
if input.Stepper(c, "Hour", &s.hour, input.StepperOptions{Max: 23}).Changed {
|
||||
log(fmt.Sprintf("hour %02d", s.hour))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func demoClouds(c *ui.Context, log func(string)) {
|
||||
over(c, 140, 80, func() { fx.Clouds(c, fx.CloudsOptions{Width: 140, Height: 80, Label: "clouds"}) })
|
||||
}
|
||||
|
||||
func demoStarfield(c *ui.Context, log func(string)) {
|
||||
fx.Starfield(c, fx.StarfieldOptions{Width: 140, Height: 80, Label: "stars"})
|
||||
}
|
||||
|
||||
func demoRain(c *ui.Context, log func(string)) {
|
||||
over(c, 140, 80, func() { fx.Rain(c, fx.RainOptions{Width: 140, Height: 80, Label: "rain"}) })
|
||||
}
|
||||
|
||||
func demoSnow(c *ui.Context, log func(string)) {
|
||||
over(c, 140, 80, func() { fx.Snow(c, fx.SnowOptions{Width: 140, Height: 80, Label: "snow"}) })
|
||||
}
|
||||
|
||||
func demoPetals(c *ui.Context, log func(string)) {
|
||||
over(c, 140, 80, func() { fx.Petals(c, fx.PetalsOptions{Width: 140, Height: 80, Label: "petals"}) })
|
||||
}
|
||||
|
||||
func demoLeaves(c *ui.Context, log func(string)) {
|
||||
over(c, 140, 80, func() { fx.Leaves(c, fx.LeavesOptions{Width: 140, Height: 80, Label: "leaves"}) })
|
||||
}
|
||||
|
||||
func demoFireflies(c *ui.Context, log func(string)) {
|
||||
pixel.Scope(c, paletteOf(c, pixel.K(c).Palette().Season, true), func() {
|
||||
over(c, 140, 80, func() { fx.Fireflies(c, fx.FirefliesOptions{Width: 140, Height: 80, Label: "fireflies"}) })
|
||||
})
|
||||
}
|
||||
|
||||
func demoWaterfallFx(c *ui.Context, log func(string)) {
|
||||
fx.Waterfall(c, fx.WaterfallOptions{Label: "waterfall"})
|
||||
}
|
||||
|
||||
func demoOcean(c *ui.Context, log func(string)) {
|
||||
fx.Ocean(c, fx.OceanOptions{Width: 140, Label: "ocean"})
|
||||
}
|
||||
|
||||
func demoFire(c *ui.Context, log func(string)) {
|
||||
fx.Fire(c, fx.FireOptions{Scale: 3, Label: "campfire"})
|
||||
}
|
||||
|
||||
func demoLava(c *ui.Context, log func(string)) {
|
||||
fx.Lava(c, fx.LavaOptions{Width: 140, Label: "lava"})
|
||||
}
|
||||
|
||||
func demoAurora(c *ui.Context, log func(string)) {
|
||||
pixel.Scope(c, paletteOf(c, pixel.K(c).Palette().Season, true), func() {
|
||||
layout.Layers(c, func() { fx.Sky(c, fx.SkyOptions{Width: 140, Height: 80}) },
|
||||
func() { fx.Aurora(c, fx.AuroraOptions{Width: 140, Height: 80, Label: "aurora"}) },
|
||||
func() { fx.Mountains(c, fx.MountainsOptions{Width: 140, Height: 80}) })
|
||||
})
|
||||
}
|
||||
|
||||
func demoLightning(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := fxState(c)
|
||||
ui.Column(c).Gap(k.D(6)).Children(func() {
|
||||
pixel.Scope(c, paletteOf(c, k.Palette().Season, true), func() {
|
||||
over(c, 120, 80, func() { fx.Lightning(c, fx.LightningOptions{Cue: s.strike, Width: 120, Height: 80, Label: "storm"}) })
|
||||
})
|
||||
if small(c, "STRIKE", false) {
|
||||
s.strike++
|
||||
log(fmt.Sprintf("strike %d", s.strike))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func demoMountains(c *ui.Context, log func(string)) {
|
||||
over(c, 140, 80, func() { fx.Mountains(c, fx.MountainsOptions{Width: 140, Height: 80, Label: "mountains"}) })
|
||||
}
|
||||
|
||||
func demoSeasonScene(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := fxState(c)
|
||||
ui.Column(c).Gap(k.D(6)).Children(func() {
|
||||
fx.SeasonScene(c, pixel.Season(s.season), s.hour, fx.SeasonSceneOptions{Width: 144, Height: 80, Label: "the world"})
|
||||
seasons := []string{"Spring", "Summer", "Autumn", "Winter"}
|
||||
if input.Segmented(c, "Season", seasons, &s.season, input.SegmentedOptions{}).Changed {
|
||||
log("season " + seasons[s.season])
|
||||
}
|
||||
if input.Stepper(c, "Hour", &s.hour, input.StepperOptions{Max: 23}).Changed {
|
||||
log(fmt.Sprintf("hour %02d", s.hour))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package gallery
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/display"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/fx"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/input"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/layout"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
|
||||
)
|
||||
|
||||
var screenFxEntries = []Entry{
|
||||
{309, "Scanlines", "扫描线", "fx", "Every other row, half shaded. Instant CRT.", demoScanlines},
|
||||
{310, "Vignette", "暗角", "fx", "The corners dither into the dark.", demoVignette},
|
||||
{311, "ScreenShake", "震屏", "fx", "Something big just landed.", demoScreenShake},
|
||||
{312, "Flash", "闪白", "fx", "A step of light, then half a step. Never under Reduce Motion.", demoFlash},
|
||||
{313, "FadeDither", "抖动淡出", "fx", "Fades through Bayer levels, never through opacity.", demoFadeDither},
|
||||
{314, "IrisWipe", "光圈转场", "fx", "The circle closes on the middle. That's all, folks.", demoIrisWipe},
|
||||
{315, "Dissolve", "溶解", "fx", "Dots give up one batch at a time, in shuffled order.", demoDissolve},
|
||||
{316, "Mosaic", "马赛克", "fx", "Blocks grow to eight dots, then the picture comes back.", demoMosaic},
|
||||
{317, "Glitch", "故障", "fx", "Rows slip and two colors trade places. Blow on the cartridge.", demoGlitch},
|
||||
{318, "Sparkle", "闪光", "fx", "Four stars take turns around a shiny thing.", demoSparkle},
|
||||
{319, "Explosion", "爆炸", "fx", "Fireball, smoke, debris. Stand back.", demoExplosion},
|
||||
{320, "Confetti", "彩纸", "fx", "Bits of every color tumble down. You won!", demoConfetti},
|
||||
}
|
||||
|
||||
// stage is a little world in the palette's season.
|
||||
func stage(c *ui.Context) {
|
||||
p := pixel.K(c).Palette()
|
||||
fx.SeasonScene(c, p.Season, hourOf(p), fx.SeasonSceneOptions{Width: 128, Height: 72, Label: "stage"})
|
||||
}
|
||||
|
||||
func demoScanlines(c *ui.Context, log func(string)) {
|
||||
ui.Column(c).AlignItems(ui.Start).Children(func() {
|
||||
fx.Scanlines(c, fx.ScanlinesOptions{}, func() { stage(c) })
|
||||
})
|
||||
}
|
||||
|
||||
func demoVignette(c *ui.Context, log func(string)) {
|
||||
ui.Column(c).AlignItems(ui.Start).Children(func() {
|
||||
fx.Vignette(c, fx.VignetteOptions{}, func() { stage(c) })
|
||||
})
|
||||
}
|
||||
|
||||
func demoScreenShake(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := fxState(c)
|
||||
ui.Column(c).Gap(k.D(6)).AlignItems(ui.Start).Children(func() {
|
||||
fx.ScreenShake(c, fx.ScreenShakeOptions{Cue: s.shake}, func() { stage(c) })
|
||||
if small(c, "SHAKE", false) {
|
||||
s.shake++
|
||||
log("rumble!")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func demoFlash(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := fxState(c)
|
||||
ui.Column(c).Gap(k.D(6)).AlignItems(ui.Start).Children(func() {
|
||||
fx.Flash(c, fx.FlashOptions{Cue: s.flash}, func() { stage(c) })
|
||||
if small(c, "FLASH", false) {
|
||||
s.flash++
|
||||
log("flash")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// toggled shows content under a transition, with its toggle.
|
||||
func toggled(c *ui.Context, text string, on *bool, log func(string), effect func(content func())) {
|
||||
k := pixel.K(c)
|
||||
ui.Column(c).Gap(k.D(6)).AlignItems(ui.Start).Children(func() {
|
||||
effect(func() { stage(c) })
|
||||
if input.Toggle(c, text, on, input.ToggleOptions{Size: input.Small}).Changed {
|
||||
log(fmt.Sprintf("%s %v", text, *on))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func demoFadeDither(c *ui.Context, log func(string)) {
|
||||
s := fxState(c)
|
||||
toggled(c, "FADE OUT", &s.fade, log, func(content func()) { fx.FadeDither(c, s.fade, fx.FadeDitherOptions{}, content) })
|
||||
}
|
||||
|
||||
func demoIrisWipe(c *ui.Context, log func(string)) {
|
||||
s := fxState(c)
|
||||
toggled(c, "CLOSE", &s.iris, log, func(content func()) { fx.IrisWipe(c, s.iris, fx.IrisWipeOptions{}, content) })
|
||||
}
|
||||
|
||||
func demoDissolve(c *ui.Context, log func(string)) {
|
||||
s := fxState(c)
|
||||
toggled(c, "DISSOLVE", &s.dissolve, log, func(content func()) { fx.Dissolve(c, s.dissolve, fx.DissolveOptions{}, content) })
|
||||
}
|
||||
|
||||
func demoMosaic(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := fxState(c)
|
||||
ui.Column(c).Gap(k.D(6)).Children(func() {
|
||||
ui.Row(c).Gap(k.D(8)).AlignItems(ui.Center).Children(func() {
|
||||
fx.Mosaic(c, display.Reduce(sunset, 48, 32, k.Palette()), fx.MosaicOptions{Cue: s.mosaic, Scale: 2, Label: "sunset"})
|
||||
fx.Mosaic(c, slime, fx.MosaicOptions{Cue: s.mosaic, Scale: 3, Label: "slime"})
|
||||
})
|
||||
if small(c, "PIXELATE", false) {
|
||||
s.mosaic++
|
||||
log("pixelate")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func demoGlitch(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := fxState(c)
|
||||
ui.Column(c).Gap(k.D(6)).Children(func() {
|
||||
ui.Row(c).Gap(k.D(8)).AlignItems(ui.Center).Children(func() {
|
||||
fx.Glitch(c, display.Reduce(sunset, 48, 32, k.Palette()), fx.GlitchOptions{Cue: s.glitch, Scale: 2, Label: "sunset"})
|
||||
fx.Glitch(c, slime, fx.GlitchOptions{Cue: s.glitch, Scale: 3, Label: "slime"})
|
||||
})
|
||||
if small(c, "GLITCH", false) {
|
||||
s.glitch++
|
||||
log("glitch")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func demoSparkle(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
layout.Layers(c, func() {
|
||||
ui.Box(c).Size(k.D(48), k.D(48)).AlignItems(ui.Center).Justify(ui.Center).Children(func() {
|
||||
display.Gem(c, display.Legendary, display.GemOptions{Scale: 2})
|
||||
})
|
||||
}, func() { fx.Sparkle(c, fx.SparkleOptions{Size: 48, Label: "sparkles"}) })
|
||||
}
|
||||
|
||||
func demoExplosion(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := fxState(c)
|
||||
ui.Column(c).Gap(k.D(6)).AlignItems(ui.Center).Children(func() {
|
||||
fx.Explosion(c, fx.ExplosionOptions{Cue: s.boom, Label: "blast"})
|
||||
if small(c, "BOOM", false) {
|
||||
s.boom++
|
||||
log("boom")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func demoConfetti(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
layout.Layers(c, func() {
|
||||
ui.Box(c).Size(k.D(140), k.D(64)).AlignItems(ui.Center).Justify(ui.Center).Children(func() {
|
||||
pixel.Text(c, "YOU WIN!", pixel.TextOptions{Scale: 2, Slot: pixel.Accent})
|
||||
})
|
||||
}, func() { fx.Confetti(c, fx.ConfettiOptions{Width: 140, Height: 64, Label: "confetti"}) })
|
||||
}
|
||||
@@ -136,7 +136,7 @@ func demoLoadingTips(c *ui.Context, log func(string)) {
|
||||
func demoLetterbox(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := screenState(c)
|
||||
ui.Column(c).Gap(k.D(6)).Children(func() {
|
||||
ui.Column(c).Gap(k.D(6)).AlignItems(ui.Start).Children(func() {
|
||||
game.Letterbox(c, s.cutscene, game.LetterboxOptions{}, func() {
|
||||
ui.Box(c).Size(k.D(200), k.D(80)).Draw(func(p *ui.Painter, r ui.Rect) {
|
||||
g := k.Canvas(p, r)
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package gallery
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
|
||||
)
|
||||
|
||||
func TestFxDemosLogTheirActions(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
path string
|
||||
steps []string
|
||||
}{
|
||||
{"/c/293-sunmoon", []string{"Hour", "Right"}}, {"/c/306-lightning", []string{"STRIKE"}},
|
||||
{"/c/308-seasonscene", []string{"Winter", "Hour", "Right"}}, {"/c/311-screenshake", []string{"SHAKE"}},
|
||||
{"/c/312-flash", []string{"FLASH"}}, {"/c/313-fadedither", []string{"FADE OUT"}}, {"/c/314-iriswipe", []string{"CLOSE"}},
|
||||
{"/c/315-dissolve", []string{"DISSOLVE"}}, {"/c/316-mosaic", []string{"PIXELATE"}}, {"/c/317-glitch", []string{"GLITCH"}},
|
||||
{"/c/319-explosion", []string{"BOOM"}},
|
||||
} {
|
||||
a, tt := open(tc.path)
|
||||
slug := tc.path[3:]
|
||||
for _, step := range tc.steps {
|
||||
before := len(a.logs[slug])
|
||||
if step == "Right" {
|
||||
tt.Key(0, ui.KeyRight)
|
||||
} else {
|
||||
click(t, tt, step)
|
||||
}
|
||||
// Focusing the stepper changes nothing worth a line.
|
||||
if len(a.logs[slug]) <= before && step != "Hour" {
|
||||
t.Errorf("%s: %s logged nothing: %q", slug, step, a.logs[slug])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroupsAreOneTabStopEach(t *testing.T) {
|
||||
for _, page := range []struct{ path, query string }{{"/", ""}, {"/cat/fx", ""}, {"/", "e"}} {
|
||||
_, tt := open(page.path)
|
||||
tt.Key(0, ui.KeySlash)
|
||||
tt.Type(page.query)
|
||||
// Search, four top-bar controls, then one stop per group.
|
||||
stops := 1
|
||||
for tt.Key(0, ui.KeyTab); !tt.Focused("Search components"); tt.Key(0, ui.KeyTab) {
|
||||
if stops++; stops > 12 {
|
||||
t.Fatalf("%s %q: a Tab cycle passes over 12 stops", page.path, page.query)
|
||||
}
|
||||
}
|
||||
}
|
||||
_, tt := open("/")
|
||||
for range 40 {
|
||||
if tt.Focused("Spring Day, in use") {
|
||||
break
|
||||
}
|
||||
tt.Key(0, ui.KeyTab)
|
||||
}
|
||||
tt.Key(0, ui.KeyRight)
|
||||
if !tt.Focused("Spring Night") {
|
||||
t.Fatal("Right did not move along the palette strip")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomeSceneFollowsThePaletteStrip(t *testing.T) {
|
||||
a, tt := open("/")
|
||||
if _, ok := tt.Find("Spring Day scene"); !ok {
|
||||
t.Fatalf("no spring scene in %q", tt.Texts())
|
||||
}
|
||||
click(t, tt, "Winter Night")
|
||||
if a.palette != 7 {
|
||||
t.Fatalf("the strip chose palette %d", a.palette)
|
||||
}
|
||||
r, ok := tt.Find("Winter Night scene")
|
||||
if !ok {
|
||||
t.Fatalf("the scene kept its season: %q", tt.Texts())
|
||||
}
|
||||
// The banner's sky starts in Winter Night's darkest slot.
|
||||
got, want := tt.Image().RGBAAt(int(r.X)+1, int(r.Y)+1), pixel.WinterNight.Colors[pixel.Well]
|
||||
if got.R != want.R || got.G != want.G || got.B != want.B {
|
||||
t.Fatalf("the banner's sky is %v, want %v", got, want)
|
||||
}
|
||||
if _, ok := tt.Find("Winter Night, in use"); !ok {
|
||||
t.Fatal("the strip does not mark the palette in use")
|
||||
}
|
||||
}
|
||||
@@ -93,7 +93,8 @@ func TestInvariantsPages(t *testing.T) {
|
||||
a.Go(path)
|
||||
}
|
||||
allowed := colors(pal)
|
||||
if name == "entry" {
|
||||
// Home shows every palette in its strip.
|
||||
if name == "entry" || name == "home" {
|
||||
allowed = colors(pixel.Palettes...)
|
||||
}
|
||||
img := render(a.View, 640, 400)
|
||||
|
||||
+48
-4
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/display"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/feedback"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/fx"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/input"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/layout"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
|
||||
@@ -49,13 +50,23 @@ func page(c *ui.Context, fn func()) {
|
||||
|
||||
func (a *App) home(c *ui.Context) {
|
||||
k := pixel.K(c)
|
||||
text.ShadowText(c, logo, text.ShadowTextOptions{Slot: pixel.Accent, Scale: 3})
|
||||
p := k.Palette()
|
||||
// The banner lives in the palette's own season and hour.
|
||||
layout.Layers(c, func() {
|
||||
fx.SeasonScene(c, p.Season, hourOf(p), fx.SeasonSceneOptions{Height: 96, Label: p.Name + " scene"}).FillWidth()
|
||||
}, func() {
|
||||
ui.Column(c).Padding(k.D(10)).Children(func() {
|
||||
text.OutlineText(c, logo, text.OutlineTextOptions{Slot: pixel.Accent, Scale: 3})
|
||||
})
|
||||
})
|
||||
pixel.Text(c, "Pixel components for MyGo. Every dot sits on the grid; every color comes from sixteen.", pixel.TextOptions{Wrap: true})
|
||||
pixel.Text(c, "为 MyGo 打造的像素组件。每个点落在点阵上,每种颜色来自十六格调色板。", pixel.TextOptions{Wrap: true, Slot: pixel.Faint})
|
||||
comps := len(Entries()) - count("foundations")
|
||||
stats := fmt.Sprintf("%d COMPONENTS %d CATEGORIES %d PALETTES", comps, len(Categories)-1, len(pixel.Palettes))
|
||||
text.MicroText(c, stats, text.MicroTextOptions{Slot: pixel.Accent})
|
||||
ui.Grid(c).Columns(3).Gap(k.D(4)).Children(func() {
|
||||
a.palettes(c)
|
||||
// Each grid is one Tab stop; arrows move inside it.
|
||||
ui.Grid(c).Columns(3).Gap(k.D(4)).FocusGroup(ui.Horizontal | ui.Vertical).Children(func() {
|
||||
for _, cat := range Categories {
|
||||
n := count(cat.Key)
|
||||
if press(c, 0, 40, cat.Name, func(g pixel.Canvas, st pixel.State) {
|
||||
@@ -76,6 +87,39 @@ func (a *App) home(c *ui.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// palettes shows each palette in its colors; press to pick.
|
||||
func (a *App) palettes(c *ui.Context) {
|
||||
k := pixel.K(c)
|
||||
ui.Grid(c).Columns(4).Gap(k.D(4)).FocusGroup(ui.Horizontal | ui.Vertical).Children(func() {
|
||||
for i, pal := range pixel.Palettes {
|
||||
name := pal.Name
|
||||
if i == a.palette {
|
||||
name += ", in use"
|
||||
}
|
||||
set := pixel.Current(c)
|
||||
set.Palette = pal
|
||||
pixel.Scope(c, set, func() {
|
||||
if press(c, 0, 30, name, func(g pixel.Canvas, st pixel.State) {
|
||||
fill := pixel.Face
|
||||
if i == a.palette || st.Hover {
|
||||
fill = pixel.Lit
|
||||
}
|
||||
g.Frame(0, 0, g.W, g.H, pixel.Window, fill)
|
||||
for s := range pixel.Slots {
|
||||
g.Rect(5+3*s, 4, 3, 8, pixel.Slot(s))
|
||||
}
|
||||
g.Text(5, 14, pal.Name+" "+pal.Zh, pixel.Ink, 1)
|
||||
if i == a.palette {
|
||||
g.Rect(g.W-9, 5, 4, 4, pixel.Accent)
|
||||
}
|
||||
}) {
|
||||
a.setPalette(i)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (a *App) category(c *ui.Context, key string) {
|
||||
var cat Category
|
||||
for _, x := range Categories {
|
||||
@@ -93,7 +137,7 @@ func (a *App) category(c *ui.Context, key string) {
|
||||
text.ShadowText(c, cat.Name, text.ShadowTextOptions{Scale: 2})
|
||||
pixel.Text(c, cat.Zh, pixel.TextOptions{Slot: pixel.Faint})
|
||||
})
|
||||
ui.Grid(c).Columns(3).Gap(k.D(4)).Children(func() {
|
||||
ui.Grid(c).Columns(3).Gap(k.D(4)).FocusGroup(ui.Horizontal | ui.Vertical).Children(func() {
|
||||
for _, e := range Entries() {
|
||||
if e.Category == key {
|
||||
a.card(c, e)
|
||||
@@ -180,7 +224,7 @@ func (a *App) results(c *ui.Context) {
|
||||
found := matches(a.query)
|
||||
pixel.Text(c, fmt.Sprintf("%d results for %q", len(found), strings.TrimSpace(a.query)), pixel.TextOptions{Slot: pixel.Faint})
|
||||
k := pixel.K(c)
|
||||
ui.Grid(c).Columns(3).Gap(k.D(4)).Children(func() {
|
||||
ui.Grid(c).Columns(3).Gap(k.D(4)).FocusGroup(ui.Horizontal | ui.Vertical).Children(func() {
|
||||
for _, e := range found {
|
||||
a.card(c, e)
|
||||
}
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ var Categories = []Category{
|
||||
{"tabletop", "Tabletop", "桌游", icon.Crown},
|
||||
}
|
||||
|
||||
var entries = slices.Concat(foundationEntries, layoutEntries, textEntries, displayEntries, inputEntries, controlEntries, feedbackEntries, navigationEntries, dataEntries, recordEntries, overlayEntries, popupEntries, datetimeEntries, timeEntries, chartEntries, plotEntries, gameEntries, rpgEntries, screenEntries, lootEntries)
|
||||
var entries = slices.Concat(foundationEntries, layoutEntries, textEntries, displayEntries, inputEntries, controlEntries, feedbackEntries, navigationEntries, dataEntries, recordEntries, overlayEntries, popupEntries, datetimeEntries, timeEntries, chartEntries, plotEntries, gameEntries, rpgEntries, screenEntries, lootEntries, fxEntries, screenFxEntries)
|
||||
|
||||
func Entries() []Entry { return entries }
|
||||
|
||||
|
||||
Reference in new issue
Block a user