Documentation
¶
Overview ¶
Package application owns a go-widgets app's whole lifecycle: it opens the native OS window, runs its event loop, blits the handler's own RGBA framebuffer into it, composes a system-tray / menu-bar icon alongside it, and fires a "ready" callback once the first frame is on screen. It is the sibling of go-widgets/window: window presents a single surface, application is the layer above it that turns a handler plus an identity (name, icon, tray) into a running application. There is no WebKit, no wasm, no HTTP — every widget is drawn by the framework (toolkit.Surface) and the window is just a bitmap surface plus a native event source. Everything is CGO_ENABLED=0.
Opening a real OS window and pumping its event loop is inherently a launch-verified boundary, so the Run path through go-widgets/window is excluded from the coverage gate (like the wasm glue elsewhere); this contract, the event translation, the present gating, the appearance pump, the launch-height seam and the ready-after-first-frame counter are all unit-tested.
The optional capability interfaces (AppearanceSink, ShortcutSink, SecondaryClicker, ContextMenuHost, ClipboardController, Accessible) are the reason the run loop must never WRAP a Handler: a wrapper would satisfy the bare Handler interface and silently drop every capability the concrete handler also implements. Everything here threads the ORIGINAL handler through, and type-asserts for a capability at the point of use.
Index ¶
- Variables
- func Bind(h Handler, scale float64) *toolkit.Surface
- func BindScaled(h Handler, scaleOf func() float64) *toolkit.Surface
- func Run(s Spec, cfg Config, h Handler, onReady func()) error
- type A11yElement
- type Accessible
- type AppearanceSink
- type ClipboardController
- type Config
- type ContextMenuHost
- type Handler
- type NativeControlProvider
- type SecondaryClicker
- type ShortcutSink
- type Spec
- type SystemAppearance
- type SystemClipboard
Constants ¶
This section is empty.
Variables ¶
var ErrUnsupported = errors.New("application: no native window back-end on this platform")
ErrUnsupported is returned by Run on a platform without a native back-end.
Functions ¶
func Bind ¶
Bind returns a toolkit.Surface showing h: the frame it presents, the input it takes, and the tree it publishes for a screen reader.
Run uses it to fill a native window, but it is exported because a window is not the only place this can go. Any go-widgets host that can lay out a widget can host this application — a desktop shell, a tab in something larger, wasmdesk — and none of them should have to reimplement the translation.
scale is the framebuffer pixels per logical point the host is rendering at; pass 1 if it does not scale. A host whose scale CHANGES while running — a window dragged between displays of different density — should use Run, which re-reads it every frame.
It is also what makes the wiring testable. Everything specific to the app lives here — the resize units, the event translation, the element mapping — and none of it needs a window to be wrong, so a test can drive a real app scene through a real surface and assert the scene moved.
func BindScaled ¶
BindScaled is Bind for a host whose scale CHANGES while it runs: a window dragged between displays of different density. The function is called every frame, and the handler is told about a change the moment it happens.
func Run ¶
Run opens the application's window and runs it to completion, putting up a menu-bar tray (when Spec.Tray is set) attached to the window's run loop, and invoking onReady once after the first frame is shown. It mirrors window.Run but owns the whole app lifecycle. (The .app bundle + Dock icon are a build-time concern handled by the packager, not here.)
The tray is ATTACHED, not Run: attaching joins the platform's already-running main loop (the one win.Run drives for the window) rather than starting a second one, which is the only way a window and a tray can coexist in one process. tray.Attach must be called on the platform's main/UI thread and, per its contract, may block until the tray is torn down, so it is started on its own goroutine and stopped with Quit when the window returns — whichever way the window loop ends (a clean quit, or an error), the deferred Quit tears the tray down so no menu-bar icon is orphaned.
A backend that cannot attach a tray (a headless build, or a platform without native tray support) makes tray.Attach return an error, which is deliberately swallowed: a missing tray must not stop the window from running. The window is the application; the tray is a convenience on top of it.
Types ¶
type A11yElement ¶
A11yElement is one element the handler wants the platform's accessibility layer to expose: what it is, what it says, and where it sits.
Rect is in DEVICE PIXELS with a top-left origin — the same space Frame's buffer and the MouseDown coordinates use — because that is the only space this package and its handler already agree on. Each back-end converts it to whatever its own accessibility API wants (macOS: screen points, y-up).
Role is a neutral name, not a platform constant: this package presents pixels and must not drag a platform's vocabulary into the handler. The back-end maps it (see axRole).
type Accessible ¶
type Accessible interface {
// A11yElements returns the current elements in reading order. It is called
// from the platform's accessibility client, which on macOS is the main
// thread, at unpredictable times — the implementation must be safe to call
// between frames.
A11yElements() []A11yElement
}
Accessible is implemented by a Handler that can describe what it is showing. A back-end that supports an accessibility API asks for the description when the platform requests it; a handler that does not implement this simply presents pixels, as before.
It is a separate, optional interface rather than a Handler method so that adding it breaks no existing back-end or handler.
type AppearanceSink ¶
type AppearanceSink interface {
SystemAppearance(SystemAppearance)
}
AppearanceSink is an optional Handler capability. A back-end that can read the host appearance (currently the macOS Cocoa back-end) pushes it so the UI adopts the native dark/light mode, accent colour, and system font.
type ClipboardController ¶
type ClipboardController interface {
SetSystemClipboard(SystemClipboard)
}
ClipboardController is an optional Handler capability. A back-end that can reach the platform pasteboard installs its SystemClipboard here at startup (the mirror of AppearanceSink: this one flows a capability OUT to the handler). A back-end without clipboard support installs nothing, leaving the toolkit's default in-process clipboard in place (copy/paste still work within the app, just not across the OS).
type ContextMenuHost ¶
ContextMenuHost is an optional Handler capability: while it reports a context menu open, the window feeds every input event to ContextMenuEvent instead of the usual MouseDown/Move/Scroll/Key path, so the menu is modal. The handler closes the menu itself (an item fired, a click landed outside, Escape) and then reports it inactive again.
type Handler ¶
type Handler interface {
// Frame returns the current RGBA framebuffer (w*h*4 bytes) and whether it
// changed since the last call (damage gate).
Frame() (buf []byte, w, h int, changed bool)
// Resize maps the new logical size to device pixels; scale is the backing
// scale factor (device pixels per point).
Resize(w, h int, scale float64)
// MouseDown reports a left button press at device-pixel coordinates.
MouseDown(x, y int)
// MouseMove reports pointer motion at device-pixel coordinates. It fires
// continuously during a left-button drag (back-ends need not emit idle
// hovers) so the handler can drive interactions like a divider resize.
MouseMove(x, y int)
// MouseUp reports a left button release at device-pixel coordinates.
MouseUp(x, y int)
// Scroll reports a wheel delta in device pixels.
Scroll(dy int)
// Key reports a key press: name is a symbolic label for editing keys
// ("Backspace"/"Escape"/"Enter"), r the rune for a printable character.
Key(name string, r rune)
}
Handler is the presenter's data source and input sink. The window calls Frame each tick (and after each event) and blits the returned buffer only when it reports changed. Input coordinates are device pixels (points × backing scale) with a top-left origin, matching the framebuffer.
type NativeControlProvider ¶ added in v0.2.0
type NativeControlProvider interface {
// NativeControls returns the native controls to embed this frame, keyed for
// identity across frames, in the framebuffer's device-pixel, top-left space
// (like A11yElements and MouseDown). It is called once per frame from the
// back-end's main thread; a control the app is not showing this frame is
// simply omitted.
NativeControls() []toolkit.NativeControl
}
NativeControlProvider is implemented by a Handler that wants some of what it draws backed by real OS controls — a secure text field, a button, a slider — embedded over its framebuffer. A back-end that can host native controls asks for the descriptors each frame and reconciles the live controls to them; a handler that does not implement this simply presents pixels, as before.
It is a separate, optional interface, the exact parallel to Accessible: where Accessible describes the surface for the platform's accessibility layer, this describes the parts of it that should be real controls. The descriptor is the toolkit's own toolkit.NativeControl — kind, key, geometry, value, and the callbacks that carry the person's input back — because the back-end's seam (toolkit.Surface.Controls) speaks exactly that.
type SecondaryClicker ¶
type SecondaryClicker interface {
SecondaryClick(x, y int)
}
SecondaryClicker is an optional Handler capability: a secondary (right / two-finger / Control-click) press arrives here as the context-menu gesture, with the same device-pixel coordinates as a MouseDown. A handler that has no context menu simply does not implement it, and the press is ignored.
type ShortcutSink ¶
ShortcutSink is an optional Handler capability: a key pressed with a command-style modifier (Ctrl/Cmd) arrives here as a shortcut, r is the base rune (modifiers stripped), ctrl/meta report which modifier was held. Back-ends route a modifier chord here instead of through Key (which drops the modified rune) so the app can act on real-browser-style shortcuts like Cmd+=.
type Spec ¶
type Spec struct {
Name string // e.g. "News Reader" — tray tooltip
Identifier string // reverse-DNS, e.g. "com.example.app"
Version string // e.g. "1.2.0"
Icon []byte // PNG icon bytes for the tray (optional)
Tray func() *tray.Menu // optional: builds the menu-bar menu
}
Spec is the application's identity and its optional tray. It is the piece that separates an application from a bare window: a name and identifier the OS and the tray tooltip can show, a version, and — when the app wants a menu-bar / system-tray presence — an icon and a builder for its menu.
type SystemAppearance ¶
type SystemAppearance struct {
// Dark is the effective dark/light mode (macOS effectiveAppearance).
Dark bool
// Accent is the user's accent colour (macOS controlAccentColor); only
// meaningful when HasAccent is set.
Accent color.RGBA
HasAccent bool
// FontTTF is the raw system font (e.g. macOS SFNS.ttf). Empty on a poll that
// only refreshes colours, so the already-installed font is kept.
FontTTF []byte
}
SystemAppearance carries look-and-feel harvested from the host UI so the renderer can match the live system look rather than a fixed palette.
type SystemClipboard ¶
SystemClipboard is a host OS text clipboard (read + write). Its method set is deliberately identical to the toolkit's back-end-neutral Clipboard interface, so the app can install a back-end's SystemClipboard as the toolkit-wide clipboard directly — making every text widget's copy/cut/paste, and the app's copy actions, go through the real OS pasteboard.