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
@@ -74,7 +74,7 @@ func AssigneePicker(c *ui.Context, ids *[]string, members []ProjectMember, opts
|
||||
st.shown = nil
|
||||
}
|
||||
before := *ids
|
||||
var trigger *ui.Element
|
||||
var trigger ui.Element
|
||||
row.Children(func() {
|
||||
if len(*ids) > 0 {
|
||||
items := make([]display.AvatarItem, min(3, len(*ids)))
|
||||
@@ -100,7 +100,7 @@ func AssigneePicker(c *ui.Context, ids *[]string, members []ProjectMember, opts
|
||||
}
|
||||
input.MultiSelectTags(c, ids, options, input.MultiSelectOptions{MaxTags: 2, Disabled: trigger.IsDisabled(), Placeholder: core.Msg(c, "assignee_picker.placeholder")})
|
||||
})
|
||||
ui.PopoverBase(c, trigger, &st.open, func(panel *ui.Element) {
|
||||
ui.PopoverBase(c, trigger, &st.open, func(panel ui.Element) {
|
||||
input.SelectPanel(c, panel)
|
||||
ww, wh := c.Size()
|
||||
panel.MinWidth(min(ww-8, trigger.Bounds().W)).MaxWidth(max(1, ww-8)).MaxHeight(max(1, wh-8))
|
||||
@@ -147,7 +147,7 @@ func AssigneePicker(c *ui.Context, ids *[]string, members []ProjectMember, opts
|
||||
} else if st.count > 0 {
|
||||
mark = internal.Mixed
|
||||
}
|
||||
var all *ui.Element
|
||||
var all ui.Element
|
||||
ui.Row(c).MinHeight(d.ControlHeight()).Children(func() {
|
||||
all = input.MultiSelectCheck(c, k, &on, mark, core.Msg(c, "multi_select.all"), full && st.count == 0)
|
||||
})
|
||||
@@ -176,7 +176,7 @@ func AssigneePicker(c *ui.Context, ids *[]string, members []ProjectMember, opts
|
||||
if selected {
|
||||
state = internal.Checked
|
||||
}
|
||||
var cb *ui.Element
|
||||
var cb ui.Element
|
||||
ui.Row(c).MinHeight(d.ControlHeight()).Children(func() { cb = input.MultiSelectCheck(c, k, &selected, state, o.Label, full && !selected) })
|
||||
if st.focus == o.Value && !cb.IsDisabled() {
|
||||
cb.Focus()
|
||||
|
||||
@@ -23,7 +23,7 @@ type IssueCardOptions struct {
|
||||
}
|
||||
|
||||
// IssueCard displays task fields in a selectable card.
|
||||
func IssueCard(c *ui.Context, task Task, opts IssueCardOptions) *ui.Element {
|
||||
func IssueCard(c *ui.Context, task Task, opts IssueCardOptions) ui.Element {
|
||||
projectCheckPriority(task.Priority)
|
||||
st := projectStatus(opts.Statuses, task.Status)
|
||||
k := core.Tokens(c)
|
||||
|
||||
@@ -54,8 +54,7 @@ func TestIssueIdBadgeCopiedFades(t *testing.T) {
|
||||
if !tt.HasText("Copied") {
|
||||
t.Fatal("no Copied note")
|
||||
}
|
||||
time.Sleep(issueCopiedFor + 50*time.Millisecond)
|
||||
tt.Frame()
|
||||
tt.Advance(issueCopiedFor + 50*time.Millisecond)
|
||||
if tt.HasText("Copied") {
|
||||
t.Fatal("Copied should go away")
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ type KanbanMove struct {
|
||||
|
||||
// KanbanResult is the board and what the user did.
|
||||
type KanbanResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
move *KanbanMove
|
||||
opened string
|
||||
}
|
||||
@@ -183,7 +183,7 @@ func kanbanColumn(c *ui.Context, st *kanbanState, ci int, col KanbanColumn, cols
|
||||
status := projectStatus(opts.Statuses, col.Status)
|
||||
over := col.WIP > 0 && len(idx) > col.WIP
|
||||
full := col.WIP > 0 && len(idx) >= col.WIP
|
||||
box := ui.Column(c).Key(col.Status).Width(d.ControlHeight()*7.5).Shrink(0).Gap(d.Unit()*2).Padding(d.Unit()*2).
|
||||
box := ui.Column(c.Key(col.Status)).Width(d.ControlHeight()*7.5).Shrink(0).Gap(d.Unit()*2).Padding(d.Unit()*2).
|
||||
Radius(theme.CardRadius).Background(k.Background).Border(theme.BorderWidth, k.Border).Role(ui.RoleGroup).Label(status.Name)
|
||||
if id, ok := ui.Drop[kanbanDrag](box); ok && !box.IsDisabled() {
|
||||
move(string(id), col.Status, "", "drag")
|
||||
|
||||
@@ -166,8 +166,8 @@ func TestKanbanBoardForeignDrop(t *testing.T) {
|
||||
tt := ui.NewTester(func(c *ui.Context) {
|
||||
core.Use(c, core.Settings{})
|
||||
ui.Row(c).Height(400).Children(func() {
|
||||
ui.Box(c).Key("left").Width(300).Height(400).Children(func() { KanbanBoard(c, &left, opts) })
|
||||
ui.Box(c).Key("right").Width(300).Height(400).Children(func() { KanbanBoard(c, &right, opts) })
|
||||
ui.Box(c.Key("left")).Width(300).Height(400).Children(func() { KanbanBoard(c, &left, opts) })
|
||||
ui.Box(c.Key("right")).Width(300).Height(400).Children(func() { KanbanBoard(c, &right, opts) })
|
||||
})
|
||||
}, 640, 420)
|
||||
src, _ := tt.Find("A-1 First")
|
||||
|
||||
@@ -81,7 +81,7 @@ func LabelManager(c *ui.Context, labels *[]TaskLabel, selected *[]string, opts L
|
||||
removed := -1
|
||||
ui.List(c, state, len(*labels), func(i int) {
|
||||
l := &(*labels)[i]
|
||||
ui.Row(c).Key(l.ID).MinHeight(d.ControlHeight()).Gap(d.Unit() * 2).AlignItems(ui.Center).Children(func() {
|
||||
ui.Row(c.Key(l.ID)).MinHeight(d.ControlHeight()).Gap(d.Unit() * 2).AlignItems(ui.Center).Children(func() {
|
||||
on := cache.selected[l.ID]
|
||||
box := ui.CheckboxBase(c, &on).Size(theme.MinHitSize, theme.MinHitSize).Center().Label(l.Name)
|
||||
internal.Ring(box, k.Focus, theme.ControlRadius)
|
||||
@@ -146,7 +146,7 @@ func labelEditRow(c *ui.Context, s *labelManagerState, l *TaskLabel, taken func(
|
||||
d := core.Density(c)
|
||||
ui.Column(c).Grow(1).Gap(d.Unit()).Children(func() {
|
||||
in := input.TextInput(c, &s.name, input.TextInputOptions{Label: core.Msg(c, "label_manager.name"), Error: s.problem})
|
||||
in.Input.AutoFocus()
|
||||
core.AutoFocus(c, in.Input)
|
||||
ui.Row(c).Gap(d.Unit()).Role(ui.RoleRadioGroup).Label(core.Msg(c, "label_manager.color")).Children(func() {
|
||||
for i := range 6 {
|
||||
col := datetime.EventColor(i)
|
||||
|
||||
@@ -25,7 +25,7 @@ type MilestoneProgressOptions struct {
|
||||
}
|
||||
|
||||
// MilestoneProgress shows completion counts, progress and due dates.
|
||||
func MilestoneProgress(c *ui.Context, m Milestone, opts MilestoneProgressOptions) *ui.Element {
|
||||
func MilestoneProgress(c *ui.Context, m Milestone, opts MilestoneProgressOptions) ui.Element {
|
||||
core.DatetimeCheckDate("MilestoneProgress Due", m.Due)
|
||||
if m.Total < 0 || m.Done < 0 || m.Done > m.Total {
|
||||
panic(fmt.Sprintf("mujica: MilestoneProgress %d of %d is not valid", m.Done, m.Total))
|
||||
|
||||
@@ -64,7 +64,7 @@ type PomodoroTimerOptions struct {
|
||||
|
||||
// PomodoroResult is the timer and what changed this frame.
|
||||
type PomodoroResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
changed bool
|
||||
finished PomodoroPhase
|
||||
done bool
|
||||
|
||||
@@ -23,7 +23,7 @@ func PriorityName(c *ui.Context, p Priority) string {
|
||||
}
|
||||
|
||||
// PriorityIndicator displays urgency with shapes and localized text.
|
||||
func PriorityIndicator(c *ui.Context, p Priority, opts PriorityIndicatorOptions) *ui.Element {
|
||||
func PriorityIndicator(c *ui.Context, p Priority, opts PriorityIndicatorOptions) ui.Element {
|
||||
name := PriorityName(c, p)
|
||||
k := core.Tokens(c)
|
||||
d := core.Density(c)
|
||||
|
||||
+2
-2
@@ -75,7 +75,7 @@ type WorkflowStatus struct {
|
||||
|
||||
// ProjectResult exposes the element and task events.
|
||||
type ProjectResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
changed string
|
||||
opened string
|
||||
}
|
||||
@@ -139,7 +139,7 @@ func projectDueText(c *ui.Context, due, today core.Date, done bool) (string, boo
|
||||
}
|
||||
|
||||
// projectBar draws a named completion bar.
|
||||
func projectBar(c *ui.Context, frac float64, label string) *ui.Element {
|
||||
func projectBar(c *ui.Context, frac float64, label string) ui.Element {
|
||||
k := core.Tokens(c)
|
||||
return ui.Box(c).Height(6).FillWidth().Role(ui.RoleProgress).Label(label).Description(fmt.Sprintf("%d%%", int(frac*100))).
|
||||
Draw(func(p *ui.Painter, r ui.Rect) {
|
||||
|
||||
@@ -41,7 +41,7 @@ func StatusSelect(c *ui.Context, value *string, statuses []WorkflowStatus, opts
|
||||
k := core.Tokens(c)
|
||||
old := *value
|
||||
pick := old
|
||||
var s *ui.SelectParts[string]
|
||||
var s ui.SelectParts[string]
|
||||
root := ui.Row(c).Shrink(0).Disabled(opts.Disabled)
|
||||
epoch := ui.Local(root, "status-epoch", func() struct {
|
||||
disabled bool
|
||||
@@ -57,7 +57,7 @@ func StatusSelect(c *ui.Context, value *string, statuses []WorkflowStatus, opts
|
||||
epoch.generation++
|
||||
}
|
||||
root.Children(func() {
|
||||
ui.Row(c).Key(epoch.generation).Children(func() { s = ui.SelectBase(c, &pick) })
|
||||
ui.Row(c.Key(epoch.generation)).Children(func() { s = ui.SelectBase(c, &pick) })
|
||||
})
|
||||
tr := s.Trigger
|
||||
label := opts.Label
|
||||
@@ -70,7 +70,7 @@ func StatusSelect(c *ui.Context, value *string, statuses []WorkflowStatus, opts
|
||||
ui.Text(c, cur.Name).SingleLine().Grow(1).Shrink(1).TextColor(k.Text)
|
||||
})
|
||||
if !tr.IsDisabled() {
|
||||
s.Popup(func(panel *ui.Element) {
|
||||
s.Popup(func(panel ui.Element) {
|
||||
input.SelectPanel(c, panel)
|
||||
_, wh := c.Size()
|
||||
ui.Scroll(c).MaxHeight(min(320, wh/2)).Children(func() {
|
||||
|
||||
@@ -88,7 +88,7 @@ func SubtaskList(c *ui.Context, subs *[]Subtask, opts SubtaskListOptions) core.C
|
||||
removed := -1
|
||||
ui.List(c, state, len(*subs), func(i int) {
|
||||
s := &(*subs)[i]
|
||||
row := ui.Row(c).Key(s.ID).MinHeight(d.ControlHeight()).Gap(d.Unit() * 2).AlignItems(ui.Center).Role(ui.RoleListItem)
|
||||
row := ui.Row(c.Key(s.ID)).MinHeight(d.ControlHeight()).Gap(d.Unit() * 2).AlignItems(ui.Center).Role(ui.RoleListItem)
|
||||
if id, ok := ui.Drop[subtaskDrag](row); ok {
|
||||
if from := slices.IndexFunc(*subs, func(o Subtask) bool { return o.ID == string(id) }); from >= 0 {
|
||||
move(from, i, "drag")
|
||||
|
||||
@@ -29,7 +29,7 @@ type TaskDetailPanelOptions struct {
|
||||
|
||||
// TaskDetailResult exposes the panel, edits and close events.
|
||||
type TaskDetailResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
changed bool
|
||||
closed bool
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ func TaskItem(c *ui.Context, task *Task, opts TaskItemOptions) ProjectResult {
|
||||
if edit.on && !row.IsDisabled() {
|
||||
in := input.TextInput(c, &edit.draft, input.TextInputOptions{Label: core.Msg(c, "task_item.rename")})
|
||||
in.Element.Grow(1)
|
||||
in.Input.AutoFocus()
|
||||
core.AutoFocus(c, in.Input)
|
||||
switch {
|
||||
case in.Submitted() && strings.TrimSpace(edit.draft) != "":
|
||||
if t := strings.TrimSpace(edit.draft); t != task.Title {
|
||||
|
||||
@@ -30,7 +30,7 @@ type WorkloadViewOptions struct {
|
||||
}
|
||||
|
||||
// WorkloadView virtualizes member loads with capacity and overload marks.
|
||||
func WorkloadView(c *ui.Context, loads []Workload, opts WorkloadViewOptions) *ui.Element {
|
||||
func WorkloadView(c *ui.Context, loads []Workload, opts WorkloadViewOptions) ui.Element {
|
||||
k := core.Tokens(c)
|
||||
d := core.Density(c)
|
||||
unit := opts.Unit
|
||||
|
||||
Reference in new issue
Block a user