Files
8bitMyGoUI/datetime/sundial.go
T
ZacharyZhang-NY a2f2796455 Add datetime components 196-215; Kit.At; inert stepper arrows
Calendars, date and time inputs, pickers, clocks, timers, a sun dial,
a season wheel, an agenda and a day schedule. Kit.At rebuilds at a
moment (relative times, midnight). Stepper and NumberInput arrows stop
under Inert. DateInput parks a popup focus as its own.
2026-10-11 23:56:10 -04:00

77 lines
2.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package datetime
import (
"fmt"
"log/slog"
"math"
"github.com/egoist/mygo/ui"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/icon"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
)
type SunDialOptions struct {
Disabled bool
}
type SunDialResult struct {
Element *ui.Element
Changed bool
}
// Dial size in dots: the arc's middle, radius and box.
const dialW, dialH, dialR = 96, 56, 40
// SunDial arcs the sun or moon; a drag sets *hour.
func SunDial(c *ui.Context, text string, hour *int, o SunDialOptions) SunDialResult {
k := pixel.K(c)
if *hour < 0 || *hour > 23 {
panic(fmt.Sprintf("datetime: SunDial hour %d outside 0–23", *hour))
}
old := *hour
shell, base, st := pixel.Shell(c, func() *ui.Element {
return ui.Box(c).Focusable().Role(ui.RoleSlider).Label(text).Disabled(o.Disabled)
})
if base.Shortcut(0, ui.KeyLeft) {
*hour = (*hour + 23) % 24
}
if base.Shortcut(0, ui.KeyRight) {
*hour = (*hour + 1) % 24
}
// The width spans the whole day, midnight to midnight.
if _, _, held := base.Dragged(); held && !st.Disabled && !pixel.Current(c).Inert {
x, _, _ := base.PointerPosition()
*hour = max(0, min(23, int(x/k.D(dialW)*24)))
}
h := *hour
label := fmt.Sprintf("%02d:00", h)
base.Description(label)
day := h >= 6 && h < 18
shell.Size(k.D(dialW), k.D(dialH)).Draw(func(p *ui.Painter, r ui.Rect) {
g := k.Canvas(p, r)
sky, body := pixel.Lit, icon.Sun
if !day {
sky, body = pixel.Shade, icon.Moon
}
g.Frame(0, 0, g.W, g.H, pixel.Sunken, sky)
cx, cy := dialW/2, dialH-6
g.HLine(2, cy, dialW-4, pixel.Edge)
for i := 0; i <= 48; i += 2 {
a := math.Pi * float64(i) / 48
g.Dot(cx+int(math.Round(dialR*math.Cos(a))), cy-int(math.Round(dialR*math.Sin(a))), pixel.Faint)
}
t := float64((h+18)%12) / 12
a := math.Pi * (1 - t)
x, y := cx+int(math.Round(dialR*math.Cos(a))), cy-int(math.Round(dialR*math.Sin(a)))
g.Sprite(x-body.W/2, y-body.H/2, body, 0, pixel.Ink, 1)
g.Micro(3, 3, label, pixel.Ink, 1)
g.Finish(st)
})
res := SunDialResult{Element: shell, Changed: *hour != old}
if res.Changed {
slog.Debug("8bit: sundial", "label", text, "hour", *hour)
}
return res
}