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.
285 lines
8.7 KiB
Go
285 lines
8.7 KiB
Go
package desk_test
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"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/pixel"
|
|
)
|
|
|
|
func TestDesktopFillsColumnsFirst(t *testing.T) {
|
|
tt := view(700, 400, newWorld(), func(c *ui.Context) {
|
|
desk.Desktop(c, "Desk", desk.DesktopOptions{Height: 140}, func() {
|
|
for _, name := range []string{"A", "B", "C"} {
|
|
desk.DesktopIcon(c, name, icon.File, desk.DesktopIconOptions{})
|
|
}
|
|
})
|
|
})
|
|
a, b, cc := find(t, tt, "A"), find(t, tt, "B"), find(t, tt, "C")
|
|
// Two icons fill a 140-dot column; the third starts another.
|
|
if b.X != a.X || b.Y <= a.Y || cc.X <= a.X || cc.Y != a.Y {
|
|
t.Fatalf("icons at %v, %v, %v", a, b, cc)
|
|
}
|
|
}
|
|
|
|
func TestDesktopIconChoosesOpensAndInverts(t *testing.T) {
|
|
chosen, clicks, opens, off := false, 0, 0, false
|
|
tt := view(300, 200, newWorld(), func(c *ui.Context) {
|
|
r := desk.DesktopIcon(c, "NOTES", icon.FileText, desk.DesktopIconOptions{Chosen: chosen, Disabled: off})
|
|
if r.Clicked {
|
|
clicks++
|
|
chosen = true
|
|
}
|
|
if r.Opened {
|
|
opens++
|
|
}
|
|
})
|
|
x, y, w, h := dots(find(t, tt, "NOTES"))
|
|
if count(tt.Image(), x, y, w, h, pixel.Accent) > 0 {
|
|
t.Fatal("an unchosen icon shows Accent")
|
|
}
|
|
click(t, tt, "NOTES")
|
|
if clicks != 1 || opens != 0 || count(tt.Image(), x, y, w, h, pixel.Accent) == 0 {
|
|
t.Fatalf("one click: %d clicks, %d opens, or it did not invert", clicks, opens)
|
|
}
|
|
// The second quick click is a double click: it opens.
|
|
click(t, tt, "NOTES")
|
|
tt.Key(0, ui.KeyEnter)
|
|
if opens != 2 {
|
|
t.Fatalf("double click and Enter opened %d times", opens)
|
|
}
|
|
// A key sent as it turns off still does nothing.
|
|
off = true
|
|
tt.Key(0, ui.KeyEnter)
|
|
if opens != 2 {
|
|
t.Fatal("a disabled icon opened")
|
|
}
|
|
}
|
|
|
|
func TestTaskbarSwitchesMinimizesAndTellsTheTime(t *testing.T) {
|
|
active, launched, trayed := 0, "", -1
|
|
wd := newWorld()
|
|
progs := []desk.Program{{Name: "GAMES", Icon: icon.Folder, Programs: []desk.Program{{Name: "SNAKE", Icon: icon.Bolt}}}, {Name: "RUN", Icon: icon.Terminal}}
|
|
tt := view(700, 300, wd, func(c *ui.Context) {
|
|
r := desk.Taskbar(c, "Bar", []string{"ONE", "TWO"}, &active, desk.TaskbarOptions{Programs: progs, Tray: []desk.TrayIcon{{Icon: icon.Wifi, Tip: "ONLINE"}}})
|
|
if r.Launched != "" {
|
|
launched = r.Launched
|
|
}
|
|
if r.Tray >= 0 {
|
|
trayed = r.Tray
|
|
}
|
|
})
|
|
tt.Frame()
|
|
find(t, tt, "10:30")
|
|
x, y, w, h := dots(find(t, tt, "OPEN WINDOWS"))
|
|
// The second button chooses TWO; pressing it again minimizes.
|
|
tt.ClickAt(float32(2*(x+w*3/4)), float32(2*(y+h/2)))
|
|
if active != 1 {
|
|
t.Fatalf("active %d after pressing TWO", active)
|
|
}
|
|
tt.Key(0, ui.KeyEnter)
|
|
if active != -1 {
|
|
t.Fatalf("pressing the active window left %d", active)
|
|
}
|
|
click(t, tt, "ONLINE")
|
|
if trayed != 0 {
|
|
t.Fatalf("tray click gave %d", trayed)
|
|
}
|
|
click(t, tt, "START")
|
|
tt.Key(0, ui.KeyEnter)
|
|
tt.Key(0, ui.KeyEnter)
|
|
if launched != "GAMES/SNAKE" {
|
|
t.Fatalf("START, Enter, Enter launched %q", launched)
|
|
}
|
|
}
|
|
|
|
func TestStartMenuCascades(t *testing.T) {
|
|
launched, off := "", false
|
|
progs := []desk.Program{
|
|
{Name: "PROGRAMS", Icon: icon.Folder, Programs: []desk.Program{
|
|
{Name: "GAMES", Icon: icon.Folder, Programs: []desk.Program{{Name: "SNAKE", Icon: icon.Bolt}, {Name: "MINES", Icon: icon.Flag}}},
|
|
{Name: "NOTEPAD", Icon: icon.FileText},
|
|
}},
|
|
{Name: "HELP", Icon: icon.Question},
|
|
}
|
|
tt := view(700, 500, newWorld(), func(c *ui.Context) {
|
|
if r := desk.StartMenu(c, "Start", progs, desk.StartMenuOptions{Disabled: off}); r.Launched != "" {
|
|
launched = r.Launched
|
|
}
|
|
})
|
|
click(t, tt, "Start")
|
|
if _, ok := tt.Find("PROGRAMS"); !ok {
|
|
t.Fatal("START did not open")
|
|
}
|
|
// Right goes in, Left comes back out, Enter runs.
|
|
tt.Key(0, ui.KeyRight)
|
|
tt.Key(0, ui.KeyRight)
|
|
if _, ok := tt.Find("MINES"); !ok {
|
|
t.Fatalf("two Rights did not reach GAMES: %q", tt.Texts())
|
|
}
|
|
tt.Key(0, ui.KeyLeft)
|
|
tt.Key(0, ui.KeyDown)
|
|
tt.Key(0, ui.KeyEnter)
|
|
if launched != "PROGRAMS/NOTEPAD" {
|
|
t.Fatalf("launched %q", launched)
|
|
}
|
|
if _, ok := tt.Find("PROGRAMS"); ok {
|
|
t.Fatal("the menu stayed open after a launch")
|
|
}
|
|
// Clicks: a submenu opens, a program runs.
|
|
click(t, tt, "Start")
|
|
click(t, tt, "PROGRAMS")
|
|
click(t, tt, "GAMES")
|
|
click(t, tt, "MINES")
|
|
if launched != "PROGRAMS/GAMES/MINES" {
|
|
t.Fatalf("clicks launched %q", launched)
|
|
}
|
|
// Turned off while open, it closes.
|
|
click(t, tt, "Start")
|
|
off = true
|
|
tt.Frame()
|
|
if _, ok := tt.Find("PROGRAMS"); ok {
|
|
t.Fatal("a START turned off stayed open")
|
|
}
|
|
click(t, tt, "Start")
|
|
if _, ok := tt.Find("PROGRAMS"); ok {
|
|
t.Fatal("a disabled START opened")
|
|
}
|
|
}
|
|
|
|
func TestStartMenuOpensSubmenusUnderThePointer(t *testing.T) {
|
|
progs := []desk.Program{{Name: "TOOLS", Icon: icon.Folder, Programs: []desk.Program{{Name: "HAMMER", Icon: icon.Sword}}}, {Name: "QUIT", Icon: icon.Power}}
|
|
tt := view(700, 500, newWorld(), func(c *ui.Context) { desk.StartMenu(c, "Start", progs, desk.StartMenuOptions{}) })
|
|
click(t, tt, "Start")
|
|
tt.Key(0, ui.KeyDown)
|
|
r := find(t, tt, "TOOLS")
|
|
tt.Move(r.X+r.W/2, r.Y+r.H/2)
|
|
tt.Frame()
|
|
if _, ok := tt.Find("HAMMER"); !ok {
|
|
t.Fatalf("pointing at TOOLS did not open it: %q", tt.Texts())
|
|
}
|
|
}
|
|
|
|
func TestTrayIconsAreOneTabStop(t *testing.T) {
|
|
clicked := -1
|
|
tt := view(400, 200, newWorld(), func(c *ui.Context) {
|
|
if i := desk.TrayIcons(c, "Tray", []desk.TrayIcon{{Icon: icon.Wifi, Tip: "ONLINE"}, {Icon: icon.Volume, Tip: "LOUD"}}, desk.TrayIconsOptions{}).Clicked; i >= 0 {
|
|
clicked = i
|
|
}
|
|
})
|
|
tt.Key(0, ui.KeyTab)
|
|
tt.Key(0, ui.KeyRight)
|
|
tt.Key(0, ui.KeyEnter)
|
|
if clicked != 1 || !tt.Focused("LOUD") {
|
|
t.Fatalf("Tab, Right, Enter clicked %d", clicked)
|
|
}
|
|
// Focused, the tip shows in a bubble too.
|
|
if n := strings.Count(strings.Join(tt.Texts(), "|"), "LOUD"); n < 2 {
|
|
t.Fatalf("LOUD shows %d times: %q", n, tt.Texts())
|
|
}
|
|
}
|
|
|
|
func TestDockRaisesAndLaunches(t *testing.T) {
|
|
for _, reduce := range []bool{false, true} {
|
|
wd := newWorld()
|
|
wd.reduce = reduce
|
|
launched := -1
|
|
tt := view(400, 200, wd, func(c *ui.Context) {
|
|
if i := desk.Dock(c, "Dock", []desk.DockApp{{Name: "MAIL", Icon: icon.Mail}, {Name: "MUSIC", Icon: icon.Music}}, desk.DockOptions{}).Launched; i >= 0 {
|
|
launched = i
|
|
}
|
|
})
|
|
x, y, _, _ := dots(find(t, tt, "MAIL"))
|
|
top := func() int {
|
|
for j := y; j < y+40; j++ {
|
|
if count(tt.Image(), x, j, 30, 1, pixel.Ink) > 0 {
|
|
return j - y
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
rest := top()
|
|
tt.Key(0, ui.KeyTab)
|
|
first := top()
|
|
later(tt, wd, time.Second)
|
|
up := top()
|
|
// Focused, it rises eight dots: stepping, or at once reduced.
|
|
if up != rest-8 || reduce != (first == up) {
|
|
t.Errorf("reduce %v: rest %d, first %d, risen %d", reduce, rest, first, up)
|
|
}
|
|
tt.Key(0, ui.KeyEnter)
|
|
if launched != 0 {
|
|
t.Errorf("Enter launched %d", launched)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestControlPanelOpensByKeysAndClicks(t *testing.T) {
|
|
opened := -1
|
|
applets := []desk.Applet{{Name: "SOUND", Icon: icon.Volume}, {Name: "CLOCK", Icon: icon.Clock}, {Name: "POWER", Icon: icon.Power}}
|
|
tt := view(500, 200, newWorld(), func(c *ui.Context) {
|
|
if i := desk.ControlPanel(c, "Panel", applets, desk.ControlPanelOptions{}).Opened; i >= 0 {
|
|
opened = i
|
|
}
|
|
})
|
|
tt.Key(0, ui.KeyTab)
|
|
tt.Key(0, ui.KeyEnd)
|
|
tt.Key(0, ui.KeyEnter)
|
|
if opened != 2 {
|
|
t.Fatalf("End, Enter opened %d", opened)
|
|
}
|
|
r := find(t, tt, "Panel")
|
|
tt.ClickAt(r.X+2*33, r.Y+2*20)
|
|
if opened != 0 {
|
|
t.Fatalf("a click on SOUND opened %d", opened)
|
|
}
|
|
}
|
|
|
|
func TestWallpaperPickerChooses(t *testing.T) {
|
|
paper, changed := desk.Checks, 0
|
|
tt := view(500, 300, newWorld(), func(c *ui.Context) {
|
|
if desk.WallpaperPicker(c, "Paper", &paper, desk.WallpaperPickerOptions{}).Changed {
|
|
changed++
|
|
}
|
|
})
|
|
tt.Key(0, ui.KeyTab)
|
|
tt.Key(0, ui.KeyDown)
|
|
tt.Key(0, ui.KeyEnter)
|
|
if paper != desk.Dots || changed != 1 {
|
|
t.Fatalf("Down, Enter chose %d, %d changes", paper, changed)
|
|
}
|
|
find(t, tt, "DOTS")
|
|
}
|
|
|
|
func TestStartMenuHoldsStillAsSubmenusOpen(t *testing.T) {
|
|
progs := []desk.Program{
|
|
{Name: "TOOLS", Icon: icon.Folder, Programs: []desk.Program{{Name: "SAW", Icon: icon.Sword}, {Name: "AXE", Icon: icon.Sword}, {Name: "HOE", Icon: icon.Leaf}}},
|
|
{Name: "QUIT", Icon: icon.Power},
|
|
}
|
|
for _, low := range []bool{false, true} {
|
|
launched := ""
|
|
tt := view(700, 500, newWorld(), func(c *ui.Context) {
|
|
// Low on the window, the menu opens upward.
|
|
ui.Box(c).Height(map[bool]float32{false: 0, true: 420}[low])
|
|
if r := desk.StartMenu(c, "Start", progs, desk.StartMenuOptions{}); r.Launched != "" {
|
|
launched = r.Launched
|
|
}
|
|
})
|
|
click(t, tt, "Start")
|
|
before := find(t, tt, "TOOLS")
|
|
click(t, tt, "TOOLS")
|
|
if after := find(t, tt, "TOOLS"); after != before {
|
|
t.Errorf("low %v: TOOLS moved from %v to %v", low, before, after)
|
|
}
|
|
click(t, tt, "AXE")
|
|
if launched != "TOOLS/AXE" {
|
|
t.Errorf("low %v: launched %q", low, launched)
|
|
}
|
|
}
|
|
}
|