Upgrade MyGo to v0.5.5
Elements are now pass-scoped value handles: tests that read bounds after a frame bind a ui.Handle. pixel tells frames apart by the build pass's root, since the Tester clock no longer moves on its own. Fix the regressions the upgrade brought: - Clipboard reads in input handlers go through c.Services(). - MyGo notes an overlay's opener after building it, so AutoFocus inside lost it; pixel.AutoFocus focuses through a Handle, and archtest bans the raw call. Dialogs hand the focus back again. - Elements around a press stay hovered: Tooltip tells a press-given focus by focus arriving under the pointer. - The web host moves the Tester clock with real time, at most 500 ms a call, and gives components the real clock. The gallery's source index also accepts CRLF checkouts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
1f3d38110e
commit
03e1f91706
532 files changed
+828
-735
No files matched your search
@@ -24,7 +24,7 @@ type AlertDialogOptions struct {
|
||||
}
|
||||
|
||||
type AlertDialogResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Pressed is the button pressed this frame, or -1.
|
||||
Pressed int
|
||||
Closed bool
|
||||
@@ -46,7 +46,7 @@ func AlertDialog(c *ui.Context, title, message string, open *bool, o AlertDialog
|
||||
was := *open
|
||||
res := AlertDialogResult{Pressed: -1}
|
||||
press := func(i int) { res.Pressed, *open = i, false }
|
||||
res.Element = ui.DialogBase(c, open, func(back, panel *ui.Element) {
|
||||
res.Element = ui.DialogBase(c, open, func(back, panel ui.Element) {
|
||||
dim(k, back, pixel.Back, 8)
|
||||
panel.Width(k.D(220)).Role(ui.RoleAlertDialog).Label(title).Description(message).Focusable().FocusRing(false)
|
||||
if pixel.Shortcut(panel, 0, ui.KeyEnter) && !o.Disabled && !pixel.Current(c).Inert {
|
||||
|
||||
@@ -15,7 +15,7 @@ type ChoicePromptOptions struct {
|
||||
}
|
||||
|
||||
type ChoicePromptResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Chosen is the choice taken this frame, or -1.
|
||||
Chosen int
|
||||
// Canceled is Escape or a click outside.
|
||||
@@ -34,7 +34,7 @@ func ChoicePrompt(c *ui.Context, question string, choices []string, open *bool,
|
||||
cur := ui.Local(home, choiceKey{}, func() int { return 0 })
|
||||
was := *open
|
||||
res := ChoicePromptResult{Chosen: -1}
|
||||
res.Element = ui.DialogBase(c, open, func(back, panel *ui.Element) {
|
||||
res.Element = ui.DialogBase(c, open, func(back, panel ui.Element) {
|
||||
dim(k, back, pixel.Back, 4)
|
||||
panel.Width(k.D(180)).Label(question).Focusable().FocusRing(false).Children(func() {
|
||||
pixel.Harbor(c, panel, func() {
|
||||
|
||||
@@ -16,7 +16,7 @@ type CoachmarkOptions struct {
|
||||
}
|
||||
|
||||
type CoachmarkResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
Next bool
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ type CoachmarkResult struct {
|
||||
const coachWidth = 140
|
||||
|
||||
// Coachmark points a bubble at target: step count, NEXT ▶.
|
||||
func Coachmark(c *ui.Context, target *ui.Element, message string, step, steps int, o CoachmarkOptions) CoachmarkResult {
|
||||
func Coachmark(c *ui.Context, target ui.Element, message string, step, steps int, o CoachmarkOptions) CoachmarkResult {
|
||||
k := pixel.K(c)
|
||||
if step < 1 || step > steps {
|
||||
panic(fmt.Sprintf("overlay: Coachmark step %d of %d", step, steps))
|
||||
|
||||
@@ -15,7 +15,7 @@ type ConfirmDialogOptions struct {
|
||||
}
|
||||
|
||||
type ConfirmDialogResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Canceled is NO, Escape or a click outside.
|
||||
Confirmed, Canceled bool
|
||||
}
|
||||
@@ -29,7 +29,7 @@ func ConfirmDialog(c *ui.Context, message string, open *bool, o ConfirmDialogOpt
|
||||
cur := ui.Local(home, confirmKey{}, func() int { return 1 })
|
||||
was := *open
|
||||
var res ConfirmDialogResult
|
||||
res.Element = ui.DialogBase(c, open, func(back, panel *ui.Element) {
|
||||
res.Element = ui.DialogBase(c, open, func(back, panel ui.Element) {
|
||||
dim(k, back, pixel.Back, 8)
|
||||
panel.Width(k.D(180)).Label(k.Msg("sure")).Description(message).Focusable().FocusRing(false).Children(func() {
|
||||
pixel.Harbor(c, panel, func() {
|
||||
|
||||
+2
-2
@@ -20,7 +20,7 @@ type DialogOptions struct {
|
||||
|
||||
type DialogResult struct {
|
||||
// Element is the window, nil while closed.
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Action is the button clicked this frame, or -1.
|
||||
Action int
|
||||
// Closed is Escape or a click outside.
|
||||
@@ -33,7 +33,7 @@ func Dialog(c *ui.Context, title string, open *bool, o DialogOptions, content fu
|
||||
w := pixel.Or("Dialog Width", o.Width, 220)
|
||||
was := *open
|
||||
res := DialogResult{Action: -1}
|
||||
res.Element = ui.DialogBase(c, open, func(back, panel *ui.Element) {
|
||||
res.Element = ui.DialogBase(c, open, func(back, panel ui.Element) {
|
||||
dim(k, back, pixel.Back, 8)
|
||||
// The panel holds the focus when nothing inside can.
|
||||
panel.Width(k.D(w)).Label(title).Focusable().FocusRing(false).Children(func() {
|
||||
|
||||
@@ -29,7 +29,7 @@ type DialogueBoxOptions struct {
|
||||
}
|
||||
|
||||
type DialogueBoxResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Changed turns a page; Done passes the last one.
|
||||
Changed, Done bool
|
||||
}
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@ type DrawerOptions struct {
|
||||
}
|
||||
|
||||
type DrawerResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Closed is Escape or a click outside.
|
||||
Closed bool
|
||||
}
|
||||
@@ -35,7 +35,7 @@ func Drawer(c *ui.Context, text string, open *bool, o DrawerOptions, content fun
|
||||
off := k.D(arriving(c, home, *open) * size / 4)
|
||||
was := *open
|
||||
var res DrawerResult
|
||||
res.Element = ui.DialogBase(c, open, func(back, panel *ui.Element) {
|
||||
res.Element = ui.DialogBase(c, open, func(back, panel ui.Element) {
|
||||
dim(k, back, pixel.Back, 8)
|
||||
panel.Absolute().Label(text).Padding(k.D(8)).Focusable().FocusRing(false)
|
||||
switch o.Side {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package overlay_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/input"
|
||||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/overlay"
|
||||
)
|
||||
|
||||
func TestDialogsHandTheFocusBack(t *testing.T) {
|
||||
kinds := map[string]func(c *ui.Context, open *bool){
|
||||
"confirm": func(c *ui.Context, open *bool) {
|
||||
overlay.ConfirmDialog(c, "Sure?", open, overlay.ConfirmDialogOptions{})
|
||||
},
|
||||
"choice": func(c *ui.Context, open *bool) {
|
||||
overlay.ChoicePrompt(c, "Pick", []string{"A", "B"}, open, overlay.ChoicePromptOptions{})
|
||||
},
|
||||
"prompt": func(c *ui.Context, open *bool) {
|
||||
v := ""
|
||||
overlay.PromptDialog(c, "Name", &v, open, overlay.PromptDialogOptions{})
|
||||
},
|
||||
"pause": func(c *ui.Context, open *bool) { overlay.PauseMenu(c, open, overlay.PauseMenuOptions{}) },
|
||||
}
|
||||
for name, show := range kinds {
|
||||
open := false
|
||||
tt := view(500, 400, newClock(), false, func(c *ui.Context) {
|
||||
if input.Button(c, "Opener", input.ButtonOptions{}).Clicked {
|
||||
open = true
|
||||
}
|
||||
show(c, &open)
|
||||
})
|
||||
if err := tt.Click("Opener"); err != nil || !open || tt.Focused("Opener") {
|
||||
t.Fatalf("%s: did not open with the focus inside (%v)", name, err)
|
||||
}
|
||||
tt.Key(0, ui.KeyEscape)
|
||||
if open || !tt.Focused("Opener") {
|
||||
t.Fatalf("%s: Escape left it open or the focus lost", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ type HoverCardOptions struct {
|
||||
type hoverKey struct{}
|
||||
|
||||
// HoverCard shows card below trigger while either is hovered.
|
||||
func HoverCard(c *ui.Context, text string, o HoverCardOptions, trigger func(), card func()) *ui.Element {
|
||||
func HoverCard(c *ui.Context, text string, o HoverCardOptions, trigger func(), card func()) ui.Element {
|
||||
k := pixel.K(c)
|
||||
w := pixel.Or("HoverCard Width", o.Width, 160)
|
||||
wrap := ui.Box(c)
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ type KeyHint struct {
|
||||
type KeyHintsOptions struct{}
|
||||
|
||||
// KeyHints shows controls, as Ⓐ CONFIRM Ⓑ BACK.
|
||||
func KeyHints(c *ui.Context, hints []KeyHint, o KeyHintsOptions) *ui.Element {
|
||||
func KeyHints(c *ui.Context, hints []KeyHint, o KeyHintsOptions) ui.Element {
|
||||
k := pixel.K(c)
|
||||
return ui.Row(c).Gap(k.D(10)).Padding(k.D(3), k.D(6)).AlignItems(ui.Center).Role(ui.RoleGroup).Draw(func(p *ui.Painter, r ui.Rect) {
|
||||
g := k.Canvas(p, r)
|
||||
|
||||
+2
-2
@@ -16,7 +16,7 @@ type LightboxOptions struct {
|
||||
}
|
||||
|
||||
type LightboxResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Changed turns the picture; Closed is ×, Escape or outside.
|
||||
Changed, Closed bool
|
||||
}
|
||||
@@ -31,7 +31,7 @@ func Lightbox(c *ui.Context, text string, n int, at *int, open *bool, o Lightbox
|
||||
off := o.Disabled || pixel.Current(c).Inert
|
||||
turn := func(d int) { *at = (*at + d + n) % n }
|
||||
var res LightboxResult
|
||||
res.Element = ui.DialogBase(c, open, func(back, panel *ui.Element) {
|
||||
res.Element = ui.DialogBase(c, open, func(back, panel ui.Element) {
|
||||
deep, light := dark(k)
|
||||
dim(k, back, deep, 13)
|
||||
left, right := pixel.Shortcut(panel, 0, ui.KeyLeft), pixel.Shortcut(panel, 0, ui.KeyRight)
|
||||
|
||||
@@ -31,7 +31,7 @@ type NotificationsOptions struct {
|
||||
|
||||
type NotificationsResult struct {
|
||||
// Element holds the cards on screen.
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Dismissed is the card × closed this frame, or nil.
|
||||
Dismissed *Notification
|
||||
}
|
||||
|
||||
+9
-8
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
// dim checkers the backdrop of a dialog.
|
||||
func dim(k pixel.Kit, back *ui.Element, s pixel.Slot, level int) {
|
||||
func dim(k pixel.Kit, back ui.Element, s pixel.Slot, level int) {
|
||||
back.Draw(func(p *ui.Painter, r ui.Rect) {
|
||||
g := k.Canvas(p, r)
|
||||
g.Veil(0, 0, g.W, g.H, s, level)
|
||||
@@ -33,7 +33,7 @@ func choose(c *ui.Context, text string, items []string, cur *int, digits, off bo
|
||||
off = off || pixel.Current(c).Inert
|
||||
box := ui.Column(c).FocusRing(false).Role(ui.RoleList).Label(text)
|
||||
if !off {
|
||||
box.Focusable().AutoFocus()
|
||||
pixel.AutoFocus(c, box.Focusable())
|
||||
}
|
||||
pixel.Park(c, box, off)
|
||||
picked := -1
|
||||
@@ -92,23 +92,24 @@ type hintKey struct{}
|
||||
|
||||
// hinting is when hover began, and a press-given focus.
|
||||
type hinting struct {
|
||||
since time.Time
|
||||
pressed bool
|
||||
since time.Time
|
||||
focus, pressed bool
|
||||
}
|
||||
|
||||
// hint holds after resting wait, or on keyboard focus.
|
||||
func hint(c *ui.Context, e *ui.Element, wait time.Duration) bool {
|
||||
func hint(c *ui.Context, e ui.Element, wait time.Duration) bool {
|
||||
k := pixel.K(c)
|
||||
s := ui.Local(e, hintKey{}, func() hinting { return hinting{} })
|
||||
_, _, over := e.PointerPosition()
|
||||
hovered, focus := e.Hovered(), e.FocusWithin()
|
||||
switch {
|
||||
case over && !hovered:
|
||||
// A press inside: its focus is not the keyboard's.
|
||||
case focus && !s.focus && over:
|
||||
// Focus arriving under the pointer came from a press.
|
||||
s.pressed = true
|
||||
case !focus:
|
||||
s.pressed = false
|
||||
}
|
||||
s.focus = focus
|
||||
rested := false
|
||||
if hovered {
|
||||
if s.since.IsZero() {
|
||||
@@ -123,7 +124,7 @@ func hint(c *ui.Context, e *ui.Element, wait time.Duration) bool {
|
||||
}
|
||||
|
||||
// arriving counts the steps, of four, a panel still slides.
|
||||
func arriving(c *ui.Context, home *ui.Element, open bool) int {
|
||||
func arriving(c *ui.Context, home ui.Element, open bool) int {
|
||||
k := pixel.K(c)
|
||||
on := 0
|
||||
if open {
|
||||
|
||||
@@ -13,7 +13,7 @@ type PauseMenuOptions struct {
|
||||
}
|
||||
|
||||
type PauseMenuResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Chosen is RESUME 0, OPTIONS 1, QUIT 2, or -1.
|
||||
Chosen int
|
||||
}
|
||||
@@ -28,7 +28,7 @@ func PauseMenu(c *ui.Context, paused *bool, o PauseMenuOptions) PauseMenuResult
|
||||
was := *paused
|
||||
res := PauseMenuResult{Chosen: -1}
|
||||
items := []string{k.Msg("resume"), k.Msg("options"), k.Msg("quit")}
|
||||
res.Element = ui.DialogBase(c, paused, func(back, panel *ui.Element) {
|
||||
res.Element = ui.DialogBase(c, paused, func(back, panel ui.Element) {
|
||||
deep, light := dark(k)
|
||||
dim(k, back, deep, 10)
|
||||
title := k.Msg("paused")
|
||||
|
||||
@@ -14,7 +14,7 @@ type PopconfirmOptions struct {
|
||||
}
|
||||
|
||||
type PopconfirmResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Canceled is NO, Escape or a click outside.
|
||||
Confirmed, Canceled bool
|
||||
}
|
||||
|
||||
+2
-2
@@ -15,11 +15,11 @@ type PopoverOptions struct {
|
||||
const notch = 4
|
||||
|
||||
// Popover opens a panel below anchor, notched toward it.
|
||||
func Popover(c *ui.Context, text string, anchor *ui.Element, open *bool, o PopoverOptions, content func()) *ui.Element {
|
||||
func Popover(c *ui.Context, text string, anchor ui.Element, open *bool, o PopoverOptions, content func()) ui.Element {
|
||||
k := pixel.K(c)
|
||||
w := pixel.Or("Popover Width", o.Width, 0)
|
||||
ab := anchor.Bounds()
|
||||
return ui.PopoverBase(c, anchor, open, func(panel *ui.Element) {
|
||||
return ui.PopoverBase(c, anchor, open, func(panel ui.Element) {
|
||||
if w > 0 {
|
||||
panel.Width(k.D(w))
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ type ProgressDialogOptions struct {
|
||||
}
|
||||
|
||||
type ProgressDialogResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Canceled is CANCEL, Escape or a click outside.
|
||||
Canceled bool
|
||||
}
|
||||
@@ -25,7 +25,7 @@ func ProgressDialog(c *ui.Context, title string, value float64, open *bool, o Pr
|
||||
k := pixel.K(c)
|
||||
was := *open
|
||||
var res ProgressDialogResult
|
||||
res.Element = ui.DialogBase(c, open, func(back, panel *ui.Element) {
|
||||
res.Element = ui.DialogBase(c, open, func(back, panel ui.Element) {
|
||||
dim(k, back, pixel.Back, 8)
|
||||
panel.Width(k.D(200)).Label(title).Focusable().FocusRing(false).Children(func() {
|
||||
pixel.Harbor(c, panel, func() {
|
||||
|
||||
@@ -17,7 +17,7 @@ type PromptDialogOptions struct {
|
||||
}
|
||||
|
||||
type PromptDialogResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Canceled is CANCEL, Escape or a click outside.
|
||||
Submitted, Canceled bool
|
||||
}
|
||||
@@ -27,14 +27,14 @@ func PromptDialog(c *ui.Context, title string, value *string, open *bool, o Prom
|
||||
k := pixel.K(c)
|
||||
was := *open
|
||||
var res PromptDialogResult
|
||||
res.Element = ui.DialogBase(c, open, func(back, panel *ui.Element) {
|
||||
res.Element = ui.DialogBase(c, open, func(back, panel ui.Element) {
|
||||
dim(k, back, pixel.Back, 8)
|
||||
panel.Width(k.D(220)).Label(title).Focusable().FocusRing(false).Children(func() {
|
||||
pixel.Harbor(c, panel, func() {
|
||||
layout.Window(c, layout.WindowOptions{Title: title}, func() {
|
||||
ui.Column(c).Gap(k.D(6)).AlignItems(ui.Stretch).Children(func() {
|
||||
f := input.TextInput(c, title, value, input.TextInputOptions{Placeholder: o.Placeholder, MaxLength: o.MaxLength, Width: 196, Disabled: o.Disabled})
|
||||
f.Input.AutoFocus()
|
||||
pixel.AutoFocus(c, f.Input)
|
||||
// A Shortcut, not Submitted: the frame builds again closed.
|
||||
if pixel.Shortcut(f.Element, 0, ui.KeyEnter) && !o.Disabled {
|
||||
res.Submitted, *open = true, false
|
||||
|
||||
+2
-2
@@ -15,7 +15,7 @@ type SheetOptions struct {
|
||||
}
|
||||
|
||||
type SheetResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Closed is a drag down, Escape or a click outside.
|
||||
Closed bool
|
||||
}
|
||||
@@ -32,7 +32,7 @@ func Sheet(c *ui.Context, text string, open *bool, o SheetOptions, content func(
|
||||
rise := k.D(arriving(c, home, *open) * h / 4)
|
||||
was := *open
|
||||
var res SheetResult
|
||||
res.Element = ui.DialogBase(c, open, func(back, panel *ui.Element) {
|
||||
res.Element = ui.DialogBase(c, open, func(back, panel ui.Element) {
|
||||
dim(k, back, pixel.Back, 8)
|
||||
panel.Label(text).Focusable().FocusRing(false).Draw(func(p *ui.Painter, r ui.Rect) {
|
||||
g := k.Canvas(p, r)
|
||||
|
||||
@@ -9,11 +9,11 @@ import (
|
||||
type SpotlightOptions struct{}
|
||||
|
||||
// Spotlight dithers all but target and captions it.
|
||||
func Spotlight(c *ui.Context, target *ui.Element, caption string, o SpotlightOptions) *ui.Element {
|
||||
func Spotlight(c *ui.Context, target ui.Element, caption string, o SpotlightOptions) ui.Element {
|
||||
k := pixel.K(c)
|
||||
b := target.Bounds()
|
||||
deep, _ := dark(k)
|
||||
var veil *ui.Element
|
||||
var veil ui.Element
|
||||
ui.Overlay(c, func() {
|
||||
veil = ui.Box(c).Absolute().Left(0).Top(0).Right(0).Bottom(0).PassThrough().Role(ui.RoleStatus).Label(caption)
|
||||
veil.Draw(func(p *ui.Painter, r ui.Rect) {
|
||||
|
||||
@@ -16,7 +16,7 @@ type TitleCardOptions struct {
|
||||
}
|
||||
|
||||
// TitleCard dithers in, holds title, dithers out; fills its parent.
|
||||
func TitleCard(c *ui.Context, title string, o TitleCardOptions) *ui.Element {
|
||||
func TitleCard(c *ui.Context, title string, o TitleCardOptions) ui.Element {
|
||||
k := pixel.K(c)
|
||||
if o.Hold < 0 {
|
||||
panic("overlay: TitleCard Hold is negative")
|
||||
|
||||
+3
-3
@@ -15,7 +15,7 @@ type TooltipOptions struct{}
|
||||
const tipWidth = 160
|
||||
|
||||
// Tooltip bubbles text after 500 ms, or on keyboard focus.
|
||||
func Tooltip(c *ui.Context, text string, o TooltipOptions, trigger func()) *ui.Element {
|
||||
func Tooltip(c *ui.Context, text string, o TooltipOptions, trigger func()) ui.Element {
|
||||
k := pixel.K(c)
|
||||
wrap := ui.Box(c)
|
||||
show := hint(c, wrap, 500*time.Millisecond)
|
||||
@@ -32,12 +32,12 @@ func Tooltip(c *ui.Context, text string, o TooltipOptions, trigger func()) *ui.E
|
||||
}
|
||||
|
||||
// bubble floats content by b; w keeps it inside.
|
||||
func bubble(c *ui.Context, b ui.Rect, content func(), w int) *ui.Element {
|
||||
func bubble(c *ui.Context, b ui.Rect, content func(), w int) ui.Element {
|
||||
k := pixel.K(c)
|
||||
ww, wh := c.Size()
|
||||
x := max(0, min(b.X+b.W/2-k.D(8), ww-k.D(w)))
|
||||
at := max(3, int((b.X+b.W/2-x)/k.Dot()))
|
||||
var e *ui.Element
|
||||
var e ui.Element
|
||||
ui.Overlay(c, func() {
|
||||
e = ui.Box(c).Absolute().Left(x).PassThrough()
|
||||
tail := display.Bottom
|
||||
|
||||
Reference in new issue
Block a user