Upgrade MyGo to v0.5.5
MyGo v0.5.5 returns ui.Element as a checked value handle instead of a
pointer. Migrated with `mygo migrate-ui`, then fixed what the codemod
leaves to review:
- Element nil checks become .Valid(); absent elements are ui.Element{}.
- Input handlers and paint callbacks run outside the build pass, where
elements expire and Context methods are inert: read IsDisabled into a
local during the build, keep a ui.Handle for Focus, use c.Services()
for clipboard access, create ui.Local state during the build.
- Add core.AutoFocus: v0.5.5 records an overlay's opener after its
content builds, so same-frame autofocus lost focus restore on close.
It focuses one frame later; cascader rows defer their focus likewise.
- core.OverlayLayer identifies build passes by the root element, not
c.Now(), which repeats across passes and under the Tester clock.
- Context menus survive the opening press counted as PressedOutside;
split button tooltip no longer reorders its open menu; disabled
menubar rows pass presses to the panel; Form defers blur-started
async checks so the growing message no longer moves Submit away
from the release; Combobox drops its disabled copy now that MyGo
blocks typing under a disabled wrapper (canary flipped to a guard).
- The Tester clock is virtual: tests use Advance/Now instead of
time.Sleep/time.Now and read element state inside the view. The web
mirror advances the clock to wall time before each frame and input.
- AGENTS.md and docs describe the value Element API and the new pin.
Snapshot goldens are darwin-only and were not re-verified here.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
bc716d27be
commit
bcce34cda1
603 files changed
+1366
-1277
No files matched your search
+1
-1
@@ -18,7 +18,7 @@ type AvatarOptions struct {
|
||||
}
|
||||
|
||||
// Avatar shows a person as picture, initials or icon.
|
||||
func Avatar(c *ui.Context, name string, opts AvatarOptions) *ui.Element {
|
||||
func Avatar(c *ui.Context, name string, opts AvatarOptions) ui.Element {
|
||||
size := opts.Size
|
||||
if size < 0 {
|
||||
panic("mujica: Avatar Size must not be negative")
|
||||
|
||||
@@ -29,7 +29,7 @@ type avatarGroupKey struct{}
|
||||
|
||||
// AvatarGroupView is a shown AvatarGroup and its full list's state.
|
||||
type AvatarGroupView struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Open reports whether the full list shows.
|
||||
Open bool
|
||||
// Toggled reports the full list opened or closed this frame.
|
||||
@@ -60,7 +60,7 @@ func AvatarGroup(c *ui.Context, items []AvatarItem, opts AvatarGroupOptions) Ava
|
||||
was := *open
|
||||
row.Children(func() {
|
||||
for i, it := range items[:shown] {
|
||||
cell := ui.Box(c).Key(i).Padding(ring).Radius(size/2 + ring).Background(k.Background).Shrink(0)
|
||||
cell := ui.Box(c.Key(i)).Padding(ring).Radius(size/2 + ring).Background(k.Background).Shrink(0)
|
||||
if i > 0 {
|
||||
cell.Margin(0, 0, 0, -size/4)
|
||||
}
|
||||
@@ -72,7 +72,7 @@ func AvatarGroup(c *ui.Context, items []AvatarItem, opts AvatarGroupOptions) Ava
|
||||
return
|
||||
}
|
||||
more := avatarGroupMore(c, k, open, size, ring, rest, len(items))
|
||||
ui.PopoverBase(c, more, open, func(panel *ui.Element) {
|
||||
ui.PopoverBase(c, more, open, func(panel ui.Element) {
|
||||
avatarGroupList(c, k, panel, items)
|
||||
})
|
||||
})
|
||||
@@ -84,10 +84,10 @@ func AvatarGroup(c *ui.Context, items []AvatarItem, opts AvatarGroupOptions) Ava
|
||||
}
|
||||
|
||||
// avatarGroupMore is the "+N" disclosure toggling *open.
|
||||
func avatarGroupMore(c *ui.Context, k theme.Tokens, open *bool, size, ring float32, rest, total int) *ui.Element {
|
||||
func avatarGroupMore(c *ui.Context, k theme.Tokens, open *bool, size, ring float32, rest, total int) ui.Element {
|
||||
hit := max(size+2*ring, theme.MinHitSize)
|
||||
var b *ui.Element
|
||||
ui.Box(c).Key("more").Margin(0, 0, 0, -size/4).Shrink(0).Children(func() {
|
||||
var b ui.Element
|
||||
ui.Box(c.Key("more")).Margin(0, 0, 0, -size/4).Shrink(0).Children(func() {
|
||||
b = ui.CollapsibleBase(c, open).Trigger.Size(hit, hit).Center().Radius(hit / 2).
|
||||
Label(fmt.Sprintf(core.Msg(c, "avatar_group.more"), total))
|
||||
internal.Ring(b, k.Focus, hit/2)
|
||||
@@ -111,7 +111,7 @@ func avatarGroupMore(c *ui.Context, k theme.Tokens, open *bool, size, ring float
|
||||
return b
|
||||
}
|
||||
|
||||
func avatarGroupList(c *ui.Context, k theme.Tokens, panel *ui.Element, items []AvatarItem) {
|
||||
func avatarGroupList(c *ui.Context, k theme.Tokens, panel ui.Element, items []AvatarItem) {
|
||||
u := core.Density(c).Unit()
|
||||
panel.Margin(u, 0, 0, 0).Padding(u*2).Radius(theme.PopupRadius).Background(k.Surface).
|
||||
Border(theme.BorderWidth, k.Border).MinWidth(200)
|
||||
@@ -121,7 +121,7 @@ func avatarGroupList(c *ui.Context, k theme.Tokens, panel *ui.Element, items []A
|
||||
ui.Scroll(c).MaxHeight(240).Children(func() {
|
||||
ui.Column(c).Gap(u * 2).Role(ui.RoleList).Children(func() {
|
||||
for i, it := range items {
|
||||
ui.Row(c).Key(i).Gap(u * 2).AlignItems(ui.Center).Role(ui.RoleListItem).Children(func() {
|
||||
ui.Row(c.Key(i)).Gap(u * 2).AlignItems(ui.Center).Role(ui.RoleListItem).Children(func() {
|
||||
Avatar(c, it.Name, AvatarOptions{Image: it.Image, Size: 24})
|
||||
ui.Text(c, it.Name).SingleLine()
|
||||
})
|
||||
|
||||
+2
-2
@@ -48,7 +48,7 @@ type BadgeOptions struct {
|
||||
}
|
||||
|
||||
// Badge shows text or a count, inline or cornered.
|
||||
func Badge(c *ui.Context, opts BadgeOptions, content func()) *ui.Element {
|
||||
func Badge(c *ui.Context, opts BadgeOptions, content func()) ui.Element {
|
||||
if opts.Count < 0 || opts.Max < 0 {
|
||||
panic("mujica: Badge Count and Max must not be negative")
|
||||
}
|
||||
@@ -92,7 +92,7 @@ func badgeText(opts BadgeOptions) (shown, full string) {
|
||||
return full, full
|
||||
}
|
||||
|
||||
func badgePill(c *ui.Context, opts BadgeOptions, shown, full string) *ui.Element {
|
||||
func badgePill(c *ui.Context, opts BadgeOptions, shown, full string) ui.Element {
|
||||
k := core.Tokens(c)
|
||||
bg, fg, border := badgeColors(k, opts.Tone)
|
||||
label := full
|
||||
|
||||
@@ -101,7 +101,7 @@ func ExampleTag() {
|
||||
ui.Row(c).Gap(8).Children(func() {
|
||||
for i, t := range tags {
|
||||
var v display.TagView
|
||||
ui.Box(c).Key(t).Children(func() {
|
||||
ui.Box(c.Key(t)).Children(func() {
|
||||
v = display.Tag(c, t, display.TagOptions{Icon: "star", Clickable: true, Closable: true})
|
||||
})
|
||||
if v.Closed {
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ type HeadingOptions struct {
|
||||
}
|
||||
|
||||
// Heading shows a serif title scaled with desktop text.
|
||||
func Heading(c *ui.Context, title string, opts HeadingOptions) *ui.Element {
|
||||
func Heading(c *ui.Context, title string, opts HeadingOptions) ui.Element {
|
||||
k := core.Tokens(c)
|
||||
size := headingSize(opts.Level)
|
||||
u := core.Density(c).Unit()
|
||||
|
||||
+2
-2
@@ -36,7 +36,7 @@ type IconOptions struct {
|
||||
type iconMissingKey struct{}
|
||||
|
||||
// Icon shows a named icon; missing ones show a fallback.
|
||||
func Icon(c *ui.Context, name string, opts IconOptions) *ui.Element {
|
||||
func Icon(c *ui.Context, name string, opts IconOptions) ui.Element {
|
||||
if name == "" {
|
||||
panic("mujica: Icon needs a name")
|
||||
}
|
||||
@@ -62,7 +62,7 @@ func Icon(c *ui.Context, name string, opts IconOptions) *ui.Element {
|
||||
return e
|
||||
}
|
||||
|
||||
func iconMissing(c *ui.Context, name string, size float32, k theme.Tokens) *ui.Element {
|
||||
func iconMissing(c *ui.Context, name string, size float32, k theme.Tokens) ui.Element {
|
||||
text := fmt.Sprintf(core.Msg(c, "icon.missing"), name)
|
||||
e := ui.Box(c).Size(size, size).Shrink(0).Role(ui.RoleImage).Label(text).Tooltip(text)
|
||||
logged := ui.Local(e, iconMissingKey{}, func() string { return "" })
|
||||
|
||||
+5
-5
@@ -26,7 +26,7 @@ type ImageOptions struct {
|
||||
|
||||
// ImageView is a shown Image and this frame's events.
|
||||
type ImageView struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Retry reports the error state's retry button was pressed.
|
||||
Retry bool
|
||||
}
|
||||
@@ -46,7 +46,7 @@ func Image(c *ui.Context, src ui.ImageSource, opts ImageOptions) ImageView {
|
||||
}
|
||||
loaded := opts.Err == nil && !opts.Loading && src != nil
|
||||
var v ImageView
|
||||
v.Element = imageSized(c, src, opts, func(box, pic *ui.Element) {
|
||||
v.Element = imageSized(c, src, opts, func(box, pic ui.Element) {
|
||||
if loaded {
|
||||
if opts.Alt != "" {
|
||||
pic.Label(opts.Alt)
|
||||
@@ -60,7 +60,7 @@ func Image(c *ui.Context, src ui.ImageSource, opts ImageOptions) ImageView {
|
||||
}
|
||||
|
||||
// imageLayers builds the state layers over the hidden picture.
|
||||
func imageLayers(c *ui.Context, box *ui.Element, opts ImageOptions) (retry bool) {
|
||||
func imageLayers(c *ui.Context, box ui.Element, opts ImageOptions) (retry bool) {
|
||||
k := core.Tokens(c)
|
||||
u := core.Density(c).Unit()
|
||||
room := box.Bounds()
|
||||
@@ -99,7 +99,7 @@ func imageLayers(c *ui.Context, box *ui.Element, opts ImageOptions) (retry bool)
|
||||
}
|
||||
|
||||
// imageSized builds the outer box and its picture, sized alike in every state.
|
||||
func imageSized(c *ui.Context, src ui.ImageSource, opts ImageOptions, layers func(box, pic *ui.Element)) *ui.Element {
|
||||
func imageSized(c *ui.Context, src ui.ImageSource, opts ImageOptions, layers func(box, pic ui.Element)) ui.Element {
|
||||
if src == nil {
|
||||
src = imageBlank
|
||||
}
|
||||
@@ -123,7 +123,7 @@ func imageSized(c *ui.Context, src ui.ImageSource, opts ImageOptions, layers fun
|
||||
}
|
||||
|
||||
// imageRetry is the 28 DIP icon-only retry button.
|
||||
func imageRetry(c *ui.Context, k theme.Tokens, problem string) *ui.Element {
|
||||
func imageRetry(c *ui.Context, k theme.Tokens, problem string) ui.Element {
|
||||
b := ui.ButtonBase(c).Size(theme.MinHitSize, theme.MinHitSize).Shrink(0).Radius(theme.ControlRadius).
|
||||
Label(core.Msg(c, "image.retry")).Description(problem).Tooltip(problem)
|
||||
bg := k.Surface
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
// Kbd shows keys as caps; "Mod" is Command or Ctrl.
|
||||
func Kbd(c *ui.Context, keys ...string) *ui.Element {
|
||||
func Kbd(c *ui.Context, keys ...string) ui.Element {
|
||||
if len(keys) == 0 {
|
||||
panic("mujica: Kbd needs at least one key")
|
||||
}
|
||||
@@ -31,7 +31,7 @@ func Kbd(c *ui.Context, keys ...string) *ui.Element {
|
||||
ui.Text(c, "+").FontSize(core.FontSize(c, theme.CaptionSize)).TextColor(k.TextMuted)
|
||||
}
|
||||
face, _ := KbdKey(key, mac)
|
||||
ui.Box(c).Key(i).Row().Center().MinWidth(core.FontSize(c, 20)).MinHeight(core.FontSize(c, 20)).Padding(0, 5).
|
||||
ui.Box(c.Key(i)).Row().Center().MinWidth(core.FontSize(c, 20)).MinHeight(core.FontSize(c, 20)).Padding(0, 5).
|
||||
Radius(theme.ControlRadius).Background(k.Surface).BorderColor(k.ControlBorder).
|
||||
BorderWidth(1, 1, 2, 1).Children(func() {
|
||||
ui.Text(c, face).Font(theme.MonoFont).FontSize(core.FontSize(c, theme.CaptionSize)).TextColor(k.Text).SingleLine()
|
||||
|
||||
+2
-2
@@ -21,7 +21,7 @@ type LinkOptions struct {
|
||||
}
|
||||
|
||||
// Link is underlined text; malformed URLs show an error instead.
|
||||
func Link(c *ui.Context, label string, opts LinkOptions) *ui.Element {
|
||||
func Link(c *ui.Context, label string, opts LinkOptions) ui.Element {
|
||||
if label == "" {
|
||||
panic("mujica: Link needs a label")
|
||||
}
|
||||
@@ -40,7 +40,7 @@ func Link(c *ui.Context, label string, opts LinkOptions) *ui.Element {
|
||||
}
|
||||
}
|
||||
}
|
||||
var l *ui.Element
|
||||
var l ui.Element
|
||||
ui.Column(c).Gap(u).AlignItems(ui.Start).Children(func() {
|
||||
ui.Row(c).Gap(u).AlignItems(ui.Center).Children(func() {
|
||||
l = ui.Link(c, label, target).Underline().DecorationThickness(1)
|
||||
|
||||
+3
-3
@@ -24,7 +24,7 @@ type TagOptions struct {
|
||||
|
||||
// TagView is a shown Tag and this frame's events.
|
||||
type TagView struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Clicked reports the label was activated, not the remove button.
|
||||
Clicked bool
|
||||
// Closed reports the remove button, Delete or Backspace.
|
||||
@@ -43,7 +43,7 @@ func Tag(c *ui.Context, label string, opts TagOptions) TagView {
|
||||
Background(k.Surface).Border(theme.BorderWidth, k.Border).Shrink(0).Disabled(opts.Disabled)
|
||||
v.Element = box
|
||||
box.Children(func() {
|
||||
var main *ui.Element
|
||||
var main ui.Element
|
||||
if opts.Clickable {
|
||||
main = ui.ButtonBase(c).Label(label)
|
||||
internal.Ring(main, k.Focus, theme.ControlRadius)
|
||||
@@ -89,7 +89,7 @@ func Tag(c *ui.Context, label string, opts TagOptions) TagView {
|
||||
return v
|
||||
}
|
||||
|
||||
func tagClose(c *ui.Context, k theme.Tokens, label string) *ui.Element {
|
||||
func tagClose(c *ui.Context, k theme.Tokens, label string) ui.Element {
|
||||
b := ui.ButtonBase(c).Size(theme.MinHitSize, theme.MinHitSize).
|
||||
Radius(theme.ControlRadius).Label(fmt.Sprintf(core.Msg(c, "tag.remove"), label))
|
||||
fg := k.TextMuted
|
||||
|
||||
+3
-3
@@ -19,7 +19,7 @@ type TextOptions struct {
|
||||
}
|
||||
|
||||
// Text shows body text wrapping at its width.
|
||||
func Text(c *ui.Context, s string, opts TextOptions) *ui.Element {
|
||||
func Text(c *ui.Context, s string, opts TextOptions) ui.Element {
|
||||
if opts.MaxLines < 0 {
|
||||
panic("mujica: Text MaxLines must not be negative")
|
||||
}
|
||||
@@ -49,7 +49,7 @@ type EditableTextOptions struct {
|
||||
|
||||
// EditableTextView is a shown EditableText and this frame's outcome.
|
||||
type EditableTextView struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Canceled reports Escape discarded an edit.
|
||||
Canceled bool
|
||||
changed bool
|
||||
@@ -118,7 +118,7 @@ func editableTextField(c *ui.Context, k theme.Tokens, value *string, opts Editab
|
||||
return v
|
||||
}
|
||||
}
|
||||
var in *ui.Element
|
||||
var in ui.Element
|
||||
box.Children(func() {
|
||||
in = ui.TextInputBase(c, &st.draft).Grow(1).MinWidth(theme.MinHitSize*3).Padding(pad, pad*2).
|
||||
Radius(theme.ControlRadius).Background(k.Surface).Border(theme.BorderWidth, k.Accent).TextColor(k.Text).
|
||||
|
||||
Reference in new issue
Block a user