selector

package
v0.0.21 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 21, 2026 License: MIT Imports: 3 Imported by: 0

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

Constants

This section is empty.

Variables

View Source
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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL