Files
8bitMyGoUI/datetime/monthpicker.go
T
ZacharyZhang-NYandClaude Opus 5.5 03e1f91706 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>
2026-10-17 01:31:08 -04:00

89 lines
2.6 KiB
Go

package datetime
import (
"log/slog"
"strconv"
"time"
"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 MonthPickerOptions struct {
Disabled bool
}
type MonthPickerResult struct {
Element ui.Element
Changed bool
}
type monthKey struct{}
// paging is the year or decade shown, and a cell.
type paging struct {
at, cur int
}
// MonthPicker lays out twelve months; ◀ ▶ turn years.
func MonthPicker(c *ui.Context, text string, month *time.Time, o MonthPickerOptions) MonthPickerResult {
k := pixel.K(c)
col := ui.Column(c).Gap(k.D(2))
st := ui.Local(col, monthKey{}, func() paging { return paging{at: month.Year(), cur: int(month.Month()) - 1} })
res := MonthPickerResult{Element: col}
// Cells fit the widest name in the frame's locale.
cw := 28
for m := range 12 {
cw = max(cw, pixel.Measure(monthName(k, time.Month(m+1)))+6)
}
col.Children(func() {
pager(c, strconv.Itoa(st.at), 4*cw, o.Disabled, func(d int) {
st.at += d
slog.Debug("8bit: month picker turned", "label", text, "year", st.at)
})
shell, _, ps, picked := pixel.Pad(c, text, 4, 12, cw, cell, &st.cur, o.Disabled)
if picked >= 0 {
*month = time.Date(st.at, time.Month(picked+1), 1, 0, 0, 0, 0, month.Location())
res.Changed = true
}
y, m := month.Year(), int(month.Month())-1
at, cur := st.at, st.cur
shell.Draw(func(p *ui.Painter, r ui.Rect) {
g := k.Canvas(p, r)
for i := range 12 {
x, yy := i%4*cw, i/4*cell
if ps.Focus && i == cur {
g.Cursor(x, yy, cw, cell)
}
if at == y && i == m {
g.Frame(x+1, yy, cw-2, cell, pixel.Sunken, pixel.Well)
}
name := monthName(k, time.Month(i+1))
g.Text(x+(cw-pixel.Measure(name))/2, yy, name, pixel.Ink, 1)
}
g.Finish(ps)
})
})
if res.Changed {
slog.Debug("8bit: month picker", "label", text, "month", month.Format("2006-01"))
}
return res
}
// pager heads a grid: ◀ title ▶ call turn.
func pager(c *ui.Context, title string, w int, disabled bool, turn func(d int)) {
k := pixel.K(c)
ui.Row(c).Width(k.D(w)).AlignItems(ui.Center).Children(func() {
if input.IconButton(c, icon.ChevronLeft, k.Msg("prev"), input.IconButtonOptions{Size: input.Small, Disabled: disabled}).Clicked {
turn(-1)
}
ui.Row(c).Grow(1).Justify(ui.Center).Children(func() { pixel.Text(c, title, pixel.TextOptions{}) })
if input.IconButton(c, icon.ChevronRight, k.Msg("next"), input.IconButtonOptions{Size: input.Small, Disabled: disabled}).Clicked {
turn(1)
}
})
}