Documentation
¶
Overview ¶
Package selector renders an "exactly one of N" choice bound directly to a Go enum value, filling the gap left by the egui2 bindings: egui's own enum-native helpers — `Ui::radio_value(&mut current, value, text)` and `Ui::selectable_value(...)`, whose whole point is to compare-and-assign an enum in one call — are not exposed in the IDL. Only the raw *bool-shaped primitives are (`RadioButton::new(checked)`, `Button::selectable(checked)`, `Button.Selected(bool)`), so every mutually-exclusive-choice site otherwise hand-rolls the same derive-checked / catch-click / assign loop.
This package is that loop, written once. Like badge it is pure Go composition over existing FFFI2 primitives — no IDL or Rust changes — so the wire format is identical to the hand-written button/radio it replaces.
Entry points:
- RadioValue — one control bound to one value; call it standalone or in your own loop. The direct analogue of egui's `radio_value`.
- Segmented — a whole option bar over one *T, addressed through a c.WidgetIdStack + scope key (the fsmview / kanban convention).
- SegmentedAbs — the same bar for widgets that address children by absolute id (a `scope string`), with no id stack to thread.
All replicate egui's exact change-rule: a primary click assigns `*current = value` and reports changed only when the value actually moved (re-clicking the already-selected option is not a change). The bridge to c.ResponseFlagsE.HasPrimaryClicked rather than a change flag is deliberate: egui's RadioButton / selectable never call `mark_changed`, so `HasChanged` would never fire — this is the apply-side gate of ADR-0013.
The Style knob picks the visual skin; all three are the same databinding underneath, so a radio group and a segmented bar differ only in looks:
selector.Segmented(ids, "granularity", &opts.granularity).
Option(rowPerDBRow, "per DB row").
Option(rowPerAttr, "per attribute").
SendResp()
selector.RadioValue(ids.PrepareStr("theme"), &cfg.theme, themeDark).
Icon(icons.IconColorMode).Text("Dark").SendResp()
Index ¶
- Variables
- type GroupFluid
- func (inst GroupFluid[T]) Frameless() GroupFluid[T]
- func (inst GroupFluid[T]) Gap(px float32) GroupFluid[T]
- func (inst GroupFluid[T]) Inline() GroupFluid[T]
- func (inst GroupFluid[T]) Option(value T, label string) GroupFluid[T]
- func (inst GroupFluid[T]) OptionIcon(value T, icon, label, tooltip string) GroupFluid[T]
- func (inst GroupFluid[T]) Send()
- func (inst GroupFluid[T]) SendResp() (changed bool)
- func (inst GroupFluid[T]) Style(s Style) GroupFluid[T]
- func (inst GroupFluid[T]) Vertical() GroupFluid[T]
- type Style
- type ValueFluid
- func (inst ValueFluid[T]) Icon(glyph string) ValueFluid[T]
- func (inst ValueFluid[T]) Send()
- func (inst ValueFluid[T]) SendResp() (changed bool)
- func (inst ValueFluid[T]) Style(s Style) ValueFluid[T]
- func (inst ValueFluid[T]) Text(label string) ValueFluid[T]
- func (inst ValueFluid[T]) Tooltip(text string) ValueFluid[T]
Constants ¶
This section is empty.
Variables ¶
var PackageProps = packageprops.Props{ WASMWASI: packageprops.WASMCompiles, WASMJS: packageprops.WASMCompiles, WASMFreestanding: packageprops.WASMCompiles, }
PackageProps records this package's curated properties (ADR-0080). Seeded by `boxer code analysis golang wasmsurvey props generate`; curate by hand. The same group's `props verify` reconciles it. Mirrors badge: pure Go composition over the egui2 bindings, same import surface — `boxer code analysis golang wasmsurvey props verify` is the authority if that ever diverges.
Functions ¶
This section is empty.
Types ¶
type GroupFluid ¶
type GroupFluid[T comparable] struct { // contains filtered or unexported fields }
GroupFluid is the chained builder for an option bar. Zero value is not valid; always start from Segmented or SegmentedAbs.
func Segmented ¶
func Segmented[T comparable](ids *c.WidgetIdStack, scopeKey string, current *T) GroupFluid[T]
Segmented builds an exclusive option bar bound to `current`. Unlike RadioValue it takes the concrete `*c.WidgetIdStack` plus a `scopeKey` (the fsmview / kanban multi-child convention): the bar renders N children, so it needs to prepare the stack once per option, and the scope namespaces those ids so two bars on the same stack cannot collide. Defaults to StyleSegmented; add GroupFluid.Vertical + GroupFluid.Style with StyleRadio for a settings-style radio list.
func SegmentedAbs ¶
func SegmentedAbs[T comparable](scope string, current *T) GroupFluid[T]
SegmentedAbs is Segmented for widgets that address their children by absolute id (a `scope string` fed to c.MakeAbsoluteIdStr) instead of threading a c.WidgetIdStack — the distsummary / canonicaltypesummary "widget-in-a-box" convention. Each option's id is derived as MakeAbsoluteIdStr(scope + "#" + i), so pass a `scope` unique to this bar (e.g. the widget scope + "-tab"). No id scope is opened — absolute ids are already globally unique.
func (GroupFluid[T]) Frameless ¶
func (inst GroupFluid[T]) Frameless() GroupFluid[T]
Frameless drops the button chrome on StyleSegmented (renders each segment with Frame(false)); it is a no-op for StyleSelectable (already frameless) and StyleRadio.
It also drops the SELECTED STATE, because StyleSegmented shows selection by filling the segment and the fill is the frame — a frameless segmented bar draws its checked and unchecked options identically. That is tolerable inside a ComboBox popup, where the popup says what is selected, and wrong for a standalone options bar.
Reach for StyleSelectable instead: it is frameless too, and highlights the selected option. Four `play` panels reached here first and rendered unreadable bars until a pair of screenshots differing only in one toggle came back pixel-identical.
func (GroupFluid[T]) Gap ¶
func (inst GroupFluid[T]) Gap(px float32) GroupFluid[T]
Gap inserts px logical points of space between adjacent options (never before the first or after the last) — matches the AddSpace(GapInline(density)) that hand-rolled tab bars put between segments. Zero (the default) packs them flush.
func (GroupFluid[T]) Inline ¶
func (inst GroupFluid[T]) Inline() GroupFluid[T]
Inline emits the options straight into the caller's existing layout instead of opening the bar's own HorizontalTop. Use it when the bar shares a row with other widgets (a label, a gap, sibling checkboxes) and the nested-horizontal vertical offset would misalign them. The id scope is still opened, so two inline bars on one stack stay collision-free. When set, GroupFluid.Vertical is ignored — orientation is then the caller's layout.
func (GroupFluid[T]) Option ¶
func (inst GroupFluid[T]) Option(value T, label string) GroupFluid[T]
Option appends a choice. Order is render order.
func (GroupFluid[T]) OptionIcon ¶
func (inst GroupFluid[T]) OptionIcon(value T, icon, label, tooltip string) GroupFluid[T]
OptionIcon appends a choice with a leading glyph and an optional hover tooltip (pass "" for none).
func (GroupFluid[T]) Send ¶
func (inst GroupFluid[T]) Send()
Send lays out the bar and discards the change edge.
func (GroupFluid[T]) SendResp ¶
func (inst GroupFluid[T]) SendResp() (changed bool)
SendResp lays out the bar and returns true on the frame a click moves the selection. Stack-form bars render under one IdScope keyed by scopeKey; absolute-id bars need no scope.
func (GroupFluid[T]) Style ¶
func (inst GroupFluid[T]) Style(s Style) GroupFluid[T]
Style overrides the visual skin (default StyleSegmented).
func (GroupFluid[T]) Vertical ¶
func (inst GroupFluid[T]) Vertical() GroupFluid[T]
Vertical stacks the options top-to-bottom instead of left-to-right — the settings-style layout, usually paired with StyleRadio.
type Style ¶
type Style uint8
Style selects the visual skin. The three are interchangeable at the call site because they share one *bool databinding — the choice is purely visual.
const ( // StyleRadio renders egui's classic ○/● RadioButton dot. Default for // [RadioValue]; the natural fit for a vertical settings-style group. StyleRadio Style = iota // StyleSegmented renders a framed Button whose Selected state fills it — // the compact "segmented control" look. Default for [Segmented]; fits a // horizontal options bar better than a row of radio dots. StyleSegmented // StyleSelectable renders a frameless highlighted label // (egui `Button::selectable`) — the filter-chip / tag-picker look. StyleSelectable )
type ValueFluid ¶
type ValueFluid[T comparable] struct { // contains filtered or unexported fields }
ValueFluid is the chained builder for one selectable control. Zero value is not valid; always start from RadioValue.
func RadioValue ¶
func RadioValue[T comparable](id c.WidgetIdCreatorI, current *T, value T) ValueFluid[T]
RadioValue binds one control to one enum value. It takes any c.WidgetIdCreatorI — like badge — because it is a single widget: `ids.PrepareStr("x")`, `ids.PrepareSeq(i)` inside a loop, or an absolute id. The control shows selected when `*current == value` and, on a primary click, assigns `*current = value`. Defaults to StyleRadio.
func (ValueFluid[T]) Icon ¶
func (inst ValueFluid[T]) Icon(glyph string) ValueFluid[T]
Icon prefixes the label with a glyph (typically an `icons.IconXxx` rune), joined by a non-breaking space so it never wraps off the control.
func (ValueFluid[T]) Send ¶
func (inst ValueFluid[T]) Send()
Send renders the control and discards the change edge — for callers that read `*current` next frame rather than reacting to the edge.
func (ValueFluid[T]) SendResp ¶
func (inst ValueFluid[T]) SendResp() (changed bool)
SendResp renders the control and returns true only on the frame a click moves the selection to this value (re-clicking the current value is not a change), so callers can gate a requery or other side effect on the edge.
func (ValueFluid[T]) Style ¶
func (inst ValueFluid[T]) Style(s Style) ValueFluid[T]
Style overrides the visual skin (default StyleRadio).
func (ValueFluid[T]) Text ¶
func (inst ValueFluid[T]) Text(label string) ValueFluid[T]
Text sets the label shown beside the control.
func (ValueFluid[T]) Tooltip ¶
func (inst ValueFluid[T]) Tooltip(text string) ValueFluid[T]
Tooltip shows the given string on hover. Empty is a no-op.