-
Notifications
You must be signed in to change notification settings - Fork 8
Debugging
A few widget mistakes are silent by construction because they produce no error.
- Two widgets sharing an
ID.IDis the identity key for focus, scroll offsets, and per-widget state, so the two collapse onto one identity. - A focusable widget with no
ID. It renders and it clicks, but focus traversal is keyed byID, so it never joins the tab order. - A scrollable widget with no
ID. Every ID-less scrollable in a window shares the key"", so they scroll in lockstep. - An
OnMouseLeaveon a widget with noID. Leave tracking is keyed byID, so the callback never fires. This one survivesFocusDisabled: true— opting out of focus does not opt out of needing an identity.
gui.Debug(true) — or GOGUI_DEBUG=1 in the environment — audits every frame
for these and more, and writes findings to stderr, once per finding per window.
gui.DebugCategories enables each class independently:
gui.Debug(true)
// gui: focusable shape at 0/2/1 has no ID; focus traversal is keyed by
// ID, so it renders and clicks but never joins the tab orderLeave it off in production: the checks walk the whole layout tree each frame and allocate while doing it.
For the mistakes that are visible in the source, requiredid reports them at
build time instead, naming the Cfg type:
go run github.com/go-gui-org/go-gui/tools/requiredid/cmd/requiredid ./...go vet -vettool= and a golangci-lint custom plugin work equally well. The tool
is offered, not required — it is an internal tool whose rules may tighten
between releases, so nothing breaks if you never run it. Without it, a widget
that needs an ID and has none panics on its first render rather than failing
your build.
Every input control panics when constructed without a non-empty Cfg.ID:
Button, Input, InputDate, NumericInput, RadioButtonGroup, Radio,
Select, Switch, Toggle, plus the stateful widgets that already did
(ColorPicker, Combobox, DatePicker, ListBox, Slider, Tree, Table,
Form, Menu, Menubar, ContextMenu, CommandPalette, ProgressBar,
DataGrid).
The ID is what focus traversal, per-widget input state, scroll offsets, and
OnMouseLeave dispatch are all keyed by, so a control without one is not merely
anonymous — it is unreachable by keyboard and shares state with every other
ID-less control. IDs must be unique within a window.
A decorative control that should never take focus opts out instead of inventing an ID:
gui.Button(gui.ButtonCfg{FocusDisabled: true, Disabled: true})FocusDisabled: true satisfies the requirement for the widgets above. It does
not exempt a widget whose ID is tagged gui:"required" without the focus
option — those key state by ID regardless of focus.
Beyond duplicates and missing IDs, the sweep reports:
- a scrollable container or
OnMouseLeaveshape without anID - a virtualized listbox that resolved to height 0
- a container setting both
WrapandOverflow(wrap wins) - a fill gradient with more stops than the GPU shader limit (silently resampled)
- a window-level feature the platform refused (no ARGB visual, no compositor,
refused opacity) —
DebugWindowDegraded - per-widget state stored under a bare leaf instead of the effective ID —
DebugUnresolvedKeys - a focus ID naming no widget in the frame —
DebugUnknownFocus - a shape whose stamp disagrees with its scope, or an ID-bearing shape with no
stamp (a hand-built
Layoutspliced into a generated tree) —DebugStampDrift - a callback that acted without
ctx.Consume()while an ancestor also handles, and a link activation that opened nothing
DebugUnscopedIDs is separate and opt-in (not part of DebugAll): an ID with
no ID-bearing ancestor, which cannot move into a second scope safely.
Two methods return the audit findings as data, for tests and CI:
-
TestDuplicateIDs()— renders the window and returns duplicate-ID and missing-ID findings as strings. The assertable form of the stderr audit. -
TestUnconsumedEvents()— dispatches one synthetic event per consume-class callback and returns the findings. -
TestFindings(mask)— returns findings for any category mask, including opt-in ones.
See Testing your app for how NewTestWindow drives these.
Getting Started
Widgets
Layout & Interaction
Development
Visuals
Reference