A system-tray / menu-bar icon with menus, submenus, checkboxes and separators.
A tray is OS-integration, not a pixel-blitted widget, so it lives outside the
pure-blitting toolkit and drives the native APIs through a small Backend
interface — all CGO_ENABLED=0:
| platform | native API | mechanism |
|---|---|---|
| darwin | NSStatusItem + NSMenu/NSMenuItem |
purego + the Objective-C runtime |
| windows | Shell_NotifyIcon + TrackPopupMenu |
golang.org/x/sys/windows syscalls |
| linux | StatusNotifierItem + com.canonical.dbusmenu |
pure-Go DBus |
menu := tray.NewMenu().Add(
tray.Item("Open", func() { open() }),
tray.Checkbox("Notifications", true, func(on bool) { setNotify(on) }),
tray.SubMenu("Recent", tray.NewMenu().Add(tray.Item("file.txt", nil))),
tray.Separator(),
tray.Item("Quit", func() { t.Quit() }),
)
t := tray.New(iconPNG).SetTooltip("My App").SetMenu(menu)
t.OnReady(func() { /* live */ })
t.Run() // blocks on the platform event loop until Quit- Core (
Tray,Menu,MenuItem, item activation/toggle,Backendinterface, headless backend) — done, 100% covered, builds on every arch. - Native backends are opt-in via the
tray_nativebuild tag, so the core keeps its 100% coverage gate while the native code is compile-verified per-OS in CI. A tray can only be runtime-verified on a live desktop session.- darwin — implemented:
NSStatusItem+NSMenuvia ebitengine/purego, CGO=0. Compile-verified; runtime confirmation on a real macOS session is pending. Build/run your app with-tags tray_native. - windows / linux — next increments (currently
nilunder the tag →ErrNoBackend).
- darwin — implemented:
Without the tag (or a native backend for the OS), Run returns ErrNoBackend;
supply one — including the headless backend — via WithBackend.
go run -tags tray_native ./cmd/yourapp # link the macOS NSStatusItem backendBSD-3-Clause. Copyright the go-widgets authors.