Files
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

74 lines
2.0 KiB
Go

package chart
import (
"fmt"
"strings"
"github.com/egoist/mygo/ui"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
)
type BarChartOptions struct {
// Width and Height are dots; zeros mean 160 by 80.
Width, Height int
// Slot fills the bars; zero means Accent.
Slot pixel.Slot
}
// BarChart stands bars on micro-font axes; hover reads a value.
func BarChart(c *ui.Context, text string, labels []string, values []float64, o BarChartOptions) ui.Element {
k := pixel.K(c)
if len(labels) != len(values) || len(values) == 0 {
panic(fmt.Sprintf("chart: BarChart has %d labels for %d values", len(labels), len(values)))
}
w, h := size("BarChart", o.Width, o.Height, 160, 80)
lo, hi := span(values)
p := fit(min(0, lo), max(0, hi), w, h, labels)
e := ui.Box(c).Size(k.D(w), k.D(h)).Role(ui.RoleImage).Label(text).Description(read(labels, values))
hover := -1
if x, y, over := e.PointerPosition(); over {
if dx := int(x/k.Dot()) - p.x; dx >= 0 && dx < p.w && int(y/k.Dot()) < p.y+p.h {
hover = dx * len(values) / p.w
}
}
ink := pick(o.Slot, 0)
return e.Draw(func(pt *ui.Painter, r ui.Rect) {
g := k.Canvas(pt, r)
p.grid(g, labels)
share := p.w / len(values)
bw, zero := max(1, share*2/3), p.at(0)
for i, v := range values {
x, top := p.slot(i, len(values))-bw/2, p.at(v)
g.Rect(x, min(top, zero), bw, max(1, abs(zero-top)), ink)
}
if hover >= 0 {
readout(g, p.slot(hover, len(values)), p.at(values[hover]), num(values[hover]), w)
}
})
}
// readout boxes s above x, y, kept inside w dots.
func readout(g pixel.Canvas, x, y int, s string, w int) {
bw := tinyW(s) + 4
bx, by := max(0, min(w-bw, x-bw/2)), max(0, y-11)
g.Frame(bx, by, bw, 9, pixel.Flat, pixel.Lit)
tiny(g, bx+2, by+2, s, pixel.Ink)
}
// read lists values for assistive technology: "A 3, B 5".
func read(labels []string, values []float64) string {
parts := make([]string, len(values))
for i, v := range values {
parts[i] = labels[i] + " " + num(v)
}
return strings.Join(parts, ", ")
}
func abs(n int) int {
if n < 0 {
return -n
}
return n
}