Twenty pieces of a retro desktop: Desktop, DesktopIcon, Taskbar, StartMenu, AppWindow, Calculator, Notepad, Minesweeper, ControlPanel, TrayIcons, ShutdownScreen, BootScreen, Screensaver, Snake, Dock, RunDialog, DiskDefrag, WallpaperPicker, ClipboardPanel and LockScreen. The calculator really adds up and calls division by zero GAME OVER; Minesweeper and Snake are playable, first click always safe; START cascades its submenus without moving the rows already open; windows drag by whole dots; the screensaver's logo changes color at each wall and sparkles in a corner; the defragmenter is oddly soothing. Clocks that step back hold at zero, and typed or copied text never reaches the log. icon.Bomb is the new 12x12 mine.
81 lines
2.0 KiB
Go
81 lines
2.0 KiB
Go
package desk
|
|
|
|
import (
|
|
"log/slog"
|
|
"math"
|
|
"time"
|
|
|
|
"github.com/egoist/mygo/ui"
|
|
|
|
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
|
|
)
|
|
|
|
type ScreensaverOptions struct{}
|
|
|
|
type ScreensaverResult struct {
|
|
Element *ui.Element
|
|
// Woke is the input that ended it this frame.
|
|
Woke bool
|
|
}
|
|
|
|
type saverKey struct{}
|
|
|
|
// bounces ink the logo, a new one at each wall.
|
|
var bounces = []pixel.Slot{pixel.Accent, pixel.Good, pixel.Warn, pixel.Info, pixel.Magic, pixel.Bad}
|
|
|
|
// Screensaver bounces a logo around; any input stops it.
|
|
func Screensaver(c *ui.Context, logo string, open *bool, o ScreensaverOptions) ScreensaverResult {
|
|
k := pixel.K(c)
|
|
if logo == "" {
|
|
panic("desk: Screensaver has no logo")
|
|
}
|
|
n := 0
|
|
anchor, _, woke := cover(c, k, logo, open, true, func(g pixel.Canvas) {
|
|
g.Rect(0, 0, g.W, g.H, pixel.Well)
|
|
bw, bh := 2*pixel.Measure(logo)+12, 2*pixel.LineHeight+8
|
|
rx, ry := max(1, g.W-bw), max(1, g.H-bh)
|
|
x, y := fold(n+rx/3, rx), fold(n+ry/2, ry)
|
|
hits := (n+rx/3)/rx + (n+ry/2)/ry
|
|
ink := bounces[hits%len(bounces)]
|
|
g.Frame(x, y, bw, bh, pixel.Raised, pixel.Face)
|
|
g.Text(x+6, y+4, logo, ink, 2)
|
|
// A corner hit, the rarest bounce, earns sparkles.
|
|
if (x == 0 || x == rx) && (y == 0 || y == ry) {
|
|
for _, d := range [][2]int{{-4, -4}, {bw + 1, -4}, {-4, bh + 1}, {bw + 1, bh + 1}} {
|
|
g.Rect(x+d[0]+1, y+d[1], 1, 3, pixel.Lit)
|
|
g.Rect(x+d[0], y+d[1]+1, 3, 1, pixel.Lit)
|
|
}
|
|
}
|
|
})
|
|
st := ui.Local(anchor, saverKey{}, func() saver { return saver{} })
|
|
switch {
|
|
case !*open:
|
|
st.on = false
|
|
case !st.on:
|
|
*st = saver{on: true, at: k.Now()}
|
|
}
|
|
// It moves a dot a step; reduced, it holds still.
|
|
if *open && !k.Reduce() {
|
|
n = max(0, k.Since(c, st.at, 15, math.MaxInt32))
|
|
}
|
|
if woke {
|
|
slog.Debug("8bit: screensaver woke", "logo", logo)
|
|
}
|
|
return ScreensaverResult{Element: anchor, Woke: woke}
|
|
}
|
|
|
|
// saver is whether it shows, and since when.
|
|
type saver struct {
|
|
on bool
|
|
at time.Time
|
|
}
|
|
|
|
// fold reflects t back and forth across 0 to r.
|
|
func fold(t, r int) int {
|
|
m := t % (2 * r)
|
|
if m > r {
|
|
return 2*r - m
|
|
}
|
|
return m
|
|
}
|