Add desk components 466-485 and icon.Bomb
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.
This commit is contained in:
39 files changed
+4120
-2
No files matched your search
@@ -0,0 +1,186 @@
|
||||
package gallery
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/desk"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/icon"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/input"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
|
||||
)
|
||||
|
||||
var deskEntries = []Entry{
|
||||
{466, "Desktop", "桌面", "desk", "A dithered wallpaper with icons down the left, as desktops had.", demoDesktop},
|
||||
{467, "DesktopIcon", "桌面图标", "desk", "Click to choose and it inverts; double-click or Enter opens it.", demoDesktopIcon},
|
||||
{468, "Taskbar", "任务栏", "desk", "START, a button for each open window, the tray and the clock.", demoTaskbar},
|
||||
{469, "StartMenu", "开始菜单", "desk", "Programs in cascading menus. Arrows walk in and out; Enter runs one.", demoStartMenu},
|
||||
{470, "AppWindow", "应用窗口", "desk", "Drag a window by its title bar, or focus the bar and use the arrows.", demoAppWindow},
|
||||
{471, "Calculator", "计算器", "desk", "It really adds up. Divide by zero for a GAME OVER; C continues.", demoCalculator},
|
||||
{472, "Notepad", "记事本", "desk", "A text area under FILE and EDIT. TIME/DATE stamps the moment.", demoNotepad},
|
||||
{473, "Minesweeper", "扫雷", "desk", "Open with a click or Enter, flag with a right click or F. The face starts over.", demoMinesweeper},
|
||||
{474, "ControlPanel", "控制面板", "desk", "Settings as an icon grid. Arrows choose; Enter opens.", demoControlPanel},
|
||||
{475, "TrayIcons", "托盘图标", "desk", "Small icons by the clock. Hover or focus one for its tip.", demoTrayIcons},
|
||||
}
|
||||
|
||||
type deskDemo struct {
|
||||
chosen, active, wx, wy int
|
||||
note string
|
||||
shut bool
|
||||
}
|
||||
|
||||
func deskState(c *ui.Context) *deskDemo {
|
||||
return ui.Local(c.Root(), demoKey("desk"), func() deskDemo {
|
||||
return deskDemo{chosen: -1, wx: 70, wy: 12, note: "Dear diary,\ntoday I defragged."}
|
||||
})
|
||||
}
|
||||
|
||||
// icons sit on the demo desktops.
|
||||
var icons = []struct {
|
||||
name string
|
||||
sp *pixel.Sprite
|
||||
}{{"MY COMPUTER", icon.Gamepad}, {"RECYCLE BIN", icon.Trash}, {"NOTES.TXT", icon.FileText}, {"GAMES", icon.Folder}}
|
||||
|
||||
// programs fill the demo START menus.
|
||||
var programs = []desk.Program{
|
||||
{Name: "PROGRAMS", Icon: icon.Folder, Programs: []desk.Program{
|
||||
{Name: "ACCESSORIES", Icon: icon.Folder, Programs: []desk.Program{{Name: "NOTEPAD", Icon: icon.FileText}, {Name: "CALCULATOR", Icon: icon.Grid}}},
|
||||
{Name: "GAMES", Icon: icon.Gamepad, Programs: []desk.Program{{Name: "MINESWEEPER", Icon: icon.Flag}, {Name: "SNAKE", Icon: icon.Bolt}}},
|
||||
}},
|
||||
{Name: "DOCUMENTS", Icon: icon.FileText},
|
||||
{Name: "SETTINGS", Icon: icon.Settings, Programs: []desk.Program{{Name: "CONTROL PANEL", Icon: icon.Settings}, {Name: "WALLPAPER", Icon: icon.Image}}},
|
||||
{Name: "RUN...", Icon: icon.Terminal},
|
||||
{Name: "SHUT DOWN", Icon: icon.Power},
|
||||
}
|
||||
|
||||
func demoDesktop(c *ui.Context, log func(string)) {
|
||||
s := deskState(c)
|
||||
desk.Desktop(c, "Desktop", desk.DesktopOptions{Width: 300, Height: 150}, func() {
|
||||
for i, it := range icons {
|
||||
r := desk.DesktopIcon(c, it.name, it.sp, desk.DesktopIconOptions{Chosen: s.chosen == i})
|
||||
if r.Clicked {
|
||||
s.chosen = i
|
||||
log("chose " + it.name)
|
||||
}
|
||||
if r.Opened {
|
||||
log("opened " + it.name)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func demoDesktopIcon(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := deskState(c)
|
||||
ui.Row(c).Gap(k.D(4)).Children(func() {
|
||||
for i, it := range icons[:3] {
|
||||
r := desk.DesktopIcon(c, it.name, it.sp, desk.DesktopIconOptions{Chosen: s.chosen == i})
|
||||
if r.Clicked {
|
||||
s.chosen = i
|
||||
log("chose " + it.name)
|
||||
}
|
||||
if r.Opened {
|
||||
log("opened " + it.name)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// tray sits by the demo taskbars' clocks.
|
||||
var tray = []desk.TrayIcon{{Icon: icon.Wifi, Tip: "ONLINE"}, {Icon: icon.Volume, Tip: "VOLUME 80%"}, {Icon: icon.Battery, Tip: "BATTERY 99%"}}
|
||||
|
||||
func demoTaskbar(c *ui.Context, log func(string)) {
|
||||
s := deskState(c)
|
||||
windows := []string{"NOTEPAD", "CALC", "MINES"}
|
||||
r := desk.Taskbar(c, "Taskbar", windows, &s.active, desk.TaskbarOptions{Programs: programs, Tray: tray})
|
||||
if r.Changed {
|
||||
log(fmt.Sprintf("active window %d", s.active))
|
||||
}
|
||||
if r.Launched != "" {
|
||||
log("launched " + r.Launched)
|
||||
}
|
||||
if r.Tray >= 0 {
|
||||
log("tray: " + tray[r.Tray].Tip)
|
||||
}
|
||||
}
|
||||
|
||||
func demoStartMenu(c *ui.Context, log func(string)) {
|
||||
if r := desk.StartMenu(c, "Start", programs, desk.StartMenuOptions{}); r.Launched != "" {
|
||||
log("launched " + r.Launched)
|
||||
}
|
||||
}
|
||||
|
||||
func demoAppWindow(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := deskState(c)
|
||||
ui.Column(c).Gap(k.D(4)).Children(func() {
|
||||
desk.Desktop(c, "Desktop", desk.DesktopOptions{Wallpaper: desk.Dots, Width: 320, Height: 170}, func() {
|
||||
if !s.shut {
|
||||
r := desk.AppWindow(c, "HELLO.TXT", &s.wx, &s.wy, desk.AppWindowOptions{Width: 120, Height: 48, Room: [2]int{320, 170}}, func() {
|
||||
pixel.Text(c, "Drag me around!", pixel.TextOptions{})
|
||||
})
|
||||
if r.Moved {
|
||||
log(fmt.Sprintf("window at %d, %d", s.wx, s.wy))
|
||||
}
|
||||
if r.Closed {
|
||||
s.shut = true
|
||||
log("window closed")
|
||||
}
|
||||
}
|
||||
})
|
||||
if s.shut && input.Button(c, "REOPEN", input.ButtonOptions{Size: input.Small}).Clicked {
|
||||
s.shut = false
|
||||
log("window reopened")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func demoCalculator(c *ui.Context, log func(string)) {
|
||||
if desk.Calculator(c, "Pocket calculator", desk.CalculatorOptions{}).Changed {
|
||||
log("key pressed")
|
||||
}
|
||||
}
|
||||
|
||||
func demoNotepad(c *ui.Context, log func(string)) {
|
||||
s := deskState(c)
|
||||
r := desk.Notepad(c, "Notepad", &s.note, desk.NotepadOptions{})
|
||||
if r.Changed {
|
||||
log(fmt.Sprintf("%d runes", len([]rune(s.note))))
|
||||
}
|
||||
if r.Saved {
|
||||
log("saved")
|
||||
}
|
||||
}
|
||||
|
||||
func demoMinesweeper(c *ui.Context, log func(string)) {
|
||||
r := desk.Minesweeper(c, "Minesweeper", desk.MinesweeperOptions{})
|
||||
if r.Flagged {
|
||||
log("flag")
|
||||
}
|
||||
if r.Restarted {
|
||||
log("new game")
|
||||
}
|
||||
if r.Won {
|
||||
log("cleared the field")
|
||||
}
|
||||
if r.Lost {
|
||||
log("boom")
|
||||
}
|
||||
}
|
||||
|
||||
func demoControlPanel(c *ui.Context, log func(string)) {
|
||||
applets := []desk.Applet{
|
||||
{Name: "DISPLAY", Icon: icon.Image}, {Name: "SOUND", Icon: icon.Volume}, {Name: "CLOCK", Icon: icon.Clock}, {Name: "NETWORK", Icon: icon.Wifi},
|
||||
{Name: "POWER", Icon: icon.Power}, {Name: "USERS", Icon: icon.Users}, {Name: "FONTS", Icon: icon.FileText}, {Name: "GAMEPAD", Icon: icon.Gamepad},
|
||||
}
|
||||
if i := desk.ControlPanel(c, "Control panel", applets, desk.ControlPanelOptions{}).Opened; i >= 0 {
|
||||
log("opened " + applets[i].Name)
|
||||
}
|
||||
}
|
||||
|
||||
func demoTrayIcons(c *ui.Context, log func(string)) {
|
||||
if i := desk.TrayIcons(c, "Tray", tray, desk.TrayIconsOptions{}).Clicked; i >= 0 {
|
||||
log("clicked " + tray[i].Tip)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package gallery
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/desk"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/icon"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/input"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
|
||||
)
|
||||
|
||||
var retroEntries = []Entry{
|
||||
{476, "ShutdownScreen", "关机画面", "desk", "The kindest words a computer ever said.", demoShutdownScreen},
|
||||
{477, "BootScreen", "开机自检", "desk", "Memory counts up, POST lines roll by, the logo waits.", demoBootScreen},
|
||||
{478, "Screensaver", "屏保", "desk", "A logo bounces round the screen. Any key, click or move wakes it. Watch the corners.", demoScreensaver},
|
||||
{479, "Snake", "贪吃蛇", "desk", "Arrows steer, food grows you. The wall is not your friend.", demoSnake},
|
||||
{480, "Dock", "程序坞", "desk", "A shelf of apps. The one you point at or focus rises.", demoDock},
|
||||
{481, "RunDialog", "运行", "desk", "Type a program's name. OK runs it; CANCEL or Escape walks away.", demoRunDialog},
|
||||
{482, "DiskDefrag", "磁盘整理", "desk", "Scattered blocks march into order. Oddly soothing.", demoDiskDefrag},
|
||||
{483, "WallpaperPicker", "壁纸选择", "desk", "Dither patterns to dress the desktop.", demoWallpaperPicker},
|
||||
{484, "ClipboardPanel", "剪贴板", "desk", "Recent clips. Click one and it is yours to paste again.", demoClipboardPanel},
|
||||
{485, "LockScreen", "锁屏", "desk", "The time, the date, and PRESS ANY KEY.", demoLockScreen},
|
||||
}
|
||||
|
||||
type retroDemo struct {
|
||||
saver, locked, defrag bool
|
||||
boots, disks int
|
||||
run string
|
||||
paper desk.Wallpaper
|
||||
}
|
||||
|
||||
func retroState(c *ui.Context) *retroDemo {
|
||||
return ui.Local(c.Root(), demoKey("retro"), func() retroDemo { return retroDemo{run: "notepad"} })
|
||||
}
|
||||
|
||||
func demoShutdownScreen(c *ui.Context, log func(string)) {
|
||||
desk.ShutdownScreen(c, desk.ShutdownScreenOptions{Width: 300, Height: 120})
|
||||
}
|
||||
|
||||
func demoBootScreen(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := retroState(c)
|
||||
ui.Column(c).Gap(k.D(4)).Children(func() {
|
||||
// A new key is a new screen: REBOOT starts over.
|
||||
ui.Box(c).Key(s.boots).Children(func() {
|
||||
if desk.BootScreen(c, "8BIT BIOS V4.51", []string{
|
||||
"CPU: PIXEL 8086 AT 4.77 MHZ", "FLOPPY A: 1.44 MB", "KEYBOARD... OK", "BOOTING FROM C:",
|
||||
}, desk.BootScreenOptions{Width: 300, Height: 140}).Done {
|
||||
log("booted")
|
||||
}
|
||||
})
|
||||
if input.Button(c, "REBOOT", input.ButtonOptions{Size: input.Small}).Clicked {
|
||||
s.boots++
|
||||
log("rebooting")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func demoScreensaver(c *ui.Context, log func(string)) {
|
||||
s := retroState(c)
|
||||
if input.Button(c, "START SAVER", input.ButtonOptions{}).Clicked {
|
||||
s.saver = true
|
||||
log("saver on")
|
||||
}
|
||||
if desk.Screensaver(c, "8BIT", &s.saver, desk.ScreensaverOptions{}).Woke {
|
||||
log("woke up")
|
||||
}
|
||||
}
|
||||
|
||||
func demoSnake(c *ui.Context, log func(string)) {
|
||||
r := desk.Snake(c, "Snake pit", desk.SnakeOptions{})
|
||||
if r.Started {
|
||||
log("go!")
|
||||
}
|
||||
if r.Over {
|
||||
log(fmt.Sprintf("game over at %d", r.Score))
|
||||
}
|
||||
}
|
||||
|
||||
func demoDock(c *ui.Context, log func(string)) {
|
||||
apps := []desk.DockApp{
|
||||
{Name: "FILES", Icon: icon.Folder}, {Name: "MAIL", Icon: icon.Mail}, {Name: "MUSIC", Icon: icon.Music},
|
||||
{Name: "CAMERA", Icon: icon.Camera}, {Name: "TERMINAL", Icon: icon.Terminal}, {Name: "TRASH", Icon: icon.Trash},
|
||||
}
|
||||
if i := desk.Dock(c, "Dock", apps, desk.DockOptions{}).Launched; i >= 0 {
|
||||
log("launched " + apps[i].Name)
|
||||
}
|
||||
}
|
||||
|
||||
func demoRunDialog(c *ui.Context, log func(string)) {
|
||||
s := retroState(c)
|
||||
r := desk.RunDialog(c, "Run", &s.run, desk.RunDialogOptions{})
|
||||
if r.Changed {
|
||||
log(fmt.Sprintf("%d letters typed", len([]rune(s.run))))
|
||||
}
|
||||
if r.Submitted {
|
||||
log(fmt.Sprintf("running a %d-letter program", len([]rune(s.run))))
|
||||
}
|
||||
if r.Canceled {
|
||||
log("canceled")
|
||||
}
|
||||
}
|
||||
|
||||
func demoDiskDefrag(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := retroState(c)
|
||||
ui.Column(c).Gap(k.D(4)).Children(func() {
|
||||
ui.Box(c).Key(s.disks).Children(func() {
|
||||
if desk.DiskDefrag(c, "Drive C:", s.defrag, desk.DiskDefragOptions{Seed: 7}).Done {
|
||||
s.defrag = false
|
||||
log("drive in order")
|
||||
}
|
||||
})
|
||||
ui.Row(c).Gap(k.D(4)).Children(func() {
|
||||
label := "START"
|
||||
if s.defrag {
|
||||
label = "PAUSE"
|
||||
}
|
||||
if input.Button(c, label, input.ButtonOptions{Size: input.Small}).Clicked {
|
||||
s.defrag = !s.defrag
|
||||
log(fmt.Sprintf("defrag running %v", s.defrag))
|
||||
}
|
||||
if input.Button(c, "NEW DISK", input.ButtonOptions{Size: input.Small}).Clicked {
|
||||
s.disks, s.defrag = s.disks+1, false
|
||||
log("a fresh mess")
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func demoWallpaperPicker(c *ui.Context, log func(string)) {
|
||||
k := pixel.K(c)
|
||||
s := retroState(c)
|
||||
ui.Row(c).Gap(k.D(8)).AlignItems(ui.Start).Children(func() {
|
||||
if desk.WallpaperPicker(c, "Wallpaper", &s.paper, desk.WallpaperPickerOptions{}).Changed {
|
||||
log(fmt.Sprintf("wallpaper %d", s.paper))
|
||||
}
|
||||
desk.Desktop(c, "Preview", desk.DesktopOptions{Wallpaper: s.paper, Width: 120, Height: 90}, func() {})
|
||||
})
|
||||
}
|
||||
|
||||
func demoClipboardPanel(c *ui.Context, log func(string)) {
|
||||
clips := []string{"git push gitea main", "IT'S NOW SAFE TO TURN OFF", "UP UP DOWN DOWN LEFT RIGHT", "8bitmygoui.workers.dev"}
|
||||
if i := desk.ClipboardPanel(c, "Clipboard", clips, desk.ClipboardPanelOptions{Width: 240}).Copied; i >= 0 {
|
||||
log(fmt.Sprintf("copied clip %d", i+1))
|
||||
}
|
||||
}
|
||||
|
||||
func demoLockScreen(c *ui.Context, log func(string)) {
|
||||
s := retroState(c)
|
||||
if input.Button(c, "LOCK", input.ButtonOptions{}).Clicked {
|
||||
s.locked = true
|
||||
log("locked")
|
||||
}
|
||||
if desk.LockScreen(c, "Lock screen", &s.locked, desk.LockScreenOptions{}).Unlocked {
|
||||
log("unlocked")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package gallery
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDeskDemosLogTheirActions(t *testing.T) {
|
||||
// Focusing, opening a menu or moving a light logs nothing.
|
||||
quiet := map[string]bool{"key:Tab": true, "Start": true, "key:Up": true, "FILE": true, "OPEN:": true}
|
||||
for _, tc := range []struct {
|
||||
path string
|
||||
steps []string
|
||||
}{
|
||||
{"/c/466-desktop", []string{"MY COMPUTER"}}, {"/c/473-minesweeper", []string{"NEW GAME"}},
|
||||
{"/c/479-snake", []string{"Snake pit"}}, {"/c/481-rundialog", []string{"OPEN:", "type:ls"}},
|
||||
{"/c/467-desktopicon", []string{"MY COMPUTER", "key:Enter"}}, {"/c/468-taskbar", []string{"ONLINE"}},
|
||||
{"/c/469-startmenu", []string{"Start", "key:Up", "key:Enter"}}, {"/c/470-appwindow", []string{"CLOSE", "REOPEN"}},
|
||||
{"/c/471-calculator", []string{"Pocket calculator"}}, {"/c/472-notepad", []string{"FILE", "SAVE"}},
|
||||
{"/c/474-controlpanel", []string{"Control panel"}}, {"/c/475-trayicons", []string{"ONLINE"}},
|
||||
{"/c/477-bootscreen", []string{"REBOOT"}}, {"/c/478-screensaver", []string{"START SAVER", "key:A"}},
|
||||
{"/c/480-dock", []string{"MAIL"}}, {"/c/481-rundialog", []string{"OK", "CANCEL"}},
|
||||
{"/c/482-diskdefrag", []string{"START", "NEW DISK"}}, {"/c/483-wallpaperpicker", []string{"Wallpaper"}},
|
||||
{"/c/484-clipboardpanel", []string{"git push gitea main"}}, {"/c/485-lockscreen", []string{"LOCK", "key:A"}},
|
||||
} {
|
||||
a, tt := open(tc.path)
|
||||
slug := tc.path[3:]
|
||||
for _, step := range tc.steps {
|
||||
before := len(a.logs[slug])
|
||||
if k, ok := strings.CutPrefix(step, "key:"); ok {
|
||||
tt.Key(0, letters[k])
|
||||
tt.Frame()
|
||||
} else if s, ok := strings.CutPrefix(step, "type:"); ok {
|
||||
tt.Type(s)
|
||||
} else {
|
||||
click(t, tt, step)
|
||||
}
|
||||
if len(a.logs[slug]) <= before && !quiet[step] {
|
||||
t.Errorf("%s: %s logged nothing: %q", slug, step, a.logs[slug])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+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, fxEntries, screenFxEntries, chatEntries, mailEntries, mediaEntries, studioEntries, codeEntries, debugEntries, accountEntries, playerEntries, filesEntries, storageEntries, projectEntries, teamEntries, shopEntries, marketEntries, agentEntries, guildEntries)
|
||||
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, chatEntries, mailEntries, mediaEntries, studioEntries, codeEntries, debugEntries, accountEntries, playerEntries, filesEntries, storageEntries, projectEntries, teamEntries, shopEntries, marketEntries, agentEntries, guildEntries, deskEntries, retroEntries)
|
||||
|
||||
func Entries() []Entry { return entries }
|
||||
|
||||
|
||||
Reference in new issue
Block a user