One component per file; GAME OVER offers CONTINUE
PRD G1 asks for one component per file. Eight files held two or three; their eleven extra components move to files of their own, helpers stay where both sides use them, and an archtest now fails any file that holds two. PRD 4.4 and the request say an error always offers CONTINUE. ErrorState's button read RETRY; it now reads CONTINUE, under the CONTINUE? countdown, on the crash screen and the lost page alike.
This commit is contained in:
25 files changed
+439
-359
No files matched your search
@@ -0,0 +1,43 @@
|
||||
package navigation
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"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/pixel"
|
||||
)
|
||||
|
||||
type BackForwardOptions struct {
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
type BackForwardResult struct {
|
||||
Element *ui.Element
|
||||
// Moved reports a step through the history.
|
||||
Moved bool
|
||||
}
|
||||
|
||||
// BackForward walks a router's history with ◀ and ▶.
|
||||
func BackForward(c *ui.Context, r *ui.Router, o BackForwardOptions) BackForwardResult {
|
||||
k := pixel.K(c)
|
||||
res := BackForwardResult{}
|
||||
res.Element = ui.Row(c).Gap(k.D(2)).FocusGroup(ui.Horizontal).Role(ui.RoleToolbar).Label(k.Msg("history")).Children(func() {
|
||||
back := input.IconButtonOptions{Size: input.Small, Disabled: o.Disabled || !r.CanGoBack()}
|
||||
if input.IconButton(c, icon.ChevronLeft, k.Msg("back"), back).Clicked {
|
||||
r.Back()
|
||||
res.Moved = true
|
||||
}
|
||||
forth := input.IconButtonOptions{Size: input.Small, Disabled: o.Disabled || !r.CanGoForward()}
|
||||
if input.IconButton(c, icon.ChevronRight, k.Msg("forward"), forth).Clicked {
|
||||
r.Forward()
|
||||
res.Moved = true
|
||||
}
|
||||
})
|
||||
if res.Moved {
|
||||
slog.Debug("8bit: history", "path", r.Path())
|
||||
}
|
||||
return res
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package navigation
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
|
||||
)
|
||||
|
||||
type BottomNavOptions struct {
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
type BottomNavResult struct {
|
||||
Element *ui.Element
|
||||
Changed bool
|
||||
}
|
||||
|
||||
// BottomNav is a bar of labeled icons for narrow screens.
|
||||
func BottomNav(c *ui.Context, text string, items []NavItem, selected *int, o BottomNavOptions) BottomNavResult {
|
||||
k := pixel.K(c)
|
||||
res := BottomNavResult{}
|
||||
// The bases turn before they learn they are off.
|
||||
v := *selected
|
||||
res.Element = ui.RadioGroup(c, func() {
|
||||
for i, it := range items {
|
||||
shell, _, st := pixel.Shell(c, func() *ui.Element {
|
||||
return ui.RadioBase(c, &v, i).Label(it.Label).Disabled(o.Disabled)
|
||||
})
|
||||
w := max(pixel.Measure(it.Label), 12) + 12
|
||||
shell.Size(k.D(w), k.D(16+pixel.LineHeight)).Draw(func(p *ui.Painter, r ui.Rect) {
|
||||
g := k.Canvas(p, r)
|
||||
ink := pixel.Faint
|
||||
if i == *selected {
|
||||
ink = pixel.Accent
|
||||
g.HLine(2, 0, g.W-4, pixel.Accent)
|
||||
} else if st.Hover {
|
||||
ink = pixel.Ink
|
||||
}
|
||||
g.Sprite((g.W-it.Icon.W)/2, 2, it.Icon, 0, ink, 1)
|
||||
g.Text((g.W-pixel.Measure(it.Label))/2, 14, it.Label, ink, 1)
|
||||
g.Finish(st)
|
||||
})
|
||||
}
|
||||
}).Row().Gap(k.D(4)).Padding(k.D(2), k.D(6)).Label(text).Draw(func(p *ui.Painter, r ui.Rect) {
|
||||
g := k.Canvas(p, r)
|
||||
g.Rect(0, 0, g.W, g.H, pixel.Face)
|
||||
g.HLine(0, 0, g.W, pixel.Edge)
|
||||
})
|
||||
if res.Changed = keep(c, v, selected, o.Disabled); res.Changed {
|
||||
slog.Debug("8bit: bottom nav", "label", text, "selected", *selected)
|
||||
}
|
||||
return res
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package navigation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
|
||||
)
|
||||
|
||||
type PageDotsOptions struct {
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
type PageDotsResult struct {
|
||||
Element *ui.Element
|
||||
Changed bool
|
||||
}
|
||||
|
||||
// PageDots shows a dot per page; clicks jump, arrows move.
|
||||
func PageDots(c *ui.Context, text string, page *int, total int, o PageDotsOptions) PageDotsResult {
|
||||
k := pixel.K(c)
|
||||
check(*page, total)
|
||||
shell, base, st := pixel.Shell(c, func() *ui.Element {
|
||||
return ui.Box(c).Focusable().Role(ui.RoleSlider).Label(text).Description(fmt.Sprintf("%d/%d", *page, total)).Disabled(o.Disabled)
|
||||
})
|
||||
old := *page
|
||||
for _, m := range []struct {
|
||||
key ui.Key
|
||||
to func() int
|
||||
}{
|
||||
{ui.KeyLeft, func() int { return *page - 1 }}, {ui.KeyRight, func() int { return *page + 1 }},
|
||||
{ui.KeyHome, func() int { return 1 }}, {ui.KeyEnd, func() int { return total }},
|
||||
} {
|
||||
if pixel.Shortcut(base, 0, m.key) {
|
||||
*page = max(1, min(total, m.to()))
|
||||
}
|
||||
}
|
||||
// Taking Enter and Space keeps keys from clicking.
|
||||
pixel.Shortcut(base, 0, ui.KeyEnter)
|
||||
pixel.Shortcut(base, 0, ui.KeySpace)
|
||||
if base.Clicked() {
|
||||
x, _, _ := base.PointerPosition()
|
||||
*page = max(1, min(total, int(x/k.Dot())/8+1))
|
||||
}
|
||||
shell.Size(k.D(total*8), k.D(8)).Draw(func(p *ui.Painter, r ui.Rect) {
|
||||
g := k.Canvas(p, r)
|
||||
for i := range total {
|
||||
if i+1 == *page {
|
||||
g.Rect(i*8+1, 1, 6, 6, pixel.Accent)
|
||||
} else {
|
||||
g.Rect(i*8+2, 2, 4, 4, pixel.Shade)
|
||||
}
|
||||
}
|
||||
g.Finish(st)
|
||||
})
|
||||
res := PageDotsResult{Element: shell, Changed: *page != old}
|
||||
if res.Changed {
|
||||
slog.Debug("8bit: page dots", "label", text, "page", *page)
|
||||
}
|
||||
return res
|
||||
}
|
||||
@@ -84,56 +84,3 @@ func check(page, total int) {
|
||||
panic(fmt.Sprintf("navigation: page %d of %d", page, total))
|
||||
}
|
||||
}
|
||||
|
||||
type PageDotsOptions struct {
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
type PageDotsResult struct {
|
||||
Element *ui.Element
|
||||
Changed bool
|
||||
}
|
||||
|
||||
// PageDots shows a dot per page; clicks jump, arrows move.
|
||||
func PageDots(c *ui.Context, text string, page *int, total int, o PageDotsOptions) PageDotsResult {
|
||||
k := pixel.K(c)
|
||||
check(*page, total)
|
||||
shell, base, st := pixel.Shell(c, func() *ui.Element {
|
||||
return ui.Box(c).Focusable().Role(ui.RoleSlider).Label(text).Description(fmt.Sprintf("%d/%d", *page, total)).Disabled(o.Disabled)
|
||||
})
|
||||
old := *page
|
||||
for _, m := range []struct {
|
||||
key ui.Key
|
||||
to func() int
|
||||
}{
|
||||
{ui.KeyLeft, func() int { return *page - 1 }}, {ui.KeyRight, func() int { return *page + 1 }},
|
||||
{ui.KeyHome, func() int { return 1 }}, {ui.KeyEnd, func() int { return total }},
|
||||
} {
|
||||
if pixel.Shortcut(base, 0, m.key) {
|
||||
*page = max(1, min(total, m.to()))
|
||||
}
|
||||
}
|
||||
// Taking Enter and Space keeps keys from clicking.
|
||||
pixel.Shortcut(base, 0, ui.KeyEnter)
|
||||
pixel.Shortcut(base, 0, ui.KeySpace)
|
||||
if base.Clicked() {
|
||||
x, _, _ := base.PointerPosition()
|
||||
*page = max(1, min(total, int(x/k.Dot())/8+1))
|
||||
}
|
||||
shell.Size(k.D(total*8), k.D(8)).Draw(func(p *ui.Painter, r ui.Rect) {
|
||||
g := k.Canvas(p, r)
|
||||
for i := range total {
|
||||
if i+1 == *page {
|
||||
g.Rect(i*8+1, 1, 6, 6, pixel.Accent)
|
||||
} else {
|
||||
g.Rect(i*8+2, 2, 4, 4, pixel.Shade)
|
||||
}
|
||||
}
|
||||
g.Finish(st)
|
||||
})
|
||||
res := PageDotsResult{Element: shell, Changed: *page != old}
|
||||
if res.Changed {
|
||||
slog.Debug("8bit: page dots", "label", text, "page", *page)
|
||||
}
|
||||
return res
|
||||
}
|
||||
@@ -73,49 +73,3 @@ func tip(c *ui.Context, k pixel.Kit, at ui.Rect, s string) {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
type BottomNavOptions struct {
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
type BottomNavResult struct {
|
||||
Element *ui.Element
|
||||
Changed bool
|
||||
}
|
||||
|
||||
// BottomNav is a bar of labeled icons for narrow screens.
|
||||
func BottomNav(c *ui.Context, text string, items []NavItem, selected *int, o BottomNavOptions) BottomNavResult {
|
||||
k := pixel.K(c)
|
||||
res := BottomNavResult{}
|
||||
// The bases turn before they learn they are off.
|
||||
v := *selected
|
||||
res.Element = ui.RadioGroup(c, func() {
|
||||
for i, it := range items {
|
||||
shell, _, st := pixel.Shell(c, func() *ui.Element {
|
||||
return ui.RadioBase(c, &v, i).Label(it.Label).Disabled(o.Disabled)
|
||||
})
|
||||
w := max(pixel.Measure(it.Label), 12) + 12
|
||||
shell.Size(k.D(w), k.D(16+pixel.LineHeight)).Draw(func(p *ui.Painter, r ui.Rect) {
|
||||
g := k.Canvas(p, r)
|
||||
ink := pixel.Faint
|
||||
if i == *selected {
|
||||
ink = pixel.Accent
|
||||
g.HLine(2, 0, g.W-4, pixel.Accent)
|
||||
} else if st.Hover {
|
||||
ink = pixel.Ink
|
||||
}
|
||||
g.Sprite((g.W-it.Icon.W)/2, 2, it.Icon, 0, ink, 1)
|
||||
g.Text((g.W-pixel.Measure(it.Label))/2, 14, it.Label, ink, 1)
|
||||
g.Finish(st)
|
||||
})
|
||||
}
|
||||
}).Row().Gap(k.D(4)).Padding(k.D(2), k.D(6)).Label(text).Draw(func(p *ui.Painter, r ui.Rect) {
|
||||
g := k.Canvas(p, r)
|
||||
g.Rect(0, 0, g.W, g.H, pixel.Face)
|
||||
g.HLine(0, 0, g.W, pixel.Edge)
|
||||
})
|
||||
if res.Changed = keep(c, v, selected, o.Disabled); res.Changed {
|
||||
slog.Debug("8bit: bottom nav", "label", text, "selected", *selected)
|
||||
}
|
||||
return res
|
||||
}
|
||||
@@ -23,20 +23,6 @@ func Tabs(c *ui.Context, text string, labels []string, selected *int, o TabsOpti
|
||||
return tabs(c, text, labels, selected, false, o, panel)
|
||||
}
|
||||
|
||||
type VerticalTabsOptions struct {
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
type VerticalTabsResult struct {
|
||||
Element *ui.Element
|
||||
Changed bool
|
||||
}
|
||||
|
||||
// VerticalTabs are tabs down the panel's left edge.
|
||||
func VerticalTabs(c *ui.Context, text string, labels []string, selected *int, o VerticalTabsOptions, panel func()) VerticalTabsResult {
|
||||
return VerticalTabsResult(tabs(c, text, labels, selected, true, TabsOptions(o), panel))
|
||||
}
|
||||
|
||||
// tabs lays tabs beside a panel; side means left.
|
||||
func tabs(c *ui.Context, text string, labels []string, selected *int, side bool, o TabsOptions, panel func()) TabsResult {
|
||||
k := pixel.K(c)
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
package navigation
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"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/pixel"
|
||||
)
|
||||
|
||||
@@ -20,35 +16,3 @@ func Toolbar(c *ui.Context, text string, o ToolbarOptions, content func()) *ui.E
|
||||
g.Frame(0, 0, g.W, g.H, pixel.Raised, pixel.Face)
|
||||
}).Children(content)
|
||||
}
|
||||
|
||||
type BackForwardOptions struct {
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
type BackForwardResult struct {
|
||||
Element *ui.Element
|
||||
// Moved reports a step through the history.
|
||||
Moved bool
|
||||
}
|
||||
|
||||
// BackForward walks a router's history with ◀ and ▶.
|
||||
func BackForward(c *ui.Context, r *ui.Router, o BackForwardOptions) BackForwardResult {
|
||||
k := pixel.K(c)
|
||||
res := BackForwardResult{}
|
||||
res.Element = ui.Row(c).Gap(k.D(2)).FocusGroup(ui.Horizontal).Role(ui.RoleToolbar).Label(k.Msg("history")).Children(func() {
|
||||
back := input.IconButtonOptions{Size: input.Small, Disabled: o.Disabled || !r.CanGoBack()}
|
||||
if input.IconButton(c, icon.ChevronLeft, k.Msg("back"), back).Clicked {
|
||||
r.Back()
|
||||
res.Moved = true
|
||||
}
|
||||
forth := input.IconButtonOptions{Size: input.Small, Disabled: o.Disabled || !r.CanGoForward()}
|
||||
if input.IconButton(c, icon.ChevronRight, k.Msg("forward"), forth).Clicked {
|
||||
r.Forward()
|
||||
res.Moved = true
|
||||
}
|
||||
})
|
||||
if res.Moved {
|
||||
slog.Debug("8bit: history", "path", r.Path())
|
||||
}
|
||||
return res
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package navigation
|
||||
|
||||
import (
|
||||
"github.com/egoist/mygo/ui"
|
||||
)
|
||||
|
||||
type VerticalTabsOptions struct {
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
type VerticalTabsResult struct {
|
||||
Element *ui.Element
|
||||
Changed bool
|
||||
}
|
||||
|
||||
// VerticalTabs are tabs down the panel's left edge.
|
||||
func VerticalTabs(c *ui.Context, text string, labels []string, selected *int, o VerticalTabsOptions, panel func()) VerticalTabsResult {
|
||||
return VerticalTabsResult(tabs(c, text, labels, selected, true, TabsOptions(o), panel))
|
||||
}
|
||||
Reference in new issue
Block a user