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
@@ -28,7 +28,7 @@ type AppShellOptions struct {
|
||||
|
||||
// AppShellResult is an AppShell and its event in this frame.
|
||||
type AppShellResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Toggled reports the sidebar opened or collapsed.
|
||||
Toggled bool
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ type AspectRatioOptions struct {
|
||||
}
|
||||
|
||||
// AspectRatio centers the largest Ratio frame that fits; the child is centered, not stretched.
|
||||
func AspectRatio(c *ui.Context, opts AspectRatioOptions, child func()) *ui.Element {
|
||||
func AspectRatio(c *ui.Context, opts AspectRatioOptions, child func()) ui.Element {
|
||||
if opts.Ratio < 0 {
|
||||
panic("mujica: AspectRatio Ratio must be positive")
|
||||
}
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ type ContainerOptions struct {
|
||||
}
|
||||
|
||||
// Container pads its children, caps their width and can center, fill and clip.
|
||||
func Container(c *ui.Context, opts ContainerOptions, children func()) *ui.Element {
|
||||
func Container(c *ui.Context, opts ContainerOptions, children func()) ui.Element {
|
||||
if opts.MaxWidth < 0 {
|
||||
panic("mujica: Container MaxWidth must not be negative")
|
||||
}
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ type DividerOptions struct {
|
||||
}
|
||||
|
||||
// Divider is a thin separating line. It never takes focus and its ornament is not read out.
|
||||
func Divider(c *ui.Context, opts DividerOptions) *ui.Element {
|
||||
func Divider(c *ui.Context, opts DividerOptions) ui.Element {
|
||||
if opts.Vertical && opts.Label != "" {
|
||||
panic("mujica: a vertical Divider cannot have a Label")
|
||||
}
|
||||
|
||||
+3
-3
@@ -19,7 +19,7 @@ type GridOptions struct {
|
||||
}
|
||||
|
||||
// Grid lays children out in columns and rows; GridCell places and spans them.
|
||||
func Grid(c *ui.Context, opts GridOptions, children func()) *ui.Element {
|
||||
func Grid(c *ui.Context, opts GridOptions, children func()) ui.Element {
|
||||
if children == nil {
|
||||
panic("mujica: Grid needs children")
|
||||
}
|
||||
@@ -48,7 +48,7 @@ type GridCellOptions struct {
|
||||
}
|
||||
|
||||
// GridCell is one child of a Grid, placed and spanned by opts; build makes one element, stretched over the cell.
|
||||
func GridCell(c *ui.Context, opts GridCellOptions, build func()) *ui.Element {
|
||||
func GridCell(c *ui.Context, opts GridCellOptions, build func()) ui.Element {
|
||||
if build == nil {
|
||||
panic("mujica: GridCell needs build")
|
||||
}
|
||||
@@ -59,7 +59,7 @@ func GridCell(c *ui.Context, opts GridCellOptions, build func()) *ui.Element {
|
||||
panic("mujica: GridCell spans must be positive, or -1 for the rest")
|
||||
}
|
||||
// A one-track grid stretches its child over the whole cell, as the grid does its own children.
|
||||
e := ui.Grid(c).Columns(1).Rows(1)
|
||||
e := ui.Grid(c).Columns(1).GridRows(1)
|
||||
if opts.Column > 0 {
|
||||
e.ColumnStart(opts.Column)
|
||||
}
|
||||
|
||||
@@ -24,11 +24,11 @@ type ScrollAreaOptions struct {
|
||||
}
|
||||
|
||||
// ScrollArea scrolls children; state keeps and sets the offset, nil keeps it by place.
|
||||
func ScrollArea(c *ui.Context, state *ui.ScrollState, opts ScrollAreaOptions, children func()) *ui.Element {
|
||||
func ScrollArea(c *ui.Context, state *ui.ScrollState, opts ScrollAreaOptions, children func()) ui.Element {
|
||||
if children == nil {
|
||||
panic("mujica: ScrollArea needs children")
|
||||
}
|
||||
var e *ui.Element
|
||||
var e ui.Element
|
||||
switch opts.Direction {
|
||||
case ScrollVertical:
|
||||
e = ui.Scroll(c)
|
||||
|
||||
@@ -40,7 +40,7 @@ type SplitPaneOptions struct {
|
||||
|
||||
// SplitPaneResult is a SplitPane and its events in this frame.
|
||||
type SplitPaneResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
Resized bool
|
||||
Toggled bool
|
||||
}
|
||||
@@ -80,7 +80,7 @@ func SplitPane(c *ui.Context, state *SplitPaneState, opts SplitPaneOptions, firs
|
||||
var res SplitPaneResult
|
||||
e.Children(func() {
|
||||
// Sizes come from this inner box: the caller's border and padding stay outside it.
|
||||
in := ui.Row(c).Key("in").Grow(1).MinWidth(0).AlignItems(ui.Stretch)
|
||||
in := ui.Row(c.Key("in")).Grow(1).MinWidth(0).AlignItems(ui.Stretch)
|
||||
if opts.Vertical {
|
||||
in.Column().MinHeight(0)
|
||||
}
|
||||
@@ -94,7 +94,7 @@ func SplitPane(c *ui.Context, state *SplitPaneState, opts SplitPaneOptions, firs
|
||||
}
|
||||
|
||||
// splitPaneBuild lays the panes, divider and drag handle out in the inner box in.
|
||||
func splitPaneBuild(c *ui.Context, in *ui.Element, state *SplitPaneState, opts SplitPaneOptions, lo, minSecond float32, res *SplitPaneResult, first, second func()) {
|
||||
func splitPaneBuild(c *ui.Context, in ui.Element, state *SplitPaneState, opts SplitPaneOptions, lo, minSecond float32, res *SplitPaneResult, first, second func()) {
|
||||
k := core.Tokens(c)
|
||||
u := core.Density(c).Unit()
|
||||
was := in.Bounds()
|
||||
@@ -123,7 +123,7 @@ func splitPaneBuild(c *ui.Context, in *ui.Element, state *SplitPaneState, opts S
|
||||
}
|
||||
firstShown := !(state.Collapsed && opts.Collapsible == SplitPaneFirst)
|
||||
secondShown := !(state.Collapsed && opts.Collapsible == SplitPaneSecond)
|
||||
along := func(el *ui.Element, v float32) {
|
||||
along := func(el ui.Element, v float32) {
|
||||
if opts.Vertical {
|
||||
el.Height(v)
|
||||
} else {
|
||||
@@ -132,7 +132,7 @@ func splitPaneBuild(c *ui.Context, in *ui.Element, state *SplitPaneState, opts S
|
||||
}
|
||||
in.Children(func() {
|
||||
if firstShown {
|
||||
a := ui.Box(c).Key("first").Shrink(0).Clip()
|
||||
a := ui.Box(c.Key("first")).Shrink(0).Clip()
|
||||
switch {
|
||||
case !secondShown:
|
||||
a.Grow(1).Shrink(1)
|
||||
@@ -145,7 +145,7 @@ func splitPaneBuild(c *ui.Context, in *ui.Element, state *SplitPaneState, opts S
|
||||
}
|
||||
a.Children(first)
|
||||
}
|
||||
div := ui.Box(c).Key("divider").Shrink(0).Focusable().FocusRing(false).Role(ui.RoleSplitter).Label(core.Msg(c, "split_pane.divider"))
|
||||
div := ui.Box(c.Key("divider")).Shrink(0).Focusable().FocusRing(false).Role(ui.RoleSplitter).Label(core.Msg(c, "split_pane.divider"))
|
||||
along(div, theme.BorderWidth)
|
||||
if state.Collapsed {
|
||||
div.Description(core.Msg(c, "split_pane.collapsed"))
|
||||
@@ -154,7 +154,7 @@ func splitPaneBuild(c *ui.Context, in *ui.Element, state *SplitPaneState, opts S
|
||||
}
|
||||
splitPaneKeys(c, div, state, opts, lo, hi, u, resize, toggle)
|
||||
if secondShown {
|
||||
b := ui.Box(c).Key("second").Grow(1).Shrink(1).Clip()
|
||||
b := ui.Box(c.Key("second")).Grow(1).Shrink(1).Clip()
|
||||
if opts.Vertical {
|
||||
b.MinHeight(0)
|
||||
} else {
|
||||
@@ -170,7 +170,7 @@ func splitPaneBuild(c *ui.Context, in *ui.Element, state *SplitPaneState, opts S
|
||||
at = total - theme.BorderWidth
|
||||
}
|
||||
grip := 1.5 * u
|
||||
handle := ui.Box(c).Key("handle").Absolute()
|
||||
handle := ui.Box(c.Key("handle")).Absolute()
|
||||
if opts.Vertical {
|
||||
handle.Left(0).Right(0).Top(at - (grip-1)/2).Height(grip).Cursor(ui.CursorResizeNS)
|
||||
} else {
|
||||
@@ -238,7 +238,7 @@ func splitPaneFollow(state *SplitPaneState, opts SplitPaneOptions, want, lo, min
|
||||
}
|
||||
|
||||
// splitPaneKeys moves the divider with the arrows, Home and End, and toggles the collapsible pane with Enter.
|
||||
func splitPaneKeys(c *ui.Context, div *ui.Element, state *SplitPaneState, opts SplitPaneOptions, lo, hi, u float32, resize func(float32), toggle func()) {
|
||||
func splitPaneKeys(c *ui.Context, div ui.Element, state *SplitPaneState, opts SplitPaneOptions, lo, hi, u float32, resize func(float32), toggle func()) {
|
||||
back, forth := ui.KeyLeft, ui.KeyRight
|
||||
if opts.Vertical {
|
||||
back, forth = ui.KeyUp, ui.KeyDown
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ type StackOptions struct {
|
||||
}
|
||||
|
||||
// Stack arranges children in a row or column. A child not built takes no room and no gap.
|
||||
func Stack(c *ui.Context, opts StackOptions, children func()) *ui.Element {
|
||||
func Stack(c *ui.Context, opts StackOptions, children func()) ui.Element {
|
||||
if children == nil {
|
||||
panic("mujica: Stack needs children")
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ type StatusBarOptions struct {
|
||||
|
||||
// StatusBarResult is a StatusBar and its event in this frame.
|
||||
type StatusBarResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Clicked is the ID of the action item clicked, or empty.
|
||||
Clicked string
|
||||
}
|
||||
@@ -86,7 +86,7 @@ func StatusBar(c *ui.Context, opts StatusBarOptions) StatusBarResult {
|
||||
return hasFocus
|
||||
}
|
||||
group := func(key string, items []StatusItem) {
|
||||
ui.Row(c).Key(key).Gap(gap).Shrink(0).Dividers(theme.BorderWidth, k.Border).Children(func() {
|
||||
ui.Row(c.Key(key)).Gap(gap).Shrink(0).Dividers(theme.BorderWidth, k.Border).Children(func() {
|
||||
for _, it := range items {
|
||||
if !hidden[it.ID] && item(it) {
|
||||
*focused = it.ID
|
||||
@@ -96,7 +96,7 @@ func StatusBar(c *ui.Context, opts StatusBarOptions) StatusBarResult {
|
||||
}
|
||||
e.Children(func() {
|
||||
group("status_bar.left", opts.Left)
|
||||
ui.Spacer(c).Key("status_bar.spacer")
|
||||
ui.Spacer(c.Key("status_bar.spacer"))
|
||||
if len(hidden) > 0 {
|
||||
more := statusBarMore(c, len(hidden))
|
||||
if wasFocused != "" && hidden[wasFocused] {
|
||||
@@ -108,7 +108,7 @@ func StatusBar(c *ui.Context, opts StatusBarOptions) StatusBarResult {
|
||||
slog.Debug("mujica: status_bar hidden items", "open", *open)
|
||||
}
|
||||
if *open {
|
||||
ui.PopoverBase(c, more, open, func(panel *ui.Element) {
|
||||
ui.PopoverBase(c, more, open, func(panel ui.Element) {
|
||||
core.OverlayStyle(c, panel, core.OverlayEnter(c, panel))
|
||||
panel.Margin(d.Unit(), 0, 0, 0).Padding(d.Unit()).Gap(d.Unit()).AlignItems(ui.Stretch)
|
||||
for _, it := range statusBarAll(opts) {
|
||||
@@ -126,9 +126,9 @@ func StatusBar(c *ui.Context, opts StatusBarOptions) StatusBarResult {
|
||||
}
|
||||
|
||||
// statusBarMore is the "+N" button that opens the hidden items.
|
||||
func statusBarMore(c *ui.Context, n int) *ui.Element {
|
||||
func statusBarMore(c *ui.Context, n int) ui.Element {
|
||||
k := core.Tokens(c)
|
||||
b := ui.ButtonBase(c).Key("status_bar.more").MinHeight(theme.MinHitSize).MinWidth(theme.MinHitSize).
|
||||
b := ui.ButtonBase(c.Key("status_bar.more")).MinHeight(theme.MinHitSize).MinWidth(theme.MinHitSize).
|
||||
PaddingX(core.Density(c).Unit()).Radius(theme.ControlRadius).TextColor(k.Text).
|
||||
Label(fmt.Sprintf(core.Msg(c, "status_bar.hidden"), n))
|
||||
internal.Ring(b, k.Focus, theme.ControlRadius)
|
||||
@@ -248,9 +248,9 @@ func statusBarItem(c *ui.Context, it StatusItem) (clicked, focused bool) {
|
||||
default:
|
||||
panic("mujica: invalid StatusItemTone")
|
||||
}
|
||||
var e *ui.Element
|
||||
var e ui.Element
|
||||
if it.Action {
|
||||
e = ui.ButtonBase(c).Key(it.ID).MinHeight(theme.MinHitSize).MinWidth(theme.MinHitSize).Radius(theme.ControlRadius)
|
||||
e = ui.ButtonBase(c.Key(it.ID)).MinHeight(theme.MinHitSize).MinWidth(theme.MinHitSize).Radius(theme.ControlRadius)
|
||||
internal.Ring(e, k.Focus, theme.ControlRadius)
|
||||
if e.Pressed() {
|
||||
e.Background(k.Selection)
|
||||
@@ -258,7 +258,7 @@ func statusBarItem(c *ui.Context, it StatusItem) (clicked, focused bool) {
|
||||
e.Background(k.SurfaceHover)
|
||||
}
|
||||
} else {
|
||||
e = ui.Row(c).Key(it.ID).MinHeight(theme.MinHitSize)
|
||||
e = ui.Row(c.Key(it.ID)).MinHeight(theme.MinHitSize)
|
||||
}
|
||||
e.PaddingX(d.Unit()).Gap(d.Unit()).TextColor(fg).Shrink(0)
|
||||
if it.Label != "" {
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ type TitleBarOptions struct {
|
||||
}
|
||||
|
||||
// TitleBar is a window title row that drags the window and clears the system buttons.
|
||||
func TitleBar(c *ui.Context, title string, opts TitleBarOptions) *ui.Element {
|
||||
func TitleBar(c *ui.Context, title string, opts TitleBarOptions) ui.Element {
|
||||
k := core.Tokens(c)
|
||||
d := core.Density(c)
|
||||
bar := c.TitleBar()
|
||||
|
||||
Reference in new issue
Block a user