Documentation
¶
Overview ¶
Package toolkit provides a pure-Go widget set for wasmdesk native apps. Widgets render per-pixel into an RGBA byte buffer (the SAB backed framebuffer wasmbox clients write to) and dispatch input events received from the wasmbox compositor.
Design notes:
- Every widget exposes the same three-method interface, so a container (HBox, VBox, ScrollView, ...) can hold any leaf.
- Drawing is allocation-free in the steady state: the widget writes into a caller-owned RGBA slice + reads its theme by reference. Per-frame work is bounded by the widget's bbox.
- Coordinates are integer pixels in the caller's surface space; the widget's Rect is its placement within that surface.
- Events are pre-translated into widget-local (X, Y) before dispatch by the parent container (HBox/VBox/ScrollView do the hit-testing + offset adjustment).
Index ¶
- Constants
- Variables
- func CardFooterH() int
- func CardHeaderH() int
- func ClipboardText() string
- func ColorPickerNaturalSize() (w, h int)
- func DatePickerFieldH() int
- func DaysInMonth(year, month int) int
- func DecodeIsoIconPayload(payload string) (id string, ok bool)
- func DeleteSelection(lines []string, sel Selection) []string
- func DiffLineH() int
- func DrawIconCopy(p painter.Painter, r Rect, ink RGBA)
- func DrawIconCut(p painter.Painter, r Rect, ink RGBA)
- func DrawIconNew(p painter.Painter, r Rect, ink RGBA)
- func DrawIconOpen(p painter.Painter, r Rect, ink RGBA)
- func DrawIconPaste(p painter.Painter, r Rect, ink RGBA)
- func DrawIconRedo(p painter.Painter, r Rect, ink RGBA)
- func DrawIconSave(p painter.Painter, r Rect, ink RGBA)
- func DrawIconSearch(p painter.Painter, r Rect, ink RGBA)
- func DrawIconSettings(p painter.Painter, r Rect, ink RGBA)
- func DrawIconUndo(p painter.Painter, r Rect, ink RGBA)
- func DrawText(p painter.Painter, x, y int, text string, ink RGBA)
- func EaseInCubic(t float64) float64
- func EaseInOutCubic(t float64) float64
- func EaseInOutQuad(t float64) float64
- func EaseInQuad(t float64) float64
- func EaseOutCubic(t float64) float64
- func EaseOutQuad(t float64) float64
- func EncodeIsoIconPayload(id string) string
- func ExpanderHeaderHeight() int
- func FolderTabsHeight() int
- func FormFieldLabelH() int
- func GlyphAdvance() int
- func GlyphHeight() int
- func JoinDropPayload(items []string) string
- func Linear(t float64) float64
- func LookupAs[T Widget](vc *ViewController, name string) (val T, ok bool)
- func MarshalIsoDocument(doc IsoDocument) ([]byte, error)
- func MetricScale() float64
- func MinHitTarget() int
- func RegisterAnimatedIcons(r *IsoIconRegistry)
- func RegisterIcon(id string, icon IsoIcon)
- func RegisterIconPack(p IsoIconPack)
- func RenderImage(w Widget, width, height int, theme *Theme) (*image.RGBA, error)
- func RenderPNG(w Widget, width, height int, theme *Theme) ([]byte, error)
- func Scaled(v int) int
- func SelectionText(lines []string, sel Selection) string
- func SetClipboard(c Clipboard)
- func SetClipboardText(s string)
- func SetDensity(d DensityLevel)
- func SetFont(f Font)
- func SetMetricScale(f float64)
- func SetTextDirection(d TextDirection)
- func SplitDropPayload(code string) []string
- func SyncSpreadsheet(ss *Spreadsheet, cs *CollabSheet) (detach func())
- func TextWidth(text string) int
- func TickTree(root Widget, dt float64)
- func TimelineEventH() int
- func TouchTarget(computed int) int
- func TreeAnimating(root Widget) bool
- func UseOpenTypeText() error
- func UseOpenTypeTextSize(sizePx int) error
- func Validate(value string, rules ...Rule) error
- func ViewSwitcherHeight() int
- func WeekdayOfFirst(year, month int) int
- type A11yInfo
- type A11yNode
- type Accelerator
- type Accessible
- type Accordion
- func (a *Accordion) A11y() A11yInfo
- func (a *Accordion) Children() []Widget
- func (a *Accordion) Draw(p painter.Painter, theme *Theme)
- func (a *Accordion) Expanded() *mvvm.Observable[int]
- func (f *Accordion) Focused() bool
- func (a *Accordion) OnEvent(ev Event)
- func (f *Accordion) SetFocused(focused bool)
- type AccordionSection
- type Action
- type ActionRegistry
- func (r *ActionRegistry) Action(id string) *Action
- func (r *ActionRegistry) Actions() []*Action
- func (r *ActionRegistry) Add(id, label string, run func()) *Action
- func (r *ActionRegistry) BindDefaults(keymap *Keymap, scope Scope) error
- func (r *ActionRegistry) Disable(id string) bool
- func (r *ActionRegistry) Enable(id string) bool
- func (r *ActionRegistry) Len() int
- func (r *ActionRegistry) Lookup(id string) (*Action, bool)
- func (r *ActionRegistry) PaletteCommands() []PaletteCommand
- func (r *ActionRegistry) Register(a *Action) *Action
- func (r *ActionRegistry) Run(id string) bool
- func (r *ActionRegistry) SetEnabled(id string, enabled bool) bool
- func (r *ActionRegistry) Unregister(id string) bool
- type ActionRow
- type ActionSheet
- func (a *ActionSheet) A11y() A11yInfo
- func (a *ActionSheet) AddAction(label string, fn func()) *Button
- func (a *ActionSheet) Animating() bool
- func (a *ActionSheet) Children() []Widget
- func (a *ActionSheet) Dismiss()
- func (a *ActionSheet) DragBegin(y int)
- func (a *ActionSheet) DragMove(y int, dt float64)
- func (a *ActionSheet) DragRelease()
- func (a *ActionSheet) Draw(p painter.Painter, theme *Theme)
- func (a *ActionSheet) HitTest(px, py int) bool
- func (a *ActionSheet) OnEvent(ev Event)
- func (a *ActionSheet) Open()
- func (a *ActionSheet) Presented() *mvvm.Observable[bool]
- func (a *ActionSheet) SetBounds(r Rect)
- func (a *ActionSheet) SetCancel(label string, fn func()) *Button
- func (a *ActionSheet) State() ActionSheetState
- func (a *ActionSheet) Tick(dt float64)
- func (a *ActionSheet) Visible() bool
- type ActionSheetState
- type AddressBar
- func (a *AddressBar) A11y() A11yInfo
- func (a *AddressBar) Blur()
- func (a *AddressBar) Bookmarked() *mvvm.Observable[bool]
- func (a *AddressBar) Copied() *mvvm.Observable[bool]
- func (a *AddressBar) CopySelectAll() (string, bool)
- func (a *AddressBar) Draw(p painter.Painter, theme *Theme)
- func (a *AddressBar) Editing() *mvvm.Observable[string]
- func (a *AddressBar) Focused() *mvvm.Observable[bool]
- func (a *AddressBar) OnEvent(ev Event)
- func (a *AddressBar) URL() *mvvm.Observable[string]
- func (a *AddressBar) Value() string
- type Agenda
- func (a *Agenda) A11y() A11yInfo
- func (a *Agenda) DayAt(x, y int) (year, month, day int, ok bool)
- func (a *Agenda) Draw(p painter.Painter, theme *Theme)
- func (a *Agenda) DrawEditor(p painter.Painter, theme *Theme)
- func (a *Agenda) EditEvent(i int)
- func (a *Agenda) Editing() int
- func (a *Agenda) EditorChar(code string)
- func (a *Agenda) EditorClick(x, y int) bool
- func (a *Agenda) EditorKey(code string)
- func (a *Agenda) OnEvent(ev Event)
- func (a *Agenda) Selected() *mvvm.Observable[int]
- func (a *Agenda) View() *mvvm.Observable[AgendaView]
- type AgendaCalendar
- type AgendaEvent
- type AgendaSidebar
- func (s *AgendaSidebar) A11y() A11yInfo
- func (s *AgendaSidebar) CancelEdit()
- func (s *AgendaSidebar) CommitEdit()
- func (s *AgendaSidebar) Draw(p painter.Painter, theme *Theme)
- func (s *AgendaSidebar) EditName(i int)
- func (s *AgendaSidebar) Editing() int
- func (s *AgendaSidebar) OnEvent(ev Event)
- func (s *AgendaSidebar) ScrollBy(delta int)
- type AgendaView
- type Alert
- type AlertKind
- type Align
- type Animator
- type AppDock
- type AppDockItem
- type AreaChart
- func (c *AreaChart) A11y() A11yInfo
- func (c *AreaChart) Draw(p painter.Painter, theme *Theme)
- func (c *AreaChart) Hover() *mvvm.Observable[bool]
- func (c *AreaChart) HoverIndex() *mvvm.Observable[int]
- func (c *AreaChart) OnEvent(ev Event)
- func (c *AreaChart) ValueAt(localX int) (index int, value float64, ok bool)
- type ArticleCard
- type Avatar
- type Backdrop
- type Badge
- type Banner
- type BarChart
- func (b *BarChart) A11y() A11yInfo
- func (c *BarChart) Draw(p painter.Painter, theme *Theme)
- func (c *BarChart) Hover() *mvvm.Observable[bool]
- func (c *BarChart) HoverIndex() *mvvm.Observable[int]
- func (c *BarChart) OnEvent(ev Event)
- func (c *BarChart) ValueAt(localX int) (index int, value float64, ok bool)
- type BarSegment
- type Base
- func (b *Base) Bounds() Rect
- func (b *Base) Disabled() *mvvm.Observable[bool]
- func (b *Base) Draw(p painter.Painter, theme *Theme)
- func (b *Base) EffectiveFont() Font
- func (b *Base) HitTest(px, py int) bool
- func (b *Base) OnEvent(ev Event)
- func (b *Base) SetBounds(r Rect)
- func (b *Base) SetFont(f Font) *Base
- type BevelDockStyle
- type BevelKind
- type Binding
- type BlockKind
- type Border
- func (b *Border) A11y() A11yInfo
- func (b *Border) Children() []Widget
- func (b *Border) Draw(p painter.Painter, theme *Theme)
- func (b *Border) OnEvent(ev Event)
- func (b *Border) ResizeSplit(side DockSide, size int)
- func (b *Border) SetBounds(r Rect)
- func (b *Border) SplitHandleAt(px, py int) (side DockSide, ok bool)
- type BorderLayout
- type BoxAlign
- type BoxLayout
- type BoxPack
- type Breadcrumbs
- type Browser
- func (b *Browser) A11y() A11yInfo
- func (b *Browser) ActiveIndex() int
- func (b *Browser) ActiveTitle() string
- func (b *Browser) AddressFocused() bool
- func (b *Browser) AddressText() string
- func (b *Browser) Back()
- func (b *Browser) Bookmarked() *mvvm.Observable[bool]
- func (b *Browser) CanBack() bool
- func (b *Browser) CanFit() bool
- func (b *Browser) CanForward() bool
- func (b *Browser) CanZoomIn() bool
- func (b *Browser) CanZoomOut() bool
- func (b *Browser) CloseTab(i int)
- func (b *Browser) CopyAddress() (string, bool)
- func (b *Browser) CurrentURL() string
- func (b *Browser) Deliver(target string, pixels []byte, imgW, imgH, width int, links []BrowserLink, ...)
- func (b *Browser) DeliverStage(target string, pixels []byte, imgW, imgH, width int, links []BrowserLink, ...)
- func (b *Browser) Draw(p painter.Painter, theme *Theme)
- func (b *Browser) FitZoom()
- func (b *Browser) Forward()
- func (b *Browser) Loading() bool
- func (b *Browser) Mode() TabMode
- func (b *Browser) Navigate(href string)
- func (b *Browser) OnEvent(ev Event)
- func (b *Browser) Open(target, title string)
- func (b *Browser) OpenExternal()
- func (b *Browser) Progress() float64
- func (b *Browser) Reload()
- func (b *Browser) ResetZoom()
- func (b *Browser) ScrollExtent() (offset, viewport, total int, shown bool)
- func (b *Browser) SetProgress(frac float64)
- func (b *Browser) SetTabMode(m TabMode)
- func (b *Browser) SetZoom(f float64)
- func (b *Browser) TabCount() int
- func (b *Browser) TabTitle(i int) string
- func (b *Browser) Tick(deltaSeconds float64)
- func (b *Browser) Zoom() float64
- func (b *Browser) ZoomIn()
- func (b *Browser) ZoomOut()
- type BrowserLink
- type Button
- func (b *Button) A11y() A11yInfo
- func (b *Button) Draw(p painter.Painter, theme *Theme)
- func (f *Button) Focused() bool
- func (b *Button) HitRect() Rect
- func (b *Button) HitTest(px, py int) bool
- func (b *Button) Label() *mvvm.Observable[string]
- func (b *Button) OnEvent(ev Event)
- func (b *Button) Selected() *mvvm.Observable[bool]
- func (f *Button) SetFocused(focused bool)
- func (b *Button) SetHovered(v bool)
- func (b *Button) SetPressed(v bool)
- type ButtonGroup
- type ButtonStyle
- type Calendar
- func (c *Calendar) A11y() A11yInfo
- func (c *Calendar) Day() *mvvm.Observable[int]
- func (c *Calendar) Draw(p painter.Painter, theme *Theme)
- func (f *Calendar) Focused() bool
- func (c *Calendar) Month() *mvvm.Observable[int]
- func (c *Calendar) NextMonth()
- func (c *Calendar) OnEvent(ev Event)
- func (c *Calendar) PrevMonth()
- func (c *Calendar) SetDate(year, month, day int)
- func (f *Calendar) SetFocused(focused bool)
- func (c *Calendar) SetToday(y, m, d int)
- func (c *Calendar) Year() *mvvm.Observable[int]
- type Card
- type CardLayout
- type CardMeta
- type Carousel
- func (c *Carousel) A11y() A11yInfo
- func (c *Carousel) Children() []Widget
- func (c *Carousel) Current() *mvvm.Observable[int]
- func (c *Carousel) Draw(p painter.Painter, theme *Theme)
- func (f *Carousel) Focused() bool
- func (c *Carousel) Next()
- func (c *Carousel) OnEvent(ev Event)
- func (c *Carousel) Prev()
- func (f *Carousel) SetFocused(focused bool)
- type CellEditor
- type ChatBubble
- type ChatSender
- type CheckButton
- func (c *CheckButton) A11y() A11yInfo
- func (c *CheckButton) Checked() *mvvm.Observable[bool]
- func (c *CheckButton) Draw(p painter.Painter, theme *Theme)
- func (f *CheckButton) Focused() bool
- func (c *CheckButton) HitRect() Rect
- func (c *CheckButton) HitTest(px, py int) bool
- func (c *CheckButton) OnEvent(ev Event)
- func (f *CheckButton) SetFocused(focused bool)
- type Chip
- type Chord
- type Clipboard
- type CodeEditor
- func (c *CodeEditor) A11y() A11yInfo
- func (c *CodeEditor) CompletionActive() bool
- func (c *CodeEditor) CompletionBounds() Rect
- func (c *CodeEditor) CompletionItems() []CompletionItem
- func (c *CodeEditor) CompletionOpen() *mvvm.Observable[bool]
- func (c *CodeEditor) CompletionSelected() int
- func (c *CodeEditor) CompletionSelection() *mvvm.Observable[int]
- func (c *CodeEditor) Draw(p painter.Painter, theme *Theme)
- func (c *CodeEditor) OnEvent(ev Event)
- type CodeMinimap
- type CollabSheet
- func (c *CollabSheet) AppendCol() (crdt.PartOps, error)
- func (c *CollabSheet) AppendRow() (crdt.PartOps, error)
- func (c *CollabSheet) Apply(batches ...crdt.PartOps) error
- func (c *CollabSheet) CellText(col, row int) string
- func (c *CollabSheet) ColCount() int
- func (c *CollabSheet) DeleteCol(pos int) (crdt.PartOps, error)
- func (c *CollabSheet) DeleteRow(pos int) (crdt.PartOps, error)
- func (c *CollabSheet) InsertCol(pos int) (crdt.PartOps, error)
- func (c *CollabSheet) InsertRow(pos int) (crdt.PartOps, error)
- func (c *CollabSheet) MoveCol(from, to int) (crdt.PartOps, error)
- func (c *CollabSheet) MoveRow(from, to int) (crdt.PartOps, error)
- func (c *CollabSheet) OpsSince(v crdt.CompositeVersion) []crdt.PartOps
- func (c *CollabSheet) Pending() int
- func (c *CollabSheet) Rev() uint64
- func (c *CollabSheet) RowCount() int
- func (c *CollabSheet) SetCellText(col, row int, raw string) (crdt.PartOps, error)
- func (c *CollabSheet) Site() crdt.SiteID
- func (c *CollabSheet) Snapshot() []byte
- func (c *CollabSheet) Subscribe(fn func()) (unsubscribe func())
- func (c *CollabSheet) Version() crdt.CompositeVersion
- type CollabText
- type ColorChooser
- func (c *ColorChooser) A11y() A11yInfo
- func (c *ColorChooser) Color() *mvvm.Observable[RGBA]
- func (c *ColorChooser) Draw(p painter.Painter, theme *Theme)
- func (c *ColorChooser) Hex() string
- func (c *ColorChooser) HitRect() Rect
- func (c *ColorChooser) OnEvent(ev Event)
- func (c *ColorChooser) SetHex(s string)
- type ColorPicker
- type ColumnBrowser
- type ColumnInfo
- type ColumnNode
- type ColumnProvider
- type ComboBox
- func (c *ComboBox) A11y() A11yInfo
- func (c *ComboBox) Draw(p painter.Painter, theme *Theme)
- func (c *ComboBox) Filtered() []string
- func (f *ComboBox) Focused() bool
- func (c *ComboBox) HitRect() Rect
- func (c *ComboBox) OnEvent(ev Event)
- func (c *ComboBox) Open() *mvvm.Observable[bool]
- func (c *ComboBox) PopoverBounds() Rect
- func (f *ComboBox) SetFocused(focused bool)
- func (c *ComboBox) Text() *mvvm.Observable[string]
- type CommandPalette
- func (c *CommandPalette) A11y() A11yInfo
- func (c *CommandPalette) Dismiss()
- func (c *CommandPalette) Draw(p painter.Painter, theme *Theme)
- func (c *CommandPalette) FilteredCommands() []PaletteCommand
- func (c *CommandPalette) HandleKey(ev Event)
- func (c *CommandPalette) MoveSelection(delta int)
- func (c *CommandPalette) OnEvent(ev Event)
- func (c *CommandPalette) Open()
- func (c *CommandPalette) Query() string
- func (c *CommandPalette) Selected() int
- func (c *CommandPalette) SetActions(r *ActionRegistry)
- func (c *CommandPalette) SetQuery(q string)
- func (c *CommandPalette) SetSelected(i int)
- func (c *CommandPalette) Visible() *mvvm.Observable[bool]
- type CompletionItem
- type CompletionItemKind
- type Container
- func (c *Container) A11y() A11yInfo
- func (c *Container) Add(it Item) *Container
- func (c *Container) AddWidget(w Widget) *Container
- func (c *Container) Children() []Widget
- func (c *Container) Draw(p painter.Painter, theme *Theme)
- func (c *Container) Items() []Item
- func (c *Container) OnEvent(ev Event)
- func (c *Container) SetBounds(r Rect)
- func (c *Container) SetItems(items ...Item) *Container
- type ContextMenu
- type Corner
- type CycleButton
- func (c *CycleButton) A11y() A11yInfo
- func (c *CycleButton) Draw(p painter.Painter, theme *Theme)
- func (f *CycleButton) Focused() bool
- func (c *CycleButton) HitRect() Rect
- func (c *CycleButton) HitTest(px, py int) bool
- func (c *CycleButton) Index() *mvvm.Observable[int]
- func (c *CycleButton) OnEvent(ev Event)
- func (f *CycleButton) SetFocused(focused bool)
- func (c *CycleButton) Value() string
- type DataSource
- type DatabaseEditor
- func (d *DatabaseEditor) A11y() A11yInfo
- func (d *DatabaseEditor) Children() []Widget
- func (d *DatabaseEditor) Draw(p painter.Painter, theme *Theme)
- func (d *DatabaseEditor) Editor() *TextView
- func (d *DatabaseEditor) Err() error
- func (d *DatabaseEditor) Exec() (affected int64, ok bool)
- func (d *DatabaseEditor) Grid() *Table
- func (d *DatabaseEditor) OnEvent(ev Event)
- func (d *DatabaseEditor) Refresh() error
- func (d *DatabaseEditor) Run()
- func (d *DatabaseEditor) SQL() string
- func (d *DatabaseEditor) SetBounds(r Rect)
- func (d *DatabaseEditor) SetSQL(sql string)
- func (d *DatabaseEditor) Toolbar() *Toolbar
- func (d *DatabaseEditor) Tree() *TreeView
- type DatabaseInfo
- type Date
- type DatePicker
- func (d *DatePicker) A11y() A11yInfo
- func (dp *DatePicker) Date() (y, m, d int)
- func (dp *DatePicker) Draw(p painter.Painter, theme *Theme)
- func (dp *DatePicker) HitRect() Rect
- func (dp *DatePicker) OnEvent(ev Event)
- func (dp *DatePicker) Open() *mvvm.Observable[bool]
- func (dp *DatePicker) PopoverBounds() Rect
- func (dp *DatePicker) SetDate(year, month, day int)
- func (dp *DatePicker) Text() string
- type DateRangePicker
- func (d *DateRangePicker) A11y() A11yInfo
- func (rp *DateRangePicker) Draw(p painter.Painter, theme *Theme)
- func (d *DateRangePicker) End() *mvvm.Observable[Date]
- func (rp *DateRangePicker) NextArrowHitRect() Rect
- func (rp *DateRangePicker) OnEvent(ev Event)
- func (rp *DateRangePicker) PrevArrowHitRect() Rect
- func (d *DateRangePicker) Start() *mvvm.Observable[Date]
- type DecoButton
- type DecoButtonShape
- type DecoGlyph
- type Decoration
- type DensityLevel
- type Dialog
- type Diff
- type DiffKind
- type DiffLine
- type DocPos
- type DocSelection
- type Dock
- type DockItemState
- type DockSide
- type DockStyle
- type DragSource
- type DropDown
- func (d *DropDown) A11y() A11yInfo
- func (d *DropDown) Current() string
- func (d *DropDown) Draw(p painter.Painter, theme *Theme)
- func (d *DropDown) DrawPopover(p painter.Painter, theme *Theme)
- func (f *DropDown) Focused() bool
- func (d *DropDown) HitRect() Rect
- func (d *DropDown) OnEvent(ev Event)
- func (d *DropDown) Open() *mvvm.Observable[bool]
- func (d *DropDown) PopoverBounds() Rect
- func (d *DropDown) PopoverClick(x, y int) bool
- func (d *DropDown) Select(idx int)
- func (d *DropDown) Selected() *mvvm.Observable[int]
- func (f *DropDown) SetFocused(focused bool)
- type DropTarget
- type DropZone
- type Easing
- type Entry
- func (e *Entry) A11y() A11yInfo
- func (e *Entry) Draw(p painter.Painter, theme *Theme)
- func (f *Entry) Focused() bool
- func (e *Entry) HitRect() Rect
- func (e *Entry) OnEvent(ev Event)
- func (f *Entry) SetFocused(focused bool)
- func (e *Entry) SetText(s string)
- func (e *Entry) Text() *mvvm.Observable[string]
- func (e *Entry) Value() string
- type Event
- type EventKind
- type Execer
- type Expander
- type Fab
- func (f *Fab) A11y() A11yInfo
- func (f *Fab) AddAction(icon, label string, onTap func()) *Fab
- func (f *Fab) AnchorIn(host Rect)
- func (f *Fab) Animating() bool
- func (f *Fab) Children() []Widget
- func (f *Fab) Collapse()
- func (f *Fab) Draw(p painter.Painter, theme *Theme)
- func (f *Fab) Expand()
- func (f *Fab) Expanded() *mvvm.Observable[bool]
- func (f *Fab) Focused() bool
- func (f *Fab) HitTest(px, py int) bool
- func (f *Fab) IsExpanded() bool
- func (f *Fab) OnEvent(ev Event)
- func (f *Fab) SetFocused(focused bool)
- func (f *Fab) Tick(dt float64)
- func (f *Fab) Toggle()
- type FabAction
- type FileChooser
- type FitLayout
- type FlowLayout
- type FocusRing
- type Focusable
- type FolderTabs
- func (t *FolderTabs) A11y() A11yInfo
- func (t *FolderTabs) Children() []Widget
- func (t *FolderTabs) Draw(p painter.Painter, theme *Theme)
- func (f *FolderTabs) Focused() bool
- func (t *FolderTabs) OnEvent(ev Event)
- func (t *FolderTabs) Selected() *mvvm.Observable[int]
- func (f *FolderTabs) SetFocused(focused bool)
- func (t *FolderTabs) TabRect(i int) Rect
- type Font
- func CurrentFont() Font
- func DefaultOpenTypeFont(sizePx int) (Font, error)
- func NewBitmapFont(scale int) Font
- func NewFallbackFont(fonts ...Font) (Font, error)
- func NewSyntheticBoldFont(f Font) (Font, error)
- func NewSyntheticItalicFont(f Font) (Font, error)
- func NewTrueTypeFont(ttf []byte, sizePx int) (Font, error)
- type FontChooser
- type FontOption
- type FormField
- type Frame
- type GalleryItem
- type GalleryView
- func (g *GalleryView) A11y() A11yInfo
- func (g *GalleryView) Draw(p painter.Painter, theme *Theme)
- func (g *GalleryView) OnEvent(ev Event)
- func (g *GalleryView) PreviewRect() Rect
- func (g *GalleryView) Selected() *mvvm.Observable[int]
- func (g *GalleryView) SetBounds(r Rect)
- func (g *GalleryView) SetItems(items []GalleryItem)
- func (g *GalleryView) SetSelected(index int)
- func (g *GalleryView) StripRect() Rect
- func (g *GalleryView) ThumbAt(x, y int) int
- func (g *GalleryView) ThumbRect(i int) (Rect, bool)
- type Gantt
- type GanttTask
- type Gauge
- type GaugeBand
- type GestureRecognizer
- type GradientDir
- type Grid
- type GroupCard
- func (c *GroupCard) A11y() A11yInfo
- func (c *GroupCard) ActionRect() Rect
- func (c *GroupCard) CheckRect() Rect
- func (c *GroupCard) Checked() *mvvm.Observable[bool]
- func (c *GroupCard) ChevronRect() Rect
- func (c *GroupCard) Children() []Widget
- func (c *GroupCard) Draw(p painter.Painter, theme *Theme)
- func (c *GroupCard) Expanded() *mvvm.Observable[bool]
- func (c *GroupCard) Measure(width int) int
- func (c *GroupCard) MemberRect(i int) Rect
- type HBox
- func (b *HBox) A11y() A11yInfo
- func (h *HBox) AddFixed(w Widget, size int)
- func (h *HBox) AddFlex(w Widget, flex int)
- func (h *HBox) Append(w Widget)
- func (h *HBox) Children() []Widget
- func (h *HBox) Draw(p painter.Painter, theme *Theme)
- func (h *HBox) OnEvent(ev Event)
- func (h *HBox) SetBounds(r Rect)
- type HeaderBar
- type Highlighter
- type IconButton
- type IconCell
- type IconFunc
- type IconGrid
- func (v *IconGrid) A11y() A11yInfo
- func (v *IconGrid) DragData() string
- func (v *IconGrid) Draw(p painter.Painter, theme *Theme)
- func (v *IconGrid) IndexAt(x, y int) int
- func (v *IconGrid) OnEvent(ev Event)
- func (v *IconGrid) Selected() *mvvm.Observable[int]
- func (v *IconGrid) SetIconSize(px int)
- func (v *IconGrid) SetSelected(index int)
- type Image
- type InlineStyles
- type IsoAnimatedIcon
- type IsoArrow
- type IsoCRDTDocument
- func (d *IsoCRDTDocument) Apply(batches ...crdt.PartOps) error
- func (d *IsoCRDTDocument) Connectors() []IsoConnector
- func (d *IsoCRDTDocument) Layer(id string) (IsoLayer, bool)
- func (d *IsoCRDTDocument) Layers() []IsoLayer
- func (d *IsoCRDTDocument) Node(id string) (IsoNode, bool)
- func (d *IsoCRDTDocument) Nodes() []IsoNode
- func (d *IsoCRDTDocument) OpsSince(v crdt.CompositeVersion) []crdt.PartOps
- func (d *IsoCRDTDocument) Pending() int
- func (d *IsoCRDTDocument) PutConnector(c IsoConnector)
- func (d *IsoCRDTDocument) PutLayer(l IsoLayer)
- func (d *IsoCRDTDocument) PutNode(n IsoNode)
- func (d *IsoCRDTDocument) PutText(t IsoText)
- func (d *IsoCRDTDocument) PutZone(z IsoZone)
- func (d *IsoCRDTDocument) RemoveConnector(id string)
- func (d *IsoCRDTDocument) RemoveLayer(id string)
- func (d *IsoCRDTDocument) RemoveNode(id string)
- func (d *IsoCRDTDocument) RemoveText(id string)
- func (d *IsoCRDTDocument) RemoveZone(id string)
- func (d *IsoCRDTDocument) Rev() uint64
- func (d *IsoCRDTDocument) Site() crdt.SiteID
- func (d *IsoCRDTDocument) Snapshot() []byte
- func (d *IsoCRDTDocument) Subscribe(fn func()) (unsubscribe func())
- func (d *IsoCRDTDocument) Text(id string) (IsoText, bool)
- func (d *IsoCRDTDocument) Texts() []IsoText
- func (d *IsoCRDTDocument) Version() crdt.CompositeVersion
- func (d *IsoCRDTDocument) Zone(id string) (IsoZone, bool)
- func (d *IsoCRDTDocument) Zones() []IsoZone
- type IsoConnector
- type IsoConnectorStyle
- type IsoDiagram
- func (d *IsoDiagram) A11y() A11yInfo
- func (d *IsoDiagram) AcceptsDrop(payload string) bool
- func (d *IsoDiagram) AddLayer(name string) string
- func (d *IsoDiagram) AnimationPhase() float64
- func (d *IsoDiagram) AnimationStep(dt float64)
- func (d *IsoDiagram) AssignLayer(ref IsoEntityRef, layerID string) bool
- func (d *IsoDiagram) AssignSelectionToLayer(layerID string) bool
- func (d *IsoDiagram) CanRedo() bool
- func (d *IsoDiagram) CanUndo() bool
- func (d *IsoDiagram) Children() []Widget
- func (d *IsoDiagram) Close()
- func (d *IsoDiagram) ContextMenu() *ContextMenu
- func (d *IsoDiagram) Copy()
- func (d *IsoDiagram) Cut()
- func (d *IsoDiagram) DeleteLayer(id string) bool
- func (d *IsoDiagram) DeleteSelection()
- func (d *IsoDiagram) Doc() IsoDocument
- func (d *IsoDiagram) Draw(p painter.Painter, theme *Theme)
- func (d *IsoDiagram) Duplicate()
- func (d *IsoDiagram) ExportJSON() ([]byte, error)
- func (d *IsoDiagram) ImportJSON(data []byte) error
- func (d *IsoDiagram) IsSelected(ref IsoEntityRef) bool
- func (d *IsoDiagram) Layers() []IsoLayer
- func (d *IsoDiagram) OnEvent(ev Event)
- func (d *IsoDiagram) Pan(dx, dy int)
- func (d *IsoDiagram) Paste()
- func (d *IsoDiagram) PlacementIcon() string
- func (d *IsoDiagram) PlacementIconObservable() *mvvm.Observable[string]
- func (d *IsoDiagram) Projection() *iso.Projection
- func (d *IsoDiagram) Redo()
- func (d *IsoDiagram) RenameLayer(id, name string) bool
- func (d *IsoDiagram) RotateCCW()
- func (d *IsoDiagram) RotateCW()
- func (d *IsoDiagram) SelectAll()
- func (d *IsoDiagram) SelectConnector(id string)
- func (d *IsoDiagram) SelectText(id string)
- func (d *IsoDiagram) SelectZone(id string)
- func (d *IsoDiagram) Selected() string
- func (d *IsoDiagram) SelectedConnector() string
- func (d *IsoDiagram) SelectedConnectorObservable() *mvvm.Observable[string]
- func (d *IsoDiagram) SelectedText() string
- func (d *IsoDiagram) SelectedTextObservable() *mvvm.Observable[string]
- func (d *IsoDiagram) SelectedZone() string
- func (d *IsoDiagram) SelectedZoneObservable() *mvvm.Observable[string]
- func (d *IsoDiagram) Selection() []IsoEntityRef
- func (d *IsoDiagram) SelectionList() *mvvm.ObservableList[IsoEntityRef]
- func (d *IsoDiagram) SetBounds(r Rect)
- func (d *IsoDiagram) SetConnectorArrow(id string, a IsoArrow) bool
- func (d *IsoDiagram) SetConnectorColor(id string, col RGBA) bool
- func (d *IsoDiagram) SetConnectorRouted(id string, routed bool) bool
- func (d *IsoDiagram) SetConnectorStyle(id string, s IsoConnectorStyle) bool
- func (d *IsoDiagram) SetLayerLocked(id string, locked bool) bool
- func (d *IsoDiagram) SetLayerOrder(id string, order int) bool
- func (d *IsoDiagram) SetLayerVisible(id string, visible bool) bool
- func (d *IsoDiagram) SetPlacementIcon(id string)
- func (d *IsoDiagram) SetSelectedConnectorArrow(a IsoArrow) bool
- func (d *IsoDiagram) SetSelectedConnectorColor(col RGBA) bool
- func (d *IsoDiagram) SetSelectedConnectorRouted(routed bool) bool
- func (d *IsoDiagram) SetSelectedConnectorStyle(s IsoConnectorStyle) bool
- func (d *IsoDiagram) SetSelectedTextColor(col RGBA) bool
- func (d *IsoDiagram) SetSelectedTextContent(text string) bool
- func (d *IsoDiagram) SetSelectedTextSize(size int) bool
- func (d *IsoDiagram) SetSelectedZoneColor(col RGBA) bool
- func (d *IsoDiagram) SetSelectedZoneLabel(label string) bool
- func (d *IsoDiagram) SetSelectionColor(col RGBA) bool
- func (d *IsoDiagram) SetTextColor(id string, col RGBA) bool
- func (d *IsoDiagram) SetTextContent(id, text string) bool
- func (d *IsoDiagram) SetTextPos(id string, x, y int) bool
- func (d *IsoDiagram) SetTextSize(id string, size int) bool
- func (d *IsoDiagram) SetViewRotation(q int)
- func (d *IsoDiagram) SetZoneColor(id string, col RGBA) bool
- func (d *IsoDiagram) SetZoneLabel(id, label string) bool
- func (d *IsoDiagram) SetZoneRect(id string, x, y, w, h int) bool
- func (d *IsoDiagram) Undo()
- func (d *IsoDiagram) ViewRotation() int
- func (d *IsoDiagram) ViewRotationObservable() *mvvm.Observable[int]
- func (d *IsoDiagram) ZoomAt(factor float64, cx, cy int)
- type IsoDoc
- func (d *IsoDoc) ConnectorList() *mvvm.ObservableList[IsoConnector]
- func (d *IsoDoc) Connectors() []IsoConnector
- func (d *IsoDoc) Layer(id string) (IsoLayer, bool)
- func (d *IsoDoc) LayerList() *mvvm.ObservableList[IsoLayer]
- func (d *IsoDoc) Layers() []IsoLayer
- func (d *IsoDoc) Node(id string) (IsoNode, bool)
- func (d *IsoDoc) NodeList() *mvvm.ObservableList[IsoNode]
- func (d *IsoDoc) Nodes() []IsoNode
- func (d *IsoDoc) PutConnector(c IsoConnector)
- func (d *IsoDoc) PutLayer(l IsoLayer)
- func (d *IsoDoc) PutNode(n IsoNode)
- func (d *IsoDoc) PutText(t IsoText)
- func (d *IsoDoc) PutZone(z IsoZone)
- func (d *IsoDoc) RemoveConnector(id string)
- func (d *IsoDoc) RemoveLayer(id string)
- func (d *IsoDoc) RemoveNode(id string)
- func (d *IsoDoc) RemoveText(id string)
- func (d *IsoDoc) RemoveZone(id string)
- func (d *IsoDoc) Subscribe(fn func()) (unsubscribe func())
- func (d *IsoDoc) Text(id string) (IsoText, bool)
- func (d *IsoDoc) TextList() *mvvm.ObservableList[IsoText]
- func (d *IsoDoc) Texts() []IsoText
- func (d *IsoDoc) Zone(id string) (IsoZone, bool)
- func (d *IsoDoc) ZoneList() *mvvm.ObservableList[IsoZone]
- func (d *IsoDoc) Zones() []IsoZone
- type IsoDocument
- type IsoEntityKind
- type IsoEntityRef
- type IsoIcon
- type IsoIconDrawing
- type IsoIconPack
- type IsoIconPalette
- func (p *IsoIconPalette) A11y() A11yInfo
- func (p *IsoIconPalette) Collapsed() *mvvm.Observable[bool]
- func (p *IsoIconPalette) DragData() string
- func (p *IsoIconPalette) Draw(pt painter.Painter, theme *Theme)
- func (p *IsoIconPalette) Entries() []IsoPaletteEntry
- func (p *IsoIconPalette) Groups() []IsoPaletteGroup
- func (p *IsoIconPalette) OnEvent(ev Event)
- func (p *IsoIconPalette) Origin() *mvvm.Observable[IsoPalettePos]
- func (p *IsoIconPalette) Registry() *IsoIconRegistry
- func (p *IsoIconPalette) SelectIcon(id string)
- func (p *IsoIconPalette) SelectedIcon() *mvvm.Observable[string]
- func (p *IsoIconPalette) SetBounds(r Rect)
- func (p *IsoIconPalette) SetCollapsed(v bool)
- func (p *IsoIconPalette) Toggle()
- type IsoIconRegistry
- type IsoLayer
- type IsoMode
- type IsoNode
- type IsoPaletteEntry
- type IsoPaletteGroup
- type IsoPalettePos
- type IsoPrimitiveIcon
- type IsoProceduralAnimIcon
- type IsoShape
- type IsoSpriteIcon
- type IsoText
- type IsoZone
- type Item
- type Kanban
- func (k *Kanban) A11y() A11yInfo
- func (k *Kanban) CardAt(x, y int) (col, card int)
- func (k *Kanban) Draw(p painter.Painter, theme *Theme)
- func (k *Kanban) MoveCard(fromCol, fromCard, toCol, toIdx int)
- func (k *Kanban) OnEvent(ev Event)
- func (k *Kanban) SelectedCard() *mvvm.Observable[int]
- func (k *Kanban) SelectedCol() *mvvm.Observable[int]
- type KanbanCard
- type KanbanColumn
- type Kbd
- type Keymap
- func (k *Keymap) Bind(chord Chord, action string, scope Scope) error
- func (k *Keymap) Bindings() []Binding
- func (k *Keymap) Conflict(chord Chord, scope Scope) (action string, conflict bool)
- func (k *Keymap) Feed(ev Event, active ScopeMask) (action string, state MatchState)
- func (k *Keymap) Pending() Chord
- func (k *Keymap) Rebind(action string, chord Chord, scope Scope) error
- func (k *Keymap) Reset()
- func (k *Keymap) ShortcutFor(action string) (Chord, bool)
- func (k *Keymap) Unbind(chord Chord, scope Scope) bool
- func (k *Keymap) UnbindAction(action string) int
- type Label
- type Layout
- type LevelBar
- type LevelThreshold
- type LineChart
- func (l *LineChart) A11y() A11yInfo
- func (c *LineChart) Draw(p painter.Painter, theme *Theme)
- func (c *LineChart) Hover() *mvvm.Observable[bool]
- func (c *LineChart) HoverIndex() *mvvm.Observable[int]
- func (c *LineChart) OnEvent(ev Event)
- func (c *LineChart) ValueAt(localX int) (index int, value float64, ok bool)
- type LinkCard
- type ListBox
- func (l *ListBox) A11y() A11yInfo
- func (l *ListBox) AcceptsDrop(payload string) bool
- func (l *ListBox) ClearSelection()
- func (l *ListBox) DragData() string
- func (l *ListBox) Draw(p painter.Painter, theme *Theme)
- func (f *ListBox) Focused() bool
- func (l *ListBox) IndexAt(x, y int) int
- func (l *ListBox) IsSelected(i int) bool
- func (l *ListBox) OnEvent(ev Event)
- func (l *ListBox) ScrollBy(delta int)
- func (l *ListBox) ScrollRow() *mvvm.Observable[int]
- func (l *ListBox) ScrollTo(row int)
- func (l *ListBox) SelectRange(a, b int)
- func (l *ListBox) Selected() *mvvm.Observable[int]
- func (l *ListBox) SelectedIndices() []int
- func (f *ListBox) SetFocused(focused bool)
- func (l *ListBox) SetSelection(indices ...int)
- func (l *ListBox) ToggleSelect(i int)
- type LoadMask
- type LogLevel
- type LogView
- type MarkdownEditor
- type MarkdownView
- type MatchState
- type Material
- func (m *Material) A11y() A11yInfo
- func (m *Material) Children() []Widget
- func (m *Material) Draw(p painter.Painter, theme *Theme)
- func (m *Material) HitTest(px, py int) bool
- func (m *Material) Invalidate()
- func (m *Material) NativeBacked() bool
- func (m *Material) OnEvent(ev Event)
- func (m *Material) SetBounds(r Rect)
- func (m *Material) SetNativeBacked(v bool)
- func (m *Material) SetSource(pixels []byte, w, h int)
- func (m *Material) Spec() MaterialSpec
- type MaterialBlend
- type MaterialKind
- type MaterialSpec
- type Measurer
- type MediaCard
- type Menu
- type MenuBar
- func (m *MenuBar) A11y() A11yInfo
- func (b *MenuBar) Active() *mvvm.Observable[int]
- func (b *MenuBar) AddMenu(name string, m *Menu)
- func (b *MenuBar) Draw(p painter.Painter, theme *Theme)
- func (b *MenuBar) HandleShortcut(code string) bool
- func (b *MenuBar) Mnemonic(i int) byte
- func (b *MenuBar) NameOriginX(i int) int
- func (b *MenuBar) NameWidth(i int) int
- func (b *MenuBar) OnEvent(ev Event)
- type MenuItem
- type ModernDockStyle
- type Momentum
- func (m *Momentum) BeginDrag()
- func (m *Momentum) Bounds() (min, max float64)
- func (m *Momentum) DragBy(delta float64) float64
- func (m *Momentum) EndDrag(velocity float64)
- func (m *Momentum) Fling(velocity float64)
- func (m *Momentum) Offset() float64
- func (m *Momentum) OffsetInt() int
- func (m *Momentum) SetBounds(min, max float64)
- func (m *Momentum) SetOffset(o float64)
- func (m *Momentum) Settling() bool
- func (m *Momentum) Stop()
- func (m *Momentum) Tick(dt float64) bool
- func (m *Momentum) Velocity() float64
- type MomentumScroller
- type MultiTouchRecognizer
- type MultiTouchState
- type Node
- type Notebook
- func (n *Notebook) A11y() A11yInfo
- func (n *Notebook) Active() *mvvm.Observable[int]
- func (n *Notebook) AddTab(label string, page Widget)
- func (n *Notebook) Children() []Widget
- func (n *Notebook) Draw(p painter.Painter, theme *Theme)
- func (f *Notebook) Focused() bool
- func (n *Notebook) OnEvent(ev Event)
- func (n *Notebook) ScrollTabsBy(delta int)
- func (f *Notebook) SetFocused(focused bool)
- type NotebookTab
- type Notification
- func (n *Notification) A11y() A11yInfo
- func (n *Notification) AnchorIn(host Rect, corner Corner)
- func (n *Notification) Draw(p painter.Painter, theme *Theme)
- func (n *Notification) Hide()
- func (n *Notification) Show(text string)
- func (n *Notification) Tick()
- func (n *Notification) Visible() *mvvm.Observable[bool]
- type Orientation
- type Overlay
- func (o *Overlay) A11y() A11yInfo
- func (o *Overlay) Children() []Widget
- func (o *Overlay) Clear()
- func (o *Overlay) Draw(p painter.Painter, theme *Theme)
- func (o *Overlay) OnEvent(ev Event)
- func (o *Overlay) Pop() Widget
- func (o *Overlay) Push(w Widget)
- func (o *Overlay) SetBounds(r Rect)
- func (o *Overlay) Top() Widget
- type PagedMode
- type PagedView
- func (pv *PagedView) A11y() A11yInfo
- func (pv *PagedView) CurrentPage() *mvvm.Observable[int]
- func (pv *PagedView) Draw(p painter.Painter, theme *Theme)
- func (f *PagedView) Focused() bool
- func (pv *PagedView) HitTest(px, py int) bool
- func (pv *PagedView) Mode() *mvvm.Observable[PagedMode]
- func (pv *PagedView) OnEvent(ev Event)
- func (pv *PagedView) PageAt(x, y int) (page, localX, localY int, ok bool)
- func (pv *PagedView) PageCount() int
- func (pv *PagedView) ScrollOffset() (x, y int)
- func (pv *PagedView) ScrollToPage(page, localY int)
- func (pv *PagedView) SetBounds(r Rect)
- func (f *PagedView) SetFocused(focused bool)
- func (pv *PagedView) SetPages(pages []*image.RGBA)
- func (pv *PagedView) Zoom() *mvvm.Observable[int]
- type Pagination
- type PagingToolbar
- type PaletteCommand
- type Paned
- type PieChart
- func (p *PieChart) A11y() A11yInfo
- func (c *PieChart) Draw(p painter.Painter, theme *Theme)
- func (c *PieChart) Hover() *mvvm.Observable[bool]
- func (c *PieChart) HoverIndex() *mvvm.Observable[int]
- func (c *PieChart) OnEvent(ev Event)
- func (c *PieChart) SliceAt(localX, localY int) (index int, value float64, ok bool)
- type Popover
- type PostCard
- type ProgressBar
- type ProgressCircle
- type PropertyGrid
- func (pg *PropertyGrid) A11y() A11yInfo
- func (pg *PropertyGrid) Add(name, value string)
- func (pg *PropertyGrid) Clear()
- func (pg *PropertyGrid) Draw(p painter.Painter, theme *Theme)
- func (pg *PropertyGrid) OnEvent(ev Event)
- func (pg *PropertyGrid) RemoveAt(i int)
- func (pg *PropertyGrid) SetBounds(r Rect)
- func (pg *PropertyGrid) SetValue(name, value string)
- func (pg *PropertyGrid) Table() *Table
- func (pg *PropertyGrid) Value(name string) string
- type PullState
- type PullToRefresh
- func (w *PullToRefresh) A11y() A11yInfo
- func (w *PullToRefresh) Animating() bool
- func (w *PullToRefresh) ChildOffset() (int, int)
- func (w *PullToRefresh) Children() []Widget
- func (w *PullToRefresh) Done()
- func (w *PullToRefresh) Draw(p painter.Painter, theme *Theme)
- func (w *PullToRefresh) OnEvent(ev Event)
- func (w *PullToRefresh) Pull() float64
- func (w *PullToRefresh) PullInt() int
- func (w *PullToRefresh) Refresh()
- func (w *PullToRefresh) Refreshing() bool
- func (w *PullToRefresh) State() PullState
- func (w *PullToRefresh) Tick(dt float64)
- func (w *PullToRefresh) TouchDown(ev Event)
- func (w *PullToRefresh) TouchMove(ev Event) bool
- func (w *PullToRefresh) TouchUp()
- type RGBA
- type RadarChart
- func (c *RadarChart) A11y() A11yInfo
- func (c *RadarChart) AxisAt(localX, localY int) (axis int, ok bool)
- func (c *RadarChart) Draw(p painter.Painter, theme *Theme)
- func (c *RadarChart) Hover() *mvvm.Observable[bool]
- func (c *RadarChart) HoverAxis() *mvvm.Observable[int]
- func (c *RadarChart) OnEvent(ev Event)
- type RadioButton
- func (r *RadioButton) A11y() A11yInfo
- func (r *RadioButton) Checked() *mvvm.Observable[bool]
- func (r *RadioButton) Draw(p painter.Painter, theme *Theme)
- func (f *RadioButton) Focused() bool
- func (r *RadioButton) HitRect() Rect
- func (r *RadioButton) HitTest(px, py int) bool
- func (r *RadioButton) OnEvent(ev Event)
- func (f *RadioButton) SetFocused(focused bool)
- type RadioGroup
- type RangeSlider
- func (s *RangeSlider) A11y() A11yInfo
- func (s *RangeSlider) Draw(p painter.Painter, theme *Theme)
- func (f *RangeSlider) Focused() bool
- func (s *RangeSlider) High() *mvvm.Observable[float64]
- func (s *RangeSlider) HighThumbHitRect() Rect
- func (s *RangeSlider) Low() *mvvm.Observable[float64]
- func (s *RangeSlider) LowThumbHitRect() Rect
- func (s *RangeSlider) OnEvent(ev Event)
- func (f *RangeSlider) SetFocused(focused bool)
- func (s *RangeSlider) SetRange(low, high float64)
- type Rating
- func (r *Rating) A11y() A11yInfo
- func (r *Rating) Draw(p painter.Painter, theme *Theme)
- func (f *Rating) Focused() bool
- func (r *Rating) HitRect() Rect
- func (r *Rating) HitTest(px, py int) bool
- func (r *Rating) OnEvent(ev Event)
- func (f *Rating) SetFocused(focused bool)
- func (r *Rating) Value() *mvvm.Observable[int]
- type Rect
- type Region
- type RichEditor
- func (e *RichEditor) A11y() A11yInfo
- func (e *RichEditor) ActiveInlineStyles() InlineStyles
- func (e *RichEditor) Caret() *mvvm.Observable[DocPos]
- func (e *RichEditor) CaretPixel(pos DocPos) (x, y int)
- func (e *RichEditor) ClearSelection()
- func (e *RichEditor) CurrentBlockKind() BlockKind
- func (e *RichEditor) CurrentListOrdered() (ordered, isList bool)
- func (e *RichEditor) DeleteSelection()
- func (e *RichEditor) Doc() *mvvm.Observable[*richdoc.Document]
- func (e *RichEditor) Document() *richdoc.Document
- func (e *RichEditor) Draw(p painter.Painter, theme *Theme)
- func (e *RichEditor) Focused() *mvvm.Observable[bool]
- func (e *RichEditor) HasSelection() bool
- func (e *RichEditor) InsertText(s string)
- func (e *RichEditor) OnEvent(ev Event)
- func (e *RichEditor) ScrollOffset() *mvvm.Observable[int]
- func (e *RichEditor) Selection() *mvvm.Observable[DocSelection]
- func (e *RichEditor) SetBlockType(kind BlockKind)
- func (e *RichEditor) SetDocument(d *richdoc.Document)
- func (e *RichEditor) ToggleCode()
- func (e *RichEditor) ToggleEmph()
- func (e *RichEditor) ToggleList(ordered bool)
- func (e *RichEditor) ToggleStrikethrough()
- func (e *RichEditor) ToggleStrong()
- type RichEditorToolbar
- func (t *RichEditorToolbar) A11y() A11yInfo
- func (t *RichEditorToolbar) Children() []Widget
- func (t *RichEditorToolbar) Dispose()
- func (t *RichEditorToolbar) Draw(p painter.Painter, theme *Theme)
- func (t *RichEditorToolbar) Editor() *RichEditor
- func (t *RichEditorToolbar) Measure(availW, availH int) (w, h int)
- func (t *RichEditorToolbar) OnEvent(ev Event)
- func (t *RichEditorToolbar) SetBounds(r Rect)
- type Role
- type Rule
- type Scale
- func (s *Scale) A11y() A11yInfo
- func (s *Scale) Draw(p painter.Painter, theme *Theme)
- func (f *Scale) Focused() bool
- func (s *Scale) OnEvent(ev Event)
- func (f *Scale) SetFocused(focused bool)
- func (s *Scale) SetValue(v float64)
- func (s *Scale) ThumbHitRect() Rect
- func (s *Scale) Value() *mvvm.Observable[float64]
- type ScaleMode
- type ScatterChart
- func (c *ScatterChart) A11y() A11yInfo
- func (c *ScatterChart) Draw(p painter.Painter, theme *Theme)
- func (c *ScatterChart) Hover() *mvvm.Observable[bool]
- func (c *ScatterChart) HoverPoint() *mvvm.Observable[int]
- func (c *ScatterChart) HoverSeries() *mvvm.Observable[int]
- func (c *ScatterChart) NearestPoint(localX, localY int) (series, point int, pt ScatterPoint, ok bool)
- func (c *ScatterChart) OnEvent(ev Event)
- type ScatterPoint
- type Schema
- type Scope
- type ScopeMask
- type ScrollView
- func (s *ScrollView) A11y() A11yInfo
- func (s *ScrollView) Animating() bool
- func (s *ScrollView) ChildOffset() (int, int)
- func (s *ScrollView) Children() []Widget
- func (s *ScrollView) Draw(p painter.Painter, theme *Theme)
- func (s *ScrollView) HitTest(px, py int) bool
- func (s *ScrollView) OffsetX() *mvvm.Observable[int]
- func (s *ScrollView) OffsetY() *mvvm.Observable[int]
- func (s *ScrollView) OnEvent(ev Event)
- func (s *ScrollView) Overscroll() (x, y int)
- func (s *ScrollView) Scroll(dx, dy int)
- func (s *ScrollView) ScrollDriven() bool
- func (s *ScrollView) SetContentSize(w, h int)
- func (s *ScrollView) SetScrollDriver(driver any)
- func (s *ScrollView) Tick(dt float64)
- type Scrollbar
- type SearchEntry
- func (s *SearchEntry) A11y() A11yInfo
- func (s *SearchEntry) ClearHitRect() Rect
- func (s *SearchEntry) Draw(p painter.Painter, theme *Theme)
- func (f *SearchEntry) Focused() bool
- func (s *SearchEntry) HitRect() Rect
- func (s *SearchEntry) OnEvent(ev Event)
- func (f *SearchEntry) SetFocused(focused bool)
- func (s *SearchEntry) Text() *mvvm.Observable[string]
- type SegmentedBar
- type SelectableText
- type Selection
- type Size
- type Skeleton
- type SkeletonGroup
- func (g *SkeletonGroup) A11y() A11yInfo
- func (g *SkeletonGroup) Add(s *Skeleton, local Rect) *SkeletonGroup
- func (g *SkeletonGroup) Animating() bool
- func (g *SkeletonGroup) Draw(p painter.Painter, theme *Theme)
- func (g *SkeletonGroup) Items() []SkeletonItem
- func (g *SkeletonGroup) SetPhase(t float64) *SkeletonGroup
- func (g *SkeletonGroup) Tick(deltaSeconds float64)
- type SkeletonItem
- type SkeletonKind
- type SourceItem
- type SourceList
- func (s *SourceList) A11y() A11yInfo
- func (s *SourceList) AcceptsDrop(payload string) bool
- func (s *SourceList) DragData() string
- func (s *SourceList) Draw(p painter.Painter, theme *Theme)
- func (s *SourceList) OnEvent(ev Event)
- func (s *SourceList) Selected() (section, row int)
- func (s *SourceList) SetBounds(r Rect)
- func (s *SourceList) SetSelected(section, row int)
- type SourceSection
- type SparkKind
- type Sparkline
- func (s *Sparkline) A11y() A11yInfo
- func (s *Sparkline) Draw(p painter.Painter, theme *Theme)
- func (s *Sparkline) HitTest(_, _ int) bool
- func (s *Sparkline) Hover() *mvvm.Observable[bool]
- func (s *Sparkline) HoverIndex() *mvvm.Observable[int]
- func (s *Sparkline) OnEvent(ev Event)
- func (s *Sparkline) ValueAt(localX int) (index int, value float64, ok bool)
- type SpinButton
- func (s *SpinButton) A11y() A11yInfo
- func (s *SpinButton) Draw(p painter.Painter, theme *Theme)
- func (f *SpinButton) Focused() bool
- func (s *SpinButton) OnEvent(ev Event)
- func (f *SpinButton) SetFocused(focused bool)
- func (s *SpinButton) SetValue(v int)
- func (s *SpinButton) Value() *mvvm.Observable[int]
- type Spinner
- type SpinnerStyle
- type SplitButton
- func (b *SplitButton) A11y() A11yInfo
- func (s *SplitButton) Draw(p painter.Painter, theme *Theme)
- func (f *SplitButton) Focused() bool
- func (s *SplitButton) HitRect() Rect
- func (s *SplitButton) HitTest(px, py int) bool
- func (s *SplitButton) OnEvent(ev Event)
- func (f *SplitButton) SetFocused(focused bool)
- type Spreadsheet
- func (s *Spreadsheet) A11y() A11yInfo
- func (s *Spreadsheet) Active() (col, row int)
- func (s *Spreadsheet) BeginEdit()
- func (s *Spreadsheet) CancelEdit()
- func (s *Spreadsheet) CellDisplay(col, row int) string
- func (s *Spreadsheet) CellRaw(col, row int) string
- func (s *Spreadsheet) Cols() int
- func (s *Spreadsheet) CommitEdit()
- func (s *Spreadsheet) Draw(p painter.Painter, theme *Theme)
- func (s *Spreadsheet) Editing() bool
- func (f *Spreadsheet) Focused() bool
- func (s *Spreadsheet) OnEvent(ev Event)
- func (s *Spreadsheet) Rows() int
- func (s *Spreadsheet) ScrollBy(dCol, dRow int)
- func (s *Spreadsheet) ScrollOffset() (col, row int)
- func (s *Spreadsheet) SetCell(col, row int, raw string)
- func (f *Spreadsheet) SetFocused(focused bool)
- type Stack
- type Stat
- type StatTrend
- type StatusArea
- type StatusIcon
- type Statusbar
- type Steps
- type Surface
- type SurfaceElement
- type SwipeAction
- type SwipeActions
- func (sa *SwipeActions) A11y() A11yInfo
- func (sa *SwipeActions) Children() []Widget
- func (sa *SwipeActions) Close()
- func (sa *SwipeActions) Draw(p painter.Painter, theme *Theme)
- func (sa *SwipeActions) InvokeLeading(i int)
- func (sa *SwipeActions) InvokeTrailing(i int)
- func (sa *SwipeActions) IsOpen() bool
- func (sa *SwipeActions) Offset() int
- func (sa *SwipeActions) OnEvent(ev Event)
- func (sa *SwipeActions) Open() *mvvm.Observable[SwipeOpenState]
- func (sa *SwipeActions) OpenLeading()
- func (sa *SwipeActions) OpenTrailing()
- func (sa *SwipeActions) Settling() bool
- func (sa *SwipeActions) State() SwipeOpenState
- func (sa *SwipeActions) Tick(dt float64) bool
- type SwipeDir
- type SwipeOpenState
- type Switch
- func (s *Switch) A11y() A11yInfo
- func (s *Switch) Draw(p painter.Painter, theme *Theme)
- func (f *Switch) Focused() bool
- func (s *Switch) HitRect() Rect
- func (s *Switch) HitTest(px, py int) bool
- func (s *Switch) On() *mvvm.Observable[bool]
- func (s *Switch) OnEvent(ev Event)
- func (f *Switch) SetFocused(focused bool)
- type TabBar
- func (t *TabBar) A11y() A11yInfo
- func (t *TabBar) Children() []Widget
- func (t *TabBar) Draw(p painter.Painter, theme *Theme)
- func (f *TabBar) Focused() bool
- func (t *TabBar) ItemHitRect(i int) Rect
- func (t *TabBar) ItemRect(i int) Rect
- func (t *TabBar) OnEvent(ev Event)
- func (t *TabBar) Selected() *mvvm.Observable[int]
- func (f *TabBar) SetFocused(focused bool)
- type TabItem
- type TabMode
- type TabSide
- type Table
- func (t *Table) A11y() A11yInfo
- func (t *Table) AcceptsDrop(payload string) bool
- func (t *Table) ArrangeGroups()
- func (t *Table) BeginEdit(row, col int)
- func (t *Table) CancelEdit()
- func (t *Table) ClearRowSelection()
- func (t *Table) ColumnSeparatorAt(localX int) int
- func (t *Table) CommitEdit()
- func (t *Table) DragData() string
- func (t *Table) Draw(p painter.Painter, theme *Theme)
- func (t *Table) EditError() error
- func (t *Table) Editing() (row, col int, editing bool)
- func (f *Table) Focused() bool
- func (t *Table) IsRowSelected(i int) bool
- func (t *Table) OnEvent(ev Event)
- func (t *Table) RowAt(x, y int) int
- func (t *Table) ScrollBy(delta int)
- func (t *Table) ScrollRow() *mvvm.Observable[int]
- func (t *Table) ScrollTo(row int)
- func (t *Table) ScrollX() *mvvm.Observable[int]
- func (t *Table) ScrollXBy(delta int)
- func (t *Table) ScrollXTo(px int)
- func (t *Table) SelectRowRange(a, b int)
- func (t *Table) Selected() *mvvm.Observable[int]
- func (t *Table) SelectedRows() []int
- func (t *Table) SetColumnWidth(col, w int)
- func (f *Table) SetFocused(focused bool)
- func (t *Table) SetRowSelection(rows ...int)
- func (t *Table) SortAsc() *mvvm.Observable[bool]
- func (t *Table) SortByColumn(col int, ascending bool)
- func (t *Table) SortColumn() *mvvm.Observable[int]
- func (t *Table) ToggleRowSelect(i int)
- type TableAggregate
- type TableColumn
- type TableEditActivation
- type TableIconFunc
- type TableInfo
- type TagField
- func (t *TagField) A11y() A11yInfo
- func (t *TagField) Draw(p painter.Painter, theme *Theme)
- func (f *TagField) Focused() bool
- func (t *TagField) HitRect() Rect
- func (t *TagField) OnEvent(ev Event)
- func (f *TagField) SetFocused(focused bool)
- func (t *TagField) Tags() *mvvm.Observable[[]string]
- func (t *TagField) Text() *mvvm.Observable[string]
- type TermCell
- type TerminalView
- func (t *TerminalView) A11y() A11yInfo
- func (t *TerminalView) Cell(col, row int) TermCell
- func (t *TerminalView) CellHeight() int
- func (t *TerminalView) CellWidth() int
- func (t *TerminalView) Draw(p painter.Painter, theme *Theme)
- func (t *TerminalView) OnEvent(ev Event)
- func (t *TerminalView) Put(col, row int, ru rune)
- func (t *TerminalView) Resize(newCols, newRows int)
- func (t *TerminalView) ScrollUp(n int)
- func (t *TerminalView) SetBounds(r Rect)
- func (t *TerminalView) SetCell(col, row int, ru rune, fg, bg RGBA)
- func (t *TerminalView) SetCellSize(w, h int)
- func (t *TerminalView) Write(s string)
- type TextDirection
- type TextRun
- type TextSelection
- func (s *TextSelection) Begin(x, y int)
- func (s *TextSelection) Clear()
- func (s *TextSelection) CopySelection() string
- func (s *TextSelection) Drag(x, y int)
- func (s *TextSelection) Draw(p painter.Painter, col RGBA)
- func (s *TextSelection) End()
- func (s *TextSelection) IsEmpty() bool
- func (s *TextSelection) SelectedText() string
- func (s *TextSelection) SetRuns(runs []TextRun)
- type TextSpan
- type TextView
- func (t *TextView) A11y() A11yInfo
- func (t *TextView) CaretPixel(line, col int) (x, y int)
- func (t *TextView) ClearSelection()
- func (t *TextView) CopySelection() string
- func (t *TextView) CursorCol() *mvvm.Observable[int]
- func (t *TextView) CursorLine() *mvvm.Observable[int]
- func (t *TextView) CutSelection() string
- func (t *TextView) DeleteSelection()
- func (t *TextView) Draw(p painter.Painter, theme *Theme)
- func (t *TextView) Focused() *mvvm.Observable[bool]
- func (t *TextView) HasSelection() bool
- func (t *TextView) OnEvent(ev Event)
- func (t *TextView) Paste(text string)
- func (t *TextView) Redo()
- func (t *TextView) ScrollLine() *mvvm.Observable[int]
- func (t *TextView) SelectAll()
- func (t *TextView) Selection() *mvvm.Observable[Selection]
- func (t *TextView) SelectionText() string
- func (t *TextView) SetSelection(sel Selection)
- func (t *TextView) SetText(s string)
- func (t *TextView) Text() *mvvm.Observable[string]
- func (t *TextView) Undo()
- type Theme
- type Thumbnail
- func (t *Thumbnail) A11y() A11yInfo
- func (t *Thumbnail) Draw(p painter.Painter, theme *Theme)
- func (t *Thumbnail) Hover() *mvvm.Observable[bool]
- func (t *Thumbnail) Invalidate()
- func (t *Thumbnail) OnEvent(ev Event)
- func (t *Thumbnail) Selected() *mvvm.Observable[bool]
- func (t *Thumbnail) SetPixels(pixels []byte, w, h int)
- type TimePicker
- func (t *TimePicker) A11y() A11yInfo
- func (tp *TimePicker) Draw(p painter.Painter, theme *Theme)
- func (tp *TimePicker) HitRect() Rect
- func (tp *TimePicker) Hour() *mvvm.Observable[int]
- func (tp *TimePicker) Minute() *mvvm.Observable[int]
- func (tp *TimePicker) OnEvent(ev Event)
- func (tp *TimePicker) StepHour(delta int)
- func (tp *TimePicker) StepMinute(delta int)
- func (tp *TimePicker) String() string
- func (tp *TimePicker) ToggleAmPm()
- type Timeline
- type TimelineEvent
- type TimelineKind
- type Toast
- func (t *Toast) A11y() A11yInfo
- func (t *Toast) AnchorIn(host Rect, corner Corner, index int)
- func (t *Toast) ButtonRects() []Rect
- func (t *Toast) Draw(p painter.Painter, theme *Theme)
- func (t *Toast) Life() *mvvm.Observable[int]
- func (t *Toast) OnEvent(ev Event)
- func (t *Toast) Tick()
- func (t *Toast) Visible() *mvvm.Observable[bool]
- type ToastAction
- type ToastKind
- type ToggleButton
- func (t *ToggleButton) A11y() A11yInfo
- func (t *ToggleButton) Draw(p painter.Painter, theme *Theme)
- func (f *ToggleButton) Focused() bool
- func (t *ToggleButton) HitRect() Rect
- func (t *ToggleButton) HitTest(px, py int) bool
- func (t *ToggleButton) OnEvent(ev Event)
- func (t *ToggleButton) Pressed() *mvvm.Observable[bool]
- func (f *ToggleButton) SetFocused(focused bool)
- type Toolbar
- type ToolbarItem
- type Tooltip
- type TooltipPlacement
- type TreeNode
- type TreeTable
- func (t *TreeTable) A11y() A11yInfo
- func (t *TreeTable) Draw(p painter.Painter, theme *Theme)
- func (f *TreeTable) Focused() bool
- func (t *TreeTable) NodeAt(x, y int) *TreeTableNode
- func (t *TreeTable) OnEvent(ev Event)
- func (t *TreeTable) Remove(n *TreeTableNode) bool
- func (t *TreeTable) ScrollBy(delta int)
- func (t *TreeTable) ScrollRow() *mvvm.Observable[int]
- func (t *TreeTable) ScrollTo(row int)
- func (t *TreeTable) Selected() *mvvm.Observable[*TreeTableNode]
- func (f *TreeTable) SetFocused(focused bool)
- type TreeTableColumn
- type TreeTableNode
- type TreeView
- func (t *TreeView) A11y() A11yInfo
- func (t *TreeView) ClearSelection()
- func (t *TreeView) Draw(p painter.Painter, theme *Theme)
- func (f *TreeView) Focused() bool
- func (t *TreeView) IsSelected(n *TreeNode) bool
- func (t *TreeView) NodeAt(x, y int) *TreeNode
- func (t *TreeView) OnEvent(ev Event)
- func (t *TreeView) Remove(n *TreeNode) bool
- func (t *TreeView) RowContentWidth(depth int) int
- func (t *TreeView) ScrollBy(delta int)
- func (t *TreeView) ScrollExtent() (offset, window, total int, shown bool)
- func (t *TreeView) ScrollRow() *mvvm.Observable[int]
- func (t *TreeView) ScrollTo(row int)
- func (t *TreeView) SelectRange(a, b *TreeNode)
- func (t *TreeView) Selected() *mvvm.Observable[*TreeNode]
- func (t *TreeView) SelectedNodes() []*TreeNode
- func (f *TreeView) SetFocused(focused bool)
- func (t *TreeView) SetSelection(nodes ...*TreeNode)
- func (t *TreeView) ToggleSelect(n *TreeNode)
- type Tween
- type VAlign
- type VBox
- func (b *VBox) A11y() A11yInfo
- func (v *VBox) AddFixed(w Widget, size int)
- func (v *VBox) AddFlex(w Widget, flex int)
- func (v *VBox) Append(w Widget)
- func (v *VBox) Children() []Widget
- func (v *VBox) Draw(p painter.Painter, theme *Theme)
- func (v *VBox) OnEvent(ev Event)
- func (v *VBox) SetBounds(r Rect)
- type VelocityTracker
- type ViewController
- type ViewSwitcher
- type Viewport
- func (v *Viewport) A11y() A11yInfo
- func (v *Viewport) Children() []Widget
- func (v *Viewport) Draw(p painter.Painter, theme *Theme)
- func (v *Viewport) OnEvent(ev Event)
- func (v *Viewport) RegionRect(region ViewportRegion) Rect
- func (v *Viewport) Set(region ViewportRegion, w Widget, size int)
- func (v *Viewport) SetBounds(r Rect)
- type ViewportRegion
- type Wallpaper
- type WallpaperMode
- type WheelPicker
- func (w *WheelPicker) A11y() A11yInfo
- func (w *WheelPicker) Draw(p painter.Painter, theme *Theme)
- func (w *WheelPicker) Focus() int
- func (w *WheelPicker) NumColumns() int
- func (w *WheelPicker) OnEvent(ev Event)
- func (w *WheelPicker) SelectedIndex(col int) int
- func (w *WheelPicker) SelectedValue(col int) string
- func (w *WheelPicker) SetFocus(col int)
- func (w *WheelPicker) SetIndex(col, idx int)
- func (w *WheelPicker) Settling() bool
- func (w *WheelPicker) Step(delta int)
- func (w *WheelPicker) Tick(dt float64) bool
- func (w *WheelPicker) TouchDown(ev Event)
- func (w *WheelPicker) TouchMove(ev Event, dt float64)
- func (w *WheelPicker) TouchUp()
- type Widget
- type Window
- func (w *Window) A11y() A11yInfo
- func (w *Window) Children() []Widget
- func (w *Window) Draw(p painter.Painter, theme *Theme)
- func (w *Window) HitRegion(px, py int) WindowRegion
- func (w *Window) MoveBy(dx, dy int)
- func (w *Window) OnEvent(ev Event)
- func (w *Window) ResizeTo(width, height int)
- func (w *Window) SetBounds(r Rect)
- type WindowDecoration
- type WindowRegion
- type WindowsDockStyle
- type Wizard
- type WizardStep
Examples ¶
Constants ¶
const ( // ActionRowPadX is the horizontal inset for the title / subtitle // text from the row's left edge (or from the prefix slot when a // Prefix widget is present). ActionRowPadX = 12 // ActionRowPadY is the vertical inset for the title above the // row's top edge; the subtitle flows below the title. ActionRowPadY = 8 // ActionRowSubtitleGap is the extra vertical gap between the title // glyph row and the subtitle glyph row. ActionRowSubtitleGap = 2 // ActionRowSlotW is the fixed width of the Prefix / Suffix slots. ActionRowSlotW = 32 )
Sizing constants. PadX / PadY inset text from the row edges; SubtitleGap is the vertical gap between the title's baseline and the subtitle's first row; SlotW is the width reserved for the optional Prefix / Suffix child widget slots.
const ( // DetentFull shows the whole panel. DetentFull = 1.0 // DetentHalf shows the bottom half of the panel. DetentHalf = 0.5 )
Detent visible-fraction constants for the common bottom-sheet stops.
const ( // ActionSheetPad is the inner margin around the panel body. ActionSheetPad = 10 // ActionSheetGap is the vertical gap between adjacent action rows. ActionSheetGap = 8 // ActionSheetCancelGap is the wider gap separating Cancel from the actions. ActionSheetCancelGap = 16 // ActionSheetRowH is the base action-row height, before [TouchTarget] clamps // it up to the density's finger floor. ActionSheetRowH = 44 // ActionSheetTitleH is the title strip height. ActionSheetTitleH = 28 // ActionSheetHandleH is the grab-handle strip height (the drag zone). ActionSheetHandleH = 22 // ActionSheetHandleBarW / ActionSheetHandleBarH size the little grab bar. ActionSheetHandleBarW = 40 ActionSheetHandleBarH = 5 // ActionSheetCornerR rounds the panel's top corners. ActionSheetCornerR = 14 // ActionSheetScrimAlpha is the default scrim opacity at full visibility. ActionSheetScrimAlpha = 120 // ActionSheetDismissFraction is the default slow-release dismiss floor. ActionSheetDismissFraction = 0.5 // ActionSheetFlingVelocity is the default fling decision speed (px/s). ActionSheetFlingVelocity = 700.0 // ActionSheetSlideDuration is the default open/dismiss slide length (s). ActionSheetSlideDuration = 0.24 // ActionSheetFrameSeconds is the default per-frame time OnEvent assumes. ActionSheetFrameSeconds = 1.0 / 60.0 )
ActionSheet metric + tuning defaults, all in LOGICAL px / seconds unless noted. Pixel metrics route through [scaled] so they follow HiDPI and density.
const ( // AgendaHeaderH is the pixel height of the day-name header row (also the // weekday header in the month view). AgendaHeaderH = 24 // AgendaHourH is the pixel height of one hour row in the grid. AgendaHourH = 32 // AgendaGutterW is the pixel width of the left hour-label gutter. AgendaGutterW = 48 // AgendaDayCellH is the pixel height of one day cell in the month view. AgendaDayCellH = 56 // AgendaMiniMonthGap is the pixel gap between mini month grids in the // quarter and year views. AgendaMiniMonthGap = 12 )
Agenda sizing constants, exported like TableRowHeight / GanttHeaderH so a host can measure the widget before it has a surface: scaled(AgendaHeaderH) + hours* AgendaHourH gives the natural height and scaled(AgendaGutterW) is the fixed hour-label gutter width.
const ( AlertPadX = 12 AlertPadY = 8 )
AlertPadX / AlertPadY set the internal margin between the banner edges and the text. Matches Notification's PadX/PadY so the two widgets read as siblings when they're used side-by-side.
const ( // AppDockItemW / AppDockItemH are one item's resting width and height. AppDockItemW = 120 AppDockItemH = 28 // AppDockGap is the spacing between adjacent items (and the end padding). AppDockGap = 4 // AppDockGlyphPx is the icon's side length inside a resting item. AppDockGlyphPx = 18 // AppDockPadX is the inset from an item's left edge to its glyph. AppDockPadX = 8 // AppDockLabelGap is the gap between the glyph and the label. AppDockLabelGap = 8 )
AppDock geometry, in logical pixels (each routed through the metric scale so the bar tracks the display like every other widget).
const ( BadgePadX = 4 BadgePadY = 1 )
BadgePadX / BadgePadY are the horizontal and vertical insets between the pill body and the text glyphs. Small: a badge should read as a compact tag, not a button. Vertical padding is intentionally 1 so the pill stays short next to same-line body text.
const ( BannerPadX = 12 BannerPadY = 8 BannerButtonPadX = 8 )
Banner sizing constants. BannerPadX/PadY are the internal margin between the strip edges and the text; BannerButtonPadX is the inner horizontal inset between the button label and its border box.
const ( // BrowserTabStripH is the tab-strip row height (shown only in MultiTab with // at least two tabs). BrowserTabStripH = 24 // BrowserToolbarH is the Back/Forward/Reload/address toolbar row height. It // is deliberately tall enough that a button box (BrowserToolbarH - 2*PadY) // is a comfortable ~34px square at scale 1 — a real, clickable icon button // rather than a cramped text chip. Multiply by Browser.Scale on HiDPI hosts. BrowserToolbarH = 44 // BrowserProgressH is the loading bar height across the content top. BrowserProgressH = 3 // BrowserPadX / BrowserPadY are the toolbar's inner insets. PadY is generous // so the buttons sit centred in the taller row with breathing room above and // below. BrowserPadX = 4 BrowserPadY = 5 // BrowserBtnGap is the gap between toolbar buttons. BrowserBtnGap = 4 // BrowserBtnPad is the horizontal text inset inside a toolbar button / the // address field. BrowserBtnPad = 6 // BrowserMaxTabs caps how many tabs MultiTab keeps; opening past it evicts // the oldest. BrowserMaxTabs = 12 // BrowserScrollStep is the content pixels scrolled per wheel row. BrowserScrollStep = 24 // BrowserTabGap is the gap between tab pills. BrowserTabGap = 2 // BrowserTabCloseW is the width of a tab pill's × close box. BrowserTabCloseW = 14 )
Chrome sizing constants.
const ( // BrowserMinZoom / BrowserMaxZoom clamp the page-zoom factor. BrowserMinZoom = 0.5 BrowserMaxZoom = 3.0 )
Page-zoom bounds. Zoom scales the already-delivered page bitmap for display only (it never re-fetches or re-renders): the content is drawn magnified or shrunk by the zoom factor, the scroll extent grows/shrinks with it, and link hit-testing maps back through the zoom.
const ( CalendarHeaderH = 22 CalendarCellW = 24 CalendarCellH = 18 // left ("<") and right (">") ends of the header row. CalendarNavW = 20 )
Sizing.
const ( // CardPadX is the horizontal inset for header / body / footer text. CardPadX = 8 // CardPadY is the vertical inset for the body text above the first // line + between the footer text and its strip border. CardPadY = 6 // CardLineSpacing is the extra vertical gap inserted between two // body lines so successive glyph rows don't touch. CardLineSpacing = 2 )
Card sizing constants. Header + Footer strips are the same size so the card reads as a symmetric frame; the body gets a matching inner pad on the left and top.
const ( // CardCornerRadius is the corner rounding of a content card's frame, in // pixels. Matched to Badge / GalleryView so a card sits next to them cleanly; // a cell back-end that cannot round degrades to square corners. CardCornerRadius = 6 // CardGapX is the horizontal gap between a leading glyph column (a favicon, // say) and the text beside it. CardGapX = 6 // CardGapY is the vertical gap inserted between two stacked content blocks // (thumbnail / title / body / meta). Distinct from CardLineSpacing, which is // the tighter gap between successive lines WITHIN one wrapped text block. CardGapY = 4 )
const ( // ChatBubblePadX is the horizontal inset between the bubble edge // and the text. ChatBubblePadX = 10 // ChatBubblePadY is the vertical inset above the first text line // and below the last. ChatBubblePadY = 6 // ChatBubbleMaxW caps the bubble's rendered width in pixels so a // pathologically long line stays inside a reasonable column. ChatBubbleMaxW = 220 // ChatBubbleLineSpacing is the extra vertical gap between two // text lines in a multi-line bubble. ChatBubbleLineSpacing = 2 )
Sizing constants for the bubble geometry.
const ( ChipPadX = 8 ChipPadY = 2 ChipCloseW = 12 ChipCloseGap = 4 // ChipDotD is the diameter in pixels of the optional leading swatch // circle (kept modest to match the chip's compact scale); ChipDotGap // is the pixel gap between that swatch and the start of the Text. ChipDotD = 6 ChipDotGap = 4 )
Chip sizing constants. PadX / PadY are the inner insets from the pill edge to the Text glyphs (kept small so a row of chips reads as compact tags); CloseW is the pixel width of the "x" click slot at the right edge; CloseGap is the pixel gap between the Text and the close slot when Closable is true.
const ( // MetaName is the presence key carrying a participant's display name, shown // as the tag beside its remote caret. MetaName = "name" // MetaColor is the presence key carrying a participant's caret colour, as the // "#rrggbbaa" string [Decoration.Color] is painted in. MetaColor = "color" )
Presence metadata keys. A participant publishes its display name and caret colour under these keys, and a peer reads them back to paint a Decoration. They are this binding's private contract with itself — the awareness core carries the map verbatim and interprets none of it.
const ( ColorChooserChannelH = 22 ColorChooserPreviewH = 36 ColorChooserPadX = 8 ColorChooserChannelPadY = 4 )
Sizing.
const ( ColorPickerSquareSize = 120 ColorPickerHueStripW = 18 ColorPickerGap = 8 ColorPickerAlphaH = 16 ColorPickerSwatchSize = 28 ColorPickerEyedropSize = 20 // ColorPickerWidth and ColorPickerHeight are the picker's natural // (unclamped) LOGICAL footprint. Use [ColorPickerNaturalSize] for the // footprint to lay out with: at a metric scale above 1 it is larger, like // every other metric. ColorPickerWidth = ColorPickerSquareSize + ColorPickerGap + ColorPickerHueStripW ColorPickerHeight = ColorPickerSquareSize + ColorPickerGap + ColorPickerAlphaH + ColorPickerGap + ColorPickerSwatchSize )
Sizing. The SV square and hue strip share a height; the alpha slider spans their combined width beneath them; the swatch + eyedropper button sit in a final row.
const ( // CompletionRowH is the pixel height of one candidate row — matched to the // DropDown/palette popover row so the completion list reads as the same // family of overlay. CompletionRowH = 18 // CompletionMaxRows caps how many rows the popup shows at once; a longer // filtered list is reachable by scrolling (see compScroll). CompletionMaxRows = 10 // CompletionMinW is the floor on the popup width so a list of short labels // still reads as a panel. CompletionMinW = 120 )
Completion popup metrics, in LOGICAL pixels (routed through scaled at use).
const ( DropZonePadX = 12 DropZonePadY = 12 DropZoneDashLen = 4 DropZoneDashGap = 4 DropZoneBorderW = 2 )
DropZone sizing constants. PadX / PadY are the outer insets from the bounds to where inner content (the prompt text) sits; DashLen + DashGap describe the stripe pattern of the dashed border, and BorderW is its per-edge pixel thickness. Kept generous so the dashed rectangle reads as a container, not a thin outline.
const ( // FabDiameter is the default disc diameter. FabDiameter = 56 // FabMiniDiameter is the default speed-dial mini-button diameter. FabMiniDiameter = 40 // FabMargin is the default inset from the docked screen edges. FabMargin = 16 // FabElevation is the elevation shadow's downward offset. FabElevation = 6 // FabActionGap is the spacing between the disc and the first mini, and // between successive minis. FabActionGap = 12 )
Fab metric defaults, in LOGICAL pixels (routed through scaled). The disc and mini diameters match Material's 56/40-dp FAB pair; the margin is the 16-dp screen inset; the elevation is the shadow's downward offset; the gap is the spacing between stacked minis and the disc.
const ( FileChooserTreeRatio = 35 // % of width for the tree pane FileChooserButtonStripH = 32 FileChooserPathH = 24 )
Sizing constants.
const ( // FolderTabsH is the strip's default vertical extent — deliberately short so // the tabs read as slim notebook tabs, not buttons. FolderTabsH = 24 // FolderTabsRadius is the corner radius applied to each tab's two TOP corners. FolderTabsRadius = 6 // FolderTabsPadX is the horizontal padding either side of a tab's label. FolderTabsPadX = 12 // FolderTabsGap is the horizontal gap between adjacent tabs. FolderTabsGap = 3 // FolderTabsInset is the left margin before the first tab, and the small gap // above the tabs (off the strip's top edge). FolderTabsInset = 3 // FolderTabsAccentH is the thickness of the active tab's top accent bar. FolderTabsAccentH = 2 )
Folder-tab layout metrics, all in LOGICAL pixels (routed through scaled at use). Exposed so a host allocating a strip can size it to match.
const ( // FontChooserPad is the panel's inner inset. FontChooserPad = 4 // FontChooserRowPad is the vertical padding above+below each row's glyphs. FontChooserRowPad = 3 )
FontChooser sizing.
const ( // GanttRowH is the pixel height of one task row. GanttRowH = 24 // GanttHeaderH is the pixel height of the tick-header band. GanttHeaderH = 20 // GanttLabelW is the pixel width of the left label gutter. GanttLabelW = 96 )
Gantt sizing constants, exported like TableRowHeight / TableHeaderHeight so a host can measure a chart before it has a surface (rows*scaled(GanttRowH) + GanttHeaderH gives the natural height; scaled(GanttLabelW) is the fixed gutter width).
const ( // GroupChevronW is the logical width of the disclosure-chevron column. GroupChevronW = 16 // GroupMemberH is the logical height of one expanded member row. GroupMemberH = 20 // GroupCheckSize is the logical side length of the download checkbox. GroupCheckSize = 18 )
const ( IsoMinTile = 8.0 IsoMaxTile = 512.0 )
IsoMinTile / IsoMaxTile clamp the tile width so zoom cannot collapse the grid to a point or blow it up unboundedly.
const ( // KanbanHeaderH is the pixel height of a column's header band. KanbanHeaderH = 28 // KanbanCardH is the pixel height of one card. KanbanCardH = 46 // KanbanColGap is the horizontal gap between adjacent columns. KanbanColGap = 8 // KanbanCardGap is the vertical gap between stacked cards (and the // horizontal inset of a card from its column's edges). KanbanCardGap = 6 // KanbanCardPadX is the inner horizontal text inset inside a card / // column header. KanbanCardPadX = 8 // KanbanStripeW is the pixel width of a card's left accent stripe. KanbanStripeW = 4 // KanbanCardRadius is the corner radius of a card's rounded body. KanbanCardRadius = 6 )
Kanban layout constants, exported so a host can measure a board the same way it reads TableRowHeight / CardHeaderH.
const ( KbdPadX = 4 KbdPadY = 2 )
KbdPadX / KbdPadY are the internal margin between the border box and the key text glyphs. Small values keep the chip compact enough to nest inside a menu row.
const ( NotebookTabStripH = 24 NotebookTabWidth = 80 )
Geometry constants for the tab strip: the strip's thickness (its height for a Top/Bottom strip, its width for a Left/Right strip) and each tab's extent along the strip.
const ( NotificationPadX = 12 NotificationPadY = 8 NotificationLife = 180 // NotificationMargin is the gap between a corner-anchored notification // and the host edges. NotificationMargin = 12 )
NotificationPadX / NotificationPadY / NotificationLife are the visual + timing defaults; a caller wanting shorter or louder toasts overrides them per-instance (via SetBounds + direct Life assignment).
const ( PanedHorizontal = 0 // First left, Second right PanedVertical = 1 // First top, Second bottom )
Paned orientations.
const ( PopoverPadX = 8 PopoverPadY = 6 PopoverBorderR = 1 )
Popover sizing constants. PopoverPadX / PopoverPadY are the inner margin between the Popover's outer edge and the child's frame; PopoverBorderR is the border stroke width, matching what strokeRect paints so a host that lays out around the popover can budget the right number of pixels for the frame.
const ( // DefaultPostCardThumbW / DefaultPostCardThumbH are the thumbnail column's // pixel size when ThumbW / ThumbH are left unset. DefaultPostCardThumbW = 72 DefaultPostCardThumbH = 72 // DefaultPostCardTitleLines caps the wrapped title when MaxTitleLines is // unset: a long headline shows at most this many lines before it is cut. DefaultPostCardTitleLines = 3 )
const ( // RatingStarW is the per-cell edge in pixels. RatingStarW = 14 // RatingStarGap is the horizontal spacing between two successive // cells (pixels of surface visible between them). RatingStarGap = 2 // RatingStarPad is the inset (logical px) between a cell's edge and the // star's outer points, so stars don't touch across the gap. RatingStarPad = 1 )
Rating sizing constants. Each star occupies a square cell so the strip reads as an evenly spaced row; the small gap keeps successive stars visually distinct without eating layout width.
const ( // RichEditorToolbarIconSize is the default square edge of each icon button. RichEditorToolbarIconSize = 28 // RichEditorToolbarSpacing is the default inter-button gap. RichEditorToolbarSpacing = 2 // RichEditorToolbarSepW is the width of a group divider cell. RichEditorToolbarSepW = 9 )
Toolbar sizing defaults, in logical pixels.
const ( // SkeletonLineH is the default pixel height of a SkeletonText bar. SkeletonLineH = 10 // SkeletonLineGap is the default vertical gap between two bars. SkeletonLineGap = 6 // SkeletonLinePad is the inset applied to SkeletonBlock so the fill // stops shy of the Bounds edge — matches Card's body pad. SkeletonLinePad = 4 // SkeletonLastFrac is the default width fraction of the last text // bar (60%), so the paragraph terminates naturally. SkeletonLastFrac = 0.6 // SkeletonRectRadius is the default corner radius for SkeletonRect. SkeletonRectRadius = 6 )
Skeleton sizing + shimmer constants. Line values line up with the toolkit's GlyphHeight() so a SkeletonText row visually replaces a row of body text without shifting the surrounding layout.
const ( // SpreadsheetColWidth is the uniform width of a data column. SpreadsheetColWidth = 64 // SpreadsheetRowHeight is the uniform height of a data row. SpreadsheetRowHeight = 20 // SpreadsheetHeaderHeight is the height of the column-letter header band. SpreadsheetHeaderHeight = 20 // SpreadsheetRowHeaderWidth is the width of the row-number header band. SpreadsheetRowHeaderWidth = 36 )
Spreadsheet cell + band metrics, in LOGICAL pixels (scaled to device pixels through scaled() at use, exactly like Table's TableRowHeight etc.).
const ( // StatPadX is the horizontal inset between the border and the // left edge of the Title / Value / Change text. StatPadX = 12 // StatPadY is the vertical inset between the top border and the // first row of Title text (and between the last row of Change // text and the bottom border). StatPadY = 8 // StatTitleGap is the vertical space inserted between the Title // row's bottom and the Value row's top. StatTitleGap = 4 // StatValueGap is the vertical space inserted between the Value // row's bottom and the Change row's top. StatValueGap = 4 )
Stat sizing constants. Padding matches Alert (12, 8) so a Stat composes cleanly next to an Alert banner; the two gap constants keep the three text rows readable at 5x7 glyphs without the tall vertical footprint of a full Card.
const ( StatusbarH = 18 StatusbarSegmentMinW = 80 StatusbarPadX = 6 )
Sizing constants.
const ( // StepBoxW is the pixel width of each badge. StepBoxW = 16 // StepBoxH is the pixel height of each badge. StepBoxH = 16 // StepConnectorW is the horizontal length of the connector line // between two badges. StepConnectorW = 20 // StepLabelGap is the vertical gap between a badge's bottom edge // and the caption text below it. StepLabelGap = 3 )
Steps sizing constants. Chosen so the badges + connectors fit inside a 40-px-tall bar (a common toolbar strip height).
const ( // TabBarHeight is the default bar height when Bounds().H is zero — the // Material bottom-navigation height, comfortably above the 44-px touch // floor at DensityCompact. TabBarHeight = 56 // TabBarIndicatorH is the thickness of the accent indicator bar along the // selected item's top edge. TabBarIndicatorH = 3 // TabBarIconLabelGap is the vertical gap between an item's icon and its // label when both are present. TabBarIconLabelGap = 2 )
Layout constants, all in LOGICAL pixels (routed through scaled at use).
const ( // TimelineMarkerW is the reserved horizontal column width for // the rail + marker before the event's text begins. TimelineMarkerW = 12 // TimelineMarkerSize is the pixel side of each event's filled // square marker painted on the rail. TimelineMarkerSize = 6 // TimelineDetailGap is the vertical space inserted between an // event's Title row and its Detail row when Detail != "". TimelineDetailGap = 2 // TimelinePadX is the horizontal inset between the widget's // left edge and the rail's marker column. TimelinePadX = 8 // TimelinePadY is the vertical inset between the widget's top // edge and the first event row (and between the last event row // and the bottom edge). TimelinePadY = 8 )
Timeline sizing constants. Marker column is 12 px wide, the marker itself 6 px so it sits centred on the rail with a 3-px gutter either side; event rows are one glyph plus a 4-px vertical spacer so successive titles don't touch, and Detail rows sit 2 px below their Title with a matching glyph height.
const ( ToastPadX = 10 ToastPadY = 6 // ToastMargin is the gap between a corner-anchored toast and the host // edges; ToastGap is the vertical space between stacked toasts. ToastMargin = 12 ToastGap = 6 // ToastLineGap is the vertical space between stacked message lines in a // multi-line (Lines) toast. Irrelevant to a single-line toast. ToastLineGap = 2 )
ToastPadX / ToastPadY are the internal margin between the pill edges and the text. Slightly tighter than Notification's 12/8 so several stacked pills read as a compact column.
const ( ToolbarButtonW = 24 ToolbarButtonH = 24 ToolbarSepW = 8 )
Sizing constants. Square buttons read as a true icon-toolbar (vs the MenuBar's wider text cells).
const ( TooltipPadX = 8 TooltipPadY = 4 )
TooltipPadX / TooltipPadY are the inner text-padding constants.
const ( // ViewSwitcherH is the default vertical extent in pixels. ViewSwitcherH = 32 // ViewSwitcherPadX is the horizontal padding at the strip's left // and right edges. Reserved for future asymmetric layouts; the // current segment layout divides the full width evenly. ViewSwitcherPadX = 12 )
Sizing constants for the strip's default vertical extent and its horizontal end padding. ViewSwitcher's Draw does not require Bounds.H == ViewSwitcherH; the constant is exposed so callers building a HeaderBar-like layout can allocate a matching strip.
const ( // WizardStripH is the pixel height of the top Steps strip. WizardStripH = 40 // WizardButtonRowH is the pixel height of the bottom Back/Next/ // Finish button row. WizardButtonRowH = 32 // WizardButtonW is the pixel width of each Back/Next/Finish button. WizardButtonW = 90 // WizardButtonGap is the horizontal gap between a button and the // Wizard's edge (Back hugs the left edge, Next/Finish the right). WizardButtonGap = 8 )
Wizard geometry constants. The strip + button row are fixed-height bands pinned to the top/bottom edges; the Body of the active step fills whatever is left between them (mirroring Notebook's strip-plus-body split in notebook.go).
const AgendaSidebarDoubleClick = "double"
AgendaSidebarDoubleClick is the Event.Code a host tags a double-click EventClick with, mirroring StatusIconSecondary for a right-click: a click carrying this Code opens the inline rename editor on the row under it, while an ordinary click (empty Code) toggles the row's visibility. A host that does not distinguish double-clicks simply never sets it, and rows only ever toggle — the rename editor stays fully opt-in.
const AgendaSidebarRowH = 24
AgendaSidebarRowH is the pixel height of one calendar row.
const AreaFillAlpha = 90
AreaFillAlpha is the opacity (0..255) of the shaded band under each series, so overlapping bands stay legible against one another and the ground.
const AvatarSize = 32
AvatarSize is the default square dimension in pixels when Bounds() is zero-sized. Matches the 32-px avatar most GTK / Material chat rows use so an Avatar drops naturally next to a Label without extra layout.
const BarGutter = 1
BarGutter is the horizontal gap (painter units) between adjacent bars.
const BorderSplitW = 6
BorderSplitW is the pixel thickness of a Border splitter handle (matches PanedHandleW).
const BreadcrumbGap = 4
BreadcrumbGap is the horizontal pixel gap inserted on either side of the separator glyph so the chevron doesn't touch the segment ink.
const BreadcrumbSep = ">"
BreadcrumbSep is the character(s) drawn between two segments. Kept as a package constant so a caller who wants "/" or "»" replaces one symbol without touching Draw.
const ChartPad = 6
ChartPad is the margin (painter units) reserved for the axes on the left and bottom edges of a chart's plot area.
const ContextMenuMinW = 96
ContextMenuMinW is the floor on a context menu's width so a menu of very short labels still reads as a panel.
const DefaultArticleBodyLines = 3
DefaultArticleBodyLines is the body clamp used when BodyLines is unset (zero or negative): a summary shows at most this many lines before it is cut.
const DefaultBoxSpacing = 4
DefaultBoxSpacing is the inter-child gap (in pixels) the box constructors NewHBox/NewVBox/NewBoxLayout seed into their Spacing field. Picked to match the 4-pixel rhythm the rest of the toolkit uses (Frame.Padding, the Button border inset, ...). Because the default lives in the constructor rather than the layout math, Spacing is honoured LITERALLY: a caller who wants a flush, zero-gap box sets Spacing = 0 explicitly; only negative values are clamped (to 0). Containers expose Spacing as a public field so apps can override it before the first SetBounds call.
const DefaultOpenTypeSizePx = 16
DefaultOpenTypeSizePx is the pixel size UseOpenTypeText renders the bundled default face at. It is chosen for comfortable on-screen UI text — clearly larger and more legible than the 7px bitmap default — while staying compact enough for dense chrome (window titles, dock labels, menus). Use UseOpenTypeTextSize (or DefaultOpenTypeFont) for a different size.
const DialogButtonStripH = 32
DialogButtonStripH is the pixel height of the bottom action strip.
const DialogButtonW = 90
DialogButtonW is the width allocated per action button.
const DialogTitleH = 28
DialogTitleH is the pixel height of the title bar.
const DiffPadX = 4
DiffPadX is the horizontal padding between the widget's outer border and the leading prefix glyph.
const DiffPadY = 2
DiffPadY is the vertical padding above the first line and below the last line.
const DropPayloadSep = "\n"
DropPayloadSep separates individual items within a multi-item drag payload.
const ExpanderHeaderH = 24
ExpanderHeaderH is the LOGICAL height of the clickable header row. Use ExpanderHeaderHeight for the height to lay out with: at a metric scale above 1 the header is taller, like every other metric.
const FormFieldChildGap = 4
FormFieldChildGap is the vertical gap in pixels between the bottom of the label row and the top of the composed Child widget.
const FormFieldHelpGap = 2
FormFieldHelpGap is the vertical gap in pixels between the bottom of the Child widget and the top of the help / error caption row.
const FormFieldPadX = 0
FormFieldPadX is the horizontal padding applied on both sides of the FormField body. Kept at 0 by default: a form is expected to live inside a container (VBox, Card, ...) that supplies its own margin. Callers that need extra breathing room can wrap the field in a Card.
const FormFieldPadY = 4
FormFieldPadY is the vertical padding applied at the top + bottom of the FormField body. Small: keeps a stack of fields compact without having every caller compute inter-field spacing.
const FrameTitleH = 22
FrameTitleH is the pixel height of a Frame's optional title bar.
const GaugeThickness = 6
GaugeThickness is the default arc stroke width in LOGICAL pixels, used when Gauge.Thickness is left at its zero value. It leaves room inside the ring for the centred Caption on the toolkit's 5x7 font while keeping the track visually prominent.
const HeaderBarHeight = 40
HeaderBarHeight is the default vertical extent of a HeaderBar in pixels. HeaderBar's Draw code assumes Bounds.H == HeaderBarHeight but scales cleanly for taller / shorter bars: children are inset by HeaderBarPad/2 top+bottom and the title / subtitle are centred in whatever remains.
const HeaderBarPad = 8
HeaderBarPad is the horizontal padding at the bar's left + right edges (space between the bar's edge and the first Start / End child). Also drives the vertical inset around child widgets: children get Bounds.Y = bar.Y + HeaderBarPad/2 and Bounds.H = bar.H - HeaderBarPad, matching GTK's typical inner spacing.
const HeaderBarSubtitleGap = 2
HeaderBarSubtitleGap is the vertical gap (in pixels) between the title's last row and the subtitle's first row in the two-line layout. Kept as a package constant so the two-line block height stays predictable across themes + font sizes.
const IconButtonSize = 28
IconButtonSize is the default square dimension in pixels when Bounds() is zero-sized. Matches the 28-px toolbar icon buttons GTK / Aqua headers use so an IconButton drops naturally next to a Label or a Button without extra layout maths.
const IsoIconDragPrefix = "iso-icon:"
IsoIconDragPrefix namespaces an IsoIconPalette drag payload. A palette that starts a drag carries "iso-icon:<id>" (see EncodeIsoIconPayload) so an IsoDiagram can tell an icon drop from any other drag reaching the canvas.
const IsoSchemaVersion = 1
IsoSchemaVersion is the version stamped into every document this build writes and the only version it reads. It is OUR native format — designed here, not borrowed from any other isometric editor — so the number tracks this schema alone. A file carrying any other version is rejected by UnmarshalIsoDocument with a clear error rather than silently mis-read. Additive field changes stay within one version: a reader loads a missing field as its zero value (an older file that predates, say, layers has no "layer" keys and every entity lands on the implicit default layer), so the version only bumps on a breaking change.
const IsoZoomStep = 1.1
IsoZoomStep is the multiplicative zoom applied per wheel notch.
const ListRowDragPrefix = "listrow:"
ListRowDragPrefix is the payload scheme ListBox's drag-to-reorder gesture uses: DragData returns ListRowDragPrefix followed by the pressed row's decimal index, and AcceptsDrop only recognizes payloads carrying this prefix -- so a foreign payload (say, a file path offered to a DropZone) is never mistaken for a reorder drag.
const LoadMaskSpinnerSize = 32
LoadMaskSpinnerSize is the pixel side of a LoadMask's centred spinner.
const MenuBarH = 22
MenuBarH is the pixel height of the bar strip.
const MenuBarItemPadX = 8
MenuBarItemPadX is the horizontal padding around a top-level name when its natural width exceeds MenuBarItemW — i.e. the extra breathing room beyond the raw glyph run.
const MenuBarItemW = 60
MenuBarItemW is the DEFAULT (minimum) pixel width allocated per top-level name. Names whose TextWidth exceeds this bound scale up (with 2×MenuBarItemPadX horizontal padding on each side); shorter names take exactly this width so the bar looks stable across varying label lengths.
const MenuCheckGutterW = 14
MenuCheckGutterW is the pixel width of the left-hand gutter that holds a checkable/radio row's ✓ or • glyph. A Menu only reserves this gutter (shifting every row's label right) when at least one of its Items is checkable or belongs to a radio group; a Menu with no such items lays out exactly as before this feature (label starts at the plain 8px inset), so plain menus render unchanged.
const MenuMinW = 96
MenuMinW is the floor width a submenu popover sizes to (see preferredSize) so a child of very short labels still reads as a panel.
const MenuRowH = 22
MenuRowH is the pixel height of a menu row.
const MenuSeparatorH = 6
MenuSeparatorH is the height of a separator row.
const PaginationBtnH = 24
PaginationBtnH is the pixel height of each button.
const PaginationBtnW = 28
PaginationBtnW is the pixel width of each button (prev, next, and every page number).
const PaginationGap = 2
PaginationGap is the horizontal pixel gap between successive buttons.
const PagingBtnH = 24
PagingBtnH is the pixel height of the toolbar (and each button).
const PagingBtnW = 26
PagingBtnW is the pixel width of each toolbar button.
const PagingGap = 2
PagingGap is the horizontal pixel gap between successive buttons.
const PaletteMaxRows = 12
PaletteMaxRows caps how many result rows the panel shows at once; a broader query's remaining matches are reachable by scrolling (see scroll), so the panel never grows taller than one query row plus PaletteMaxRows results.
const PaletteMinW = 240
PaletteMinW is the floor on the panel width so a palette of short labels still reads as a dialog.
const PalettePadX = 8
PalettePadX is the horizontal padding between the panel border and its text content.
const PaletteRowH = 18
PaletteRowH is the pixel height of every row (the query row and each result row).
const PanedHandleW = 6
PanedHandleW is the pixel thickness of the splitter handle.
const PopoverMaxRows = 12
PopoverMaxRows caps the dropdown popover height; longer option lists are reachable by scrolling the popover (see popScroll).
const PopoverRowH = 18
PopoverRowH is the pixel height of one option row in the popover.
const ProgressCircleSize = 40
ProgressCircleSize is the default side-length in pixels of a ProgressCircle rendered with a zero-sized Bounds. Roughly matches the "large" circular-progress indicator in Material / Adwaita dashboards; small enough to sit next to a status label yet big enough for the "XX%" caption to read on the toolkit's 5x7 font.
const ProgressCircleStroke = 4
ProgressCircleStroke is the ring thickness in pixels: the offset between the outer track square and the inner "hole" that carries the percentage caption. A thicker stroke leaves less room for the text; 4px keeps a two-digit percentage centred inside the ring with pixels to spare on either side.
const RadarRings = 4
RadarRings is the number of concentric grid rings drawn between the centre and the outer edge.
const ScatterDot = 2
ScatterDot is the side (painter units) of the square marker drawn per point.
const SearchEntryIconW = 16
SearchEntryIconW is the pixel width reserved for the leading prefix glyph and the trailing clear affordance. Both slots share the same width so hit-testing stays symmetric.
const SearchEntryPadX = 4
SearchEntryPadX is the horizontal padding between the widget's outer border and the inner content (the search prefix, the text field, the clear affordance).
const SourceRowDragPrefix = "sourcerow:"
SourceRowDragPrefix is the payload scheme a SourceList reorder drag carries: DragData returns this prefix followed by "<section>:<row>", and AcceptsDrop recognizes only payloads bearing it, so a foreign drag is never mistaken for a row reorder.
const SparkPad = 1
SparkPad is the inset (painter units) reserved on every edge so the line's endpoints and the tallest bar never bleed against the widget's border.
const SplitButtonArrowW = 20
SplitButtonArrowW is the pixel width of the arrow slot on the right edge when Arrow is true. Sized to comfortably fit the 5x7 arrow glyph plus symmetric padding on either side.
const SplitButtonPadX = 12
SplitButtonPadX is the horizontal padding a caller should reserve on either side of the label when positioning a sibling widget flush with the main slot's inner edge. The label itself is rendered centred; this constant is exported for external layout code that wants to align against the same inset.
const StatusAreaGap = 4
StatusAreaGap is the default horizontal spacing in pixels between the icons in a StatusArea when Gap is left at its zero value.
const StatusIconSecondary = "right"
StatusIconSecondary is the Event.Code a host tags a secondary (right / menu) click with so a StatusIcon can route it to OnRightClick. A primary click carries any other Code (typically ""). This mirrors how other widgets read Event.Code to distinguish an activation variant without a dedicated EventKind — the compositor sets it when the secondary mouse button (or a long-press) produced the click.
const StatusIconSize = 18
StatusIconSize is the default square dimension in pixels when Bounds() is zero-sized. 18 px sits in the 16-22 px band a desktop status area (the GNOME top-bar tray, the macOS menu-bar extras) gives each indicator, so a StatusIcon drops next to a clock or a Label without extra layout maths.
const TableCellPadX = 4
TableCellPadX is the left/right pixel padding applied inside every header + body cell before its text lands.
const TableDoubleClick = "double"
TableDoubleClick is the Event.Code a host tags onto an EventClick to mark it a double-click, so a Table in EditOnDoubleClick mode can tell the second click of a double-click from a fresh single click without the toolkit growing a dedicated double-click EventKind.
const TableHeaderHeight = 24
TableHeaderHeight is the pixel height of the header row.
const TableIconSize = 16
TableIconSize is the pixel width+height of the square rect a per-row leading icon (see Table.RowIcon) is painted into -- sized to sit comfortably inside a TableRowHeight-tall body row with a little vertical breathing room.
const TableRowHeight = 22
TableRowHeight is the pixel height of one body row.
const ThumbnailLabelPad = 2
ThumbnailLabelPad is the vertical padding above + below the caption text in the label strip.
const TreeChevronW = 14
TreeChevronW is the pixel column the chevron lives in.
const TreeIndentW = 16
TreeIndentW is the per-depth pixel indent.
const TreeTableHeaderHeight = 24
TreeTableHeaderHeight is the pixel height of the header row.
const TreeTableRowHeight = 22
TreeTableRowHeight is the pixel height of one body row.
const WindowTitleH = 24
WindowTitleH is the pixel height of the title-bar band.
Variables ¶
var ErrCellOutOfRange = errors.New("toolkit: spreadsheet cell out of range")
ErrCellOutOfRange reports a positional cell access outside the sheet's current row/column extent. It is the one error the cell methods raise; the underlying CRDT writes cannot fail once an in-range identity is resolved.
var ErrConflict = errors.New("toolkit: chord already bound in this scope")
ErrConflict is returned by Keymap.Bind / Keymap.Rebind when the target chord is already bound to a different action in the same scope.
var ErrEmptyAction = errors.New("toolkit: empty action id")
ErrEmptyAction is returned when a bind is attempted with an empty action id.
var ErrEmptyChord = errors.New("toolkit: empty chord")
ErrEmptyChord is returned when a bind is attempted with a zero-length chord.
var IsoAnimatedIconIDs = []string{
"anim/cloud",
"anim/database",
"anim/gear",
"anim/server",
"anim/spinner",
}
IsoAnimatedIconIDs are the ids of the built-in procedural animated icons that RegisterAnimatedIcons installs. They are namespaced under "anim/" so they never collide with the still built-ins, and are opt-in: the default registry does NOT carry them until a host registers them.
var IsoBuiltinIconIDs = []string{
"server", "rack", "cloud", "database",
"router", "switch", "storage", "box", "user",
}
IsoBuiltinIconIDs are the ids of the primitive-composed architecture icons the default registry ships with. They are stylised, code-authored isometric icons — not traced from any third-party art.
Functions ¶
func CardFooterH ¶ added in v0.7.0
func CardFooterH() int
CardFooterH is the height of the footer strip when Footer != "".
func CardHeaderH ¶ added in v0.7.0
func CardHeaderH() int
CardHeaderH is the height of the header strip when Title != "" (a function, as it derives from the active font's GlyphHeight and the metric-scaled pad).
func ClipboardText ¶ added in v0.42.0
func ClipboardText() string
ClipboardText returns the active clipboard's contents. Convenience shorthand for CurrentClipboard().ClipboardText().
func ColorPickerNaturalSize ¶ added in v0.166.0
func ColorPickerNaturalSize() (w, h int)
ColorPickerNaturalSize is the picker's footprint in device pixels at the current MetricScale.
func DatePickerFieldH ¶ added in v0.11.0
func DatePickerFieldH() int
DatePickerFieldH is the pixel height of the closed field: a glyph row plus a scaled vertical padding, so the field grows with HiDPI and touch density.
func DaysInMonth ¶
DaysInMonth returns the day count for (year, month).
func DecodeIsoIconPayload ¶ added in v0.206.0
DecodeIsoIconPayload recovers the icon id from a drop payload, scanning the (possibly multi-item, newline-separated — see SplitDropPayload) payload for the first "iso-icon:<id>" item with a non-empty id. It returns "" and false when the payload carries no icon, so a diagram accepts and acts on exactly the drags a palette originates.
func DeleteSelection ¶
DeleteSelection removes the selected range from lines + returns the new lines slice. The result always has at least one line (an empty line at minimum).
func DiffLineH ¶ added in v0.8.0
func DiffLineH() int
DiffLineH is the vertical stride between successive lines: one glyph tall plus two pixels of separation.
func DrawIconCopy ¶
DrawIconCopy paints two overlapping document outlines.
func DrawIconCut ¶
DrawIconCut paints a pair-of-scissors icon (two open-circle handles + crossed blades).
func DrawIconNew ¶
DrawIconNew paints a document-outline icon (rectangle with a folded top-right corner).
func DrawIconOpen ¶
DrawIconOpen paints a folder-outline icon (rectangle with a small tab on the top-left).
func DrawIconPaste ¶
DrawIconPaste paints a clipboard outline with a clip on top.
func DrawIconRedo ¶
DrawIconRedo paints a curved arrow pointing right (mirror of Undo).
func DrawIconSave ¶
DrawIconSave paints a floppy-disk-outline icon (outer square with a small label rectangle on top).
func DrawIconSearch ¶
DrawIconSearch paints a magnifying-glass icon (a circle + a diagonal handle).
func DrawIconSettings ¶
DrawIconSettings paints a gear-outline icon (approximated as a square with corner "teeth").
func DrawIconUndo ¶
DrawIconUndo paints a curved arrow pointing left (approximated as a horizontal stroke + a triangular head).
func DrawText ¶
DrawText paints text left-to-right starting at (x, y) in widget-local coordinates, using the active font (see SetFont). It is a thin wrapper over the active Font's Draw so every widget's text rendering follows a font swap.
func EaseInCubic ¶ added in v0.35.0
EaseInCubic starts slow and accelerates towards the end (t^3), more pronounced than EaseInQuad.
func EaseInOutCubic ¶ added in v0.35.0
EaseInOutCubic accelerates through the first half and decelerates through the second half, using EaseInCubic then EaseOutCubic symmetrically, more pronounced than EaseInOutQuad.
func EaseInOutQuad ¶ added in v0.35.0
EaseInOutQuad accelerates through the first half and decelerates through the second half, using EaseInQuad then EaseOutQuad symmetrically.
func EaseInQuad ¶ added in v0.35.0
EaseInQuad starts slow and accelerates towards the end (t^2).
func EaseOutCubic ¶ added in v0.35.0
EaseOutCubic starts fast and decelerates towards the end (1-(1-t)^3), more pronounced than EaseOutQuad.
func EaseOutQuad ¶ added in v0.35.0
EaseOutQuad starts fast and decelerates towards the end (1-(1-t)^2).
func EncodeIsoIconPayload ¶ added in v0.206.0
EncodeIsoIconPayload builds the drag payload for icon id — the string an IsoIconPalette returns from DragData and an IsoDiagram decodes on drop.
func ExpanderHeaderHeight ¶ added in v0.162.0
func ExpanderHeaderHeight() int
ExpanderHeaderHeight is the header height in device pixels at the current MetricScale and touch Density: the scaled ExpanderHeaderH clamped UP to the density minimum hit target via TouchTarget. The header is the clickable row, so at DensityTouch it grows to the finger floor (>=44 device px); under the default DensityCompact the clamp is a pass-through, so at MetricScale 1 it is exactly the historical raw ExpanderHeaderH. Accordion shares this height for its own header rows.
func FolderTabsHeight ¶ added in v0.211.0
func FolderTabsHeight() int
FolderTabsHeight is the recommended strip height in device pixels at the current MetricScale and touch Density: the scaled FolderTabsH clamped UP to the density minimum hit target via TouchTarget. A tab is a click target, so at DensityTouch the recommended height reaches the finger floor; under the default DensityCompact the clamp is a pass-through. A host allocating a strip sizes it to this.
func FormFieldLabelH ¶ added in v0.9.0
func FormFieldLabelH() int
FormFieldLabelH is the height in pixels of the label row drawn at the top of a FormField. One glyph row plus a scaled 2px of breathing space keeps the label snug against the input beneath it without touching the glyph's descender pixels, and grows with HiDPI / touch density.
func GlyphAdvance ¶
func GlyphAdvance() int
GlyphAdvance is the active font's horizontal step from one glyph to the next.
func GlyphHeight ¶
func GlyphHeight() int
GlyphHeight is the active font's glyph box height. It is a function (not a const) so widgets re-read it after SetFont; layout dimensions that derive from it are likewise functions.
func JoinDropPayload ¶ added in v0.16.0
JoinDropPayload builds a multi-item payload string from items, the inverse of SplitDropPayload — a host uses it to package several dragged paths into one Event.Code.
func LookupAs ¶ added in v0.62.0
func LookupAs[T Widget](vc *ViewController, name string) (val T, ok bool)
LookupAs returns the widget tagged with Ref(name) as type T. ok is false when the name is absent or the widget is not a T — the typed lookup by name.
func MarshalIsoDocument ¶ added in v0.202.0
func MarshalIsoDocument(doc IsoDocument) ([]byte, error)
MarshalIsoDocument serialises a whole IsoDocument — every node, connector, zone, text annotation and layer — to our native, versioned JSON schema. The output is deterministic (entities sorted by id, keys in a fixed order), so two runs on the same document produce identical bytes and a re-serialised import round-trips byte-for-byte. It carries no viewport: the grid extent and pan/zoom are view state, not part of the shared document.
func MetricScale ¶ added in v0.159.0
func MetricScale() float64
MetricScale returns the current global metric scale (1.0 by default).
func MinHitTarget ¶ added in v0.184.0
func MinHitTarget() int
MinHitTarget returns the current minimum hit dimension in DEVICE pixels: the density's logical floor scaled by MetricScale only (NOT by [densityFactor]). It is 0 under DensityCompact, so nothing is clamped on the desktop. Under DensityTouch it is 44 logical pixels — 44 at MetricScale 1, 88 at MetricScale 2 — so a fingertip always lands a large-enough target regardless of panel DPI.
func RegisterAnimatedIcons ¶ added in v0.219.0
func RegisterAnimatedIcons(r *IsoIconRegistry)
RegisterAnimatedIcons installs the built-in animated icons (IsoAnimatedIconIDs) into r. It is opt-in — a host calls it on the default registry (IsoDefaultIcons) or on a per-widget one to make the "anim/*" ids resolvable. Registering is idempotent (it replaces under the same ids).
func RegisterIcon ¶ added in v0.200.0
RegisterIcon registers icon under id in the default registry (IsoDefaultIcons).
func RegisterIconPack ¶ added in v0.200.0
func RegisterIconPack(p IsoIconPack)
RegisterIconPack registers a whole pack in the default registry.
func RenderImage ¶ added in v0.39.0
RenderImage is the toolkit's headless "screenshot" path: it renders any Widget into an in-memory *image.RGBA instead of a live pixel surface. The same Draw call a window or wasmbox surface would trigger runs here against a throwaway PixelPainter, so a widget doesn't need to know it's being captured rather than displayed. Callers use this to save a widget's appearance to disk, embed it in a generated report, or assert on pixels in a test.
width and height must both be positive; a widget with zero (or negative) extent has no pixels to capture. On success the returned image is exactly width x height, with Background painted first so any pixel the widget itself doesn't touch (e.g. outside its own drawn shape) still comes out as the theme's canvas colour rather than transparent black.
func RenderPNG ¶ added in v0.39.0
RenderPNG renders w exactly as RenderImage does, then encodes the result as a PNG. It is the one-call path from a widget to bytes suitable for os.WriteFile, an HTTP response body, or embedding in a document.
png.Encode into an in-memory bytes.Buffer does not fail in practice (it has no I/O to fail on), so its error is simply propagated rather than wrapped — the only error this function can realistically surface is RenderImage's dimension check.
Encode runs as its own statement (not inlined into the return) so buf is fully populated before buf.Bytes() is evaluated: Go evaluates a return statement's operands left to right, and inlining would capture buf.Bytes() while the buffer was still empty.
func Scaled ¶ added in v0.159.0
Scaled is the exported form of [scaled]: it rounds a base (logical-pixel) metric to device pixels at the current MetricScale. Sibling packages that compose these widgets (e.g. the virtual list feed) route their own fixed metrics through it so they scale in lockstep with the core widgets instead of re-deriving the rounding.
func SelectionText ¶
SelectionText returns the substring covered by sel in lines (a TextView's Lines slice). Empty selection returns "".
func SetClipboard ¶ added in v0.42.0
func SetClipboard(c Clipboard)
SetClipboard installs c as the toolkit-wide active clipboard. Every widget's copy/cut/paste operation goes through c from this point on. Passing nil restores the default in-memory clipboard.
func SetClipboardText ¶ added in v0.42.0
func SetClipboardText(s string)
SetClipboardText replaces the active clipboard's contents. Convenience shorthand for CurrentClipboard().SetClipboardText(s).
func SetDensity ¶ added in v0.184.0
func SetDensity(d DensityLevel)
SetDensity sets the global touch profile. An out-of-range value is ignored, so the density never lands on an undefined level (mirroring how SetMetricScale ignores a non-positive scale).
func SetFont ¶ added in v0.20.0
func SetFont(f Font)
SetFont makes f the active font. A nil f gives the built-in bitmap back, at whatever the current MetricScale is. All subsequent layout (GlyphHeight / GlyphAdvance) and DrawText use it.
Example ¶
ExampleSetFont swaps the active font; every widget re-lays-out against the new metrics. NewBitmapFont(2) doubles the built-in bitmap ("retina" text).
package main
import (
"fmt"
"github.com/go-widgets/toolkit"
)
func main() {
fmt.Println(toolkit.GlyphHeight()) // default 5×7 bitmap
toolkit.SetFont(toolkit.NewBitmapFont(2))
fmt.Println(toolkit.GlyphHeight()) // doubled
toolkit.SetFont(nil) // restore the default
fmt.Println(toolkit.GlyphHeight())
}
Output: 7 14 7
func SetMetricScale ¶ added in v0.159.0
func SetMetricScale(f float64)
SetMetricScale sets the global metric scale. A non-positive value is ignored, so the scale never collapses metrics to zero.
func SetTextDirection ¶ added in v0.43.0
func SetTextDirection(d TextDirection)
SetTextDirection makes d the base direction used by DrawText and the font Draw paths when they reorder logical text to visual order. It affects only how mixed / right-to-left text is arranged; all-LTR text is untouched under the default DirLTR.
func SplitDropPayload ¶ added in v0.16.0
SplitDropPayload splits a drop payload (as carried in Event.Code) into its individual items, dropping empty entries so a trailing separator or an empty payload yields no phantom items.
func SyncSpreadsheet ¶ added in v0.203.0
func SyncSpreadsheet(ss *Spreadsheet, cs *CollabSheet) (detach func())
SyncSpreadsheet wires a Spreadsheet widget onto a CollabSheet without changing the widget's API: the widget renders and edits exactly as before, but its committed edits now flow into the collaborative model and a merged remote edit flows back into the widget. It is the observable adaptation layer the collaborative model exposes to the existing widget.
On call it mirrors the collab sheet's current cells into the widget, then subscribes so every later change — local or remote — re-mirrors, and routes the widget's Spreadsheet.OnCellChange into CollabSheet.SetCellText. Only the overlapping (column, row) window is mirrored, so a widget smaller than the collaborative sheet shows its top-left corner and never addresses a cell it has no room for. The returned detach unsubscribes and clears the widget's change hook.
func TextWidth ¶
TextWidth returns the pixel width that DrawText would occupy if it rendered text in the active font. It defers to the active font's Measure so a proportional font (see NewTrueTypeFont) reports its true rendered width; the built-in bitmap font is monospace, so it still equals len(text)*GlyphAdvance.
func TickTree ¶ added in v0.155.0
TickTree advances every Animator in the tree rooted at root by dt seconds.
It descends into any widget exposing its children via [childContainer] — the same walk WalkA11y and CollectRuns use — so one call drives a whole composed UI. The root itself is ticked when it is an Animator, and the walk still descends into it (a widget can be both an Animator and a container). A nil root (or a nil child a container might yield) is skipped, so callers need not guard the tree they hand in.
func TimelineEventH ¶ added in v0.9.0
func TimelineEventH() int
TimelineEventH is the vertical stride from one event's Title row to the next when the event has NO Detail — one glyph row plus 4 px of inter-event spacing. A function, as it derives from the active font's GlyphHeight.
func TouchTarget ¶ added in v0.184.0
TouchTarget clamps an interactive control's computed hit dimension (already in device pixels) UP to MinHitTarget, leaving anything already large enough untouched. Under DensityCompact the minimum is 0, so it is a pass-through and the control's hit rect is byte-identical to its drawn bounds. A widget uses it on each axis of its clickable region, e.g.:
hitW := TouchTarget(r.W) hitH := TouchTarget(r.H)
then centres the (possibly larger) hit rect over its visual bounds — see Switch.HitRect for a worked example. Clamping the HIT area, not the drawn pixels, keeps a small control visually unchanged while giving a finger the reach the platform requires.
func TreeAnimating ¶ added in v0.155.0
TreeAnimating reports whether at least one Animator in the tree rooted at root still needs frames (its Animating returns true).
It descends through [childContainer] exactly like TickTree, and short-circuits: the first still-animating widget found ends the walk, because a host only needs to know that *something* wants another frame, not how many. A nil root (or a nil child) contributes nothing, so an empty or partly-built tree simply reports false.
func UseOpenTypeText ¶ added in v0.77.0
func UseOpenTypeText() error
UseOpenTypeText switches the toolkit's active font from the built-in 5x7 bitmap to anti-aliased, shaped OpenType text — the bundled Atkinson Hyperlegible face at DefaultOpenTypeSizePx — in a single call. After it, every widget (window titles, dock, menus, HUD, …) re-lays-out and repaints against the vector face without any further per-widget wiring.
Call it once at start-up. It returns any parse error (the bundled face never produces one) and leaves the active font unchanged in that case, so a failure degrades to the still-working bitmap default rather than to no text. Restore the bitmap default at any time with SetFont(nil).
func UseOpenTypeTextSize ¶ added in v0.77.0
UseOpenTypeTextSize is UseOpenTypeText at an explicit pixel size — for apps (or high-DPI surfaces) that want AA text larger or smaller than DefaultOpenTypeSizePx. The active font is only swapped on success; on a parse error it is left untouched and the error is returned.
func Validate ¶ added in v0.42.0
Validate runs rules against value in order and returns the first non-nil error. It returns nil when every rule passes, including when rules is empty.
func ViewSwitcherHeight ¶ added in v0.187.0
func ViewSwitcherHeight() int
ViewSwitcherHeight is the recommended strip height in device pixels at the current MetricScale and touch Density: the scaled ViewSwitcherH clamped UP to the density minimum hit target via TouchTarget. A segment is a tap target, so at DensityTouch the recommended height reaches the finger floor (>=44 device px); under the default DensityCompact the clamp is a pass-through, so at MetricScale 1 it is exactly the historical raw ViewSwitcherH. A host allocating a strip (a HeaderBar-like layout) sizes it to this so its segments are large enough to tap.
func WeekdayOfFirst ¶
WeekdayOfFirst returns the weekday-index (0=Mon..6=Sun) of the first day of (year, month). Uses Zeller-ish congruence so we don't depend on time.Time.
Types ¶
type A11yInfo ¶ added in v0.19.0
type A11yInfo struct {
Role Role
Name string
Value string
// HasRange reports whether Min, Max and Now carry a meaningful reading.
HasRange bool
// Min, Max and Now are the numeric range and current position of a
// range-valued control, valid only when HasRange is true.
Min, Max, Now float64
}
A11yInfo is a widget's accessibility description. Name is the accessible name (its label/caption); Value is the current value where meaningful (a textbox's text, a checkbox's "checked"/"" state, a slider's number).
Range-valued controls (sliders, spin buttons, ratings, progress/level bars, gauges) additionally expose a machine-readable numeric reading via Min, Max and Now — the WAI-ARIA aria-valuemin / aria-valuemax / aria-valuenow triple. HasRange gates them: it is true only when the widget populated all three, so a consumer can tell a genuine 0 (Now on an empty slider) from an unset field. A single bool rather than three *float64 pointers keeps A11yInfo a comparable value type — == / != still work, as the table-driven tests rely on — and no existing caller is affected, since every widget that omits a range leaves HasRange false and the three floats at their zero value.
func CollectA11y ¶ added in v0.19.0
CollectA11y returns the A11yInfo for every MEANINGFUL widget in the slice, preserving order. A host owns its widget tree, so it passes the flat list it composed.
Two kinds of widget are skipped, for two different reasons. One that does not implement Accessible has nothing to say. One that reports RolePresentation has deliberately said it is layout or decoration — a box, a scrim, a scrollbar — and ARIA's role="presentation" means exactly "look through me to the content inside". Either way the result is the list a reader should announce, with no structural furniture in it.
The distinction matters to the toolkit rather than to callers: every widget now answers A11y(), so "described as presentational" and "never described" are no longer the same silence, even though both are filtered out here.
Example ¶
ExampleCollectA11y walks a host-composed widget list and returns each Accessible widget's role, name, and value for a screen-reader bridge.
package main
import (
"fmt"
"github.com/go-widgets/toolkit"
)
func main() {
widgets := []toolkit.Widget{
toolkit.NewButton("Save", nil),
toolkit.NewCheckButton("Wrap", true),
}
for _, info := range toolkit.CollectA11y(widgets) {
fmt.Printf("%s %q %q\n", info.Role, info.Name, info.Value)
}
}
Output: button "Save" "" checkbox "Wrap" "checked"
type A11yNode ¶ added in v0.135.0
type A11yNode struct {
A11yInfo
// Rect is the element's placement in SURFACE coordinates.
//
// It is [Widget.Bounds] verbatim, because bounds in this toolkit are
// already absolute rather than parent-relative — [translateEvent] converts
// a parent-local event to child-local by `ev.X + parentRect.X -
// childRect.X`, which only holds when both rectangles share the surface's
// origin. Accumulating offsets during the walk, the obvious reading of
// "placement within its parent surface", would double every position.
Rect Rect
}
A11yNode is one accessible element together with WHERE it is.
CollectA11y answers what a tree contains; this answers where each thing is, which is the other half every platform accessibility API asks for. A screen reader draws the focus ring, routes a touch or moves the pointer from this rectangle, so an element described without one can be read but not pointed at.
func WalkA11y ¶ added in v0.135.0
WalkA11y returns every meaningful element of the tree rooted at w, each with its bounds, in visual order.
It descends through any widget that exposes its children via [childContainer] — the same convention CollectRuns uses — so a host does not have to keep a flat list of everything it composed, which is what CollectA11y requires and what a deeply nested layout makes impractical.
Widgets reporting RolePresentation are skipped exactly as CollectA11y skips them: ARIA's role="presentation" means "look through me to the content inside", so a box, a scrim or a scrollbar contributes nothing to announce — but the walk still descends INTO it, because its children usually do.
Nothing else is filtered here. Whether an unnamed or zero-area element is worth publishing is a decision for the platform bridge consuming this, which knows what its own screen reader does with one.
type Accelerator ¶ added in v0.151.0
Accelerator is a single key combination: a base Key plus the four desktop modifier flags. It is the atom a Keymap binds and an Event is matched against.
Key is a canonical key name — a single upper-case letter ("P"), a digit ("1"), a punctuation rune ("/"), or a named key ("Enter", "ArrowLeft", "F1", "Escape"). The modifier flags mirror Event: Ctrl/Shift are the two common modifiers, Alt is the Option (⌥) / Alt key, and Meta is the Command (⌘) / Super (Windows/logo) key. Two accelerators are equal only when the Key and all four flags match, so Ctrl+P, Shift+Ctrl+P and plain P are three distinct accelerators.
func AcceleratorFromEvent ¶ added in v0.151.0
func AcceleratorFromEvent(ev Event) (Accelerator, bool)
AcceleratorFromEvent derives the Accelerator a key event represents, reporting ok=false for any non-keyboard event or a keyboard event with an empty Code. It reads EventKeyDown and EventChar, canonicalising Code the same way ParseAccelerator canonicalises a key token, so a binding parsed from a string matches an event delivered by a host.
func MustParseAccelerator ¶ added in v0.151.0
func MustParseAccelerator(s string) Accelerator
MustParseAccelerator is ParseAccelerator that panics on error, for package-level accelerator literals known to be valid at author time.
func ParseAccelerator ¶ added in v0.151.0
func ParseAccelerator(s string) (Accelerator, error)
ParseAccelerator parses a human accelerator string ("Ctrl+Shift+P", "Meta+K", "Alt+Left", "Ctrl++") into an Accelerator. Segments are split on '+'; every segment but the last is a modifier (ctrl/control, shift, alt/opt/option, meta/cmd/command/super/win) and the last is the key. A trailing '+' denotes the '+' key itself ("Ctrl++" = Ctrl and the plus key). Modifier and key spelling are case-insensitive. An empty string, an unknown modifier, or a missing key returns an error.
func (Accelerator) String ¶ added in v0.151.0
func (a Accelerator) String() string
String renders the accelerator in canonical Ctrl+Shift+Alt+Meta+Key order, the inverse of ParseAccelerator (aliases resolved), suitable as a menu or tooltip hint.
type Accessible ¶ added in v0.19.0
Accessible is implemented by widgets that expose accessibility metadata.
type Accordion ¶ added in v0.35.0
type Accordion struct {
Base
Sections []AccordionSection
Multiple bool
// contains filtered or unexported fields
}
Accordion is a vertical stack of AccordionHeaderH-tall header rows, each owning a collapsible body below it. By default the sections are mutually exclusive (exclusive-accordion behaviour): clicking a header expands it + collapses any other expanded section. Setting Multiple lets every header toggle independently instead.
The expanded state (used when Multiple is false) is the index of the single expanded section, or -1 when every section is collapsed. It is MVVM-only, exposed via Accordion.Expanded: a host binds the Observable rather than setting a field. In Multiple mode it is ignored + each section's open/closed state is tracked independently.
The remaining vertical space below all headers (Bounds().H minus every header's height) is shared evenly among the currently expanded sections, mirroring how Expander gives its Content the full remaining space when there is only one section.
func NewAccordion ¶ added in v0.35.0
func NewAccordion(sections []AccordionSection) *Accordion
NewAccordion builds an Accordion with every section collapsed.
func (*Accordion) A11y ¶ added in v0.40.0
A11y reports the Accordion as a group carrying the titles of every currently-expanded section (one in single mode, zero or more in Multiple mode), joined together.
func (*Accordion) Children ¶ added in v0.137.0
Children yields the section bodies, in section order.
func (*Accordion) Draw ¶ added in v0.35.0
Draw paints every section's header (title + disclosure chevron) + the body of every currently expanded section, clipped to its share of the remaining space.
func (*Accordion) Expanded ¶ added in v0.35.0
func (a *Accordion) Expanded() *mvvm.Observable[int]
Expanded is the index of the single expanded section as a shared mvvm.Observable (or -1 when every section is collapsed, in exclusive mode): a host binds it (Set / Subscribe / two-way) -- there is no settable Expanded field. A header click or an Enter/Space key press Sets it; subscribers are notified. In Multiple mode the per-section state is tracked separately and this Observable is not consulted. The lazy default is the int zero value 0 (matching a bare &Accordion{}); NewAccordion initialises it to -1.
func (*Accordion) Focused ¶ added in v0.102.0
func (f *Accordion) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*Accordion) OnEvent ¶ added in v0.35.0
OnEvent: a click on header i toggles it (see toggle); a click inside an expanded section's body forwards to that Body via translated coordinates. While focused, Enter/Space toggles the focused header and Up/Down move the header focus between sections. Anything else (other non-click events, or a click that lands on neither a header nor an open body) is a no-op.
Scroll model: a section body is a host-supplied Widget occupying its allotted rect, so the Accordion does not itself scroll a body's contents. Instead a tall body is clipped cleanly to its rect (see Draw) and the mouse wheel (EventScroll) over an expanded body is forwarded to that body -- translated into its local frame -- so a scrollable child (a ListBox, a ScrollView, ...) scrolls itself. A wheel event over a header, a collapsed section, or dead space is ignored.
func (*Accordion) SetFocused ¶ added in v0.102.0
func (f *Accordion) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
type AccordionSection ¶ added in v0.35.0
AccordionSection is one titled, collapsible slot in an Accordion: a header row showing Title + a body Widget shown only while the section is expanded.
type Action ¶ added in v0.151.0
type Action struct {
// ID is the stable identifier a [Keymap] binds and an [ActionRegistry]
// keys on. It must be non-empty to register.
ID string
// Label is the human-readable text shown in menus, buttons and the
// palette.
Label string
// Icon, when set, draws the action's glyph into rect r in colour ink —
// the same callback shape as [Button.Icon]. Optional (nil draws no
// glyph).
Icon func(p painter.Painter, r Rect, ink RGBA)
// Shortcut is the action's declared default accelerator (single- or
// multi-stroke). Register it into a [Keymap] with
// [ActionRegistry.BindDefaults] or [Keymap.Bind]; the Keymap, not this
// field, is the live source of truth once rebinding is allowed.
Shortcut Chord
// Run is the command body, invoked by [Action.Execute] when the action
// is enabled. Nil makes the action a non-running placeholder.
Run func()
// Enabled gates whether the action runs and reads as active; Visible
// gates whether surfaces show it at all. Both are observable.
Enabled *mvvm.Observable[bool]
Visible *mvvm.Observable[bool]
}
Action is a single named command — the one source of truth that a menu item, a toolbar button, a command-palette entry and a keyboard shortcut all point at. Define it once, then surface it in every affordance via the converters (Action.MenuItem, Action.ToolbarButton) and register it with an ActionRegistry (which feeds a CommandPalette) and a Keymap (which binds its accelerator); flipping Action.Enabled or Action.Visible then updates every surface at once.
Enabled and Visible are mvvm.Observable values so app state can drive them through the go-widgets MVVM layer — an Observable[bool] on a view model binds straight onto them — and any surface observing them repaints on change. NewAction initialises both to true; the fields also default to a usable nil (treated as "enabled"/"visible") so a zero-value literal still runs.
func NewAction ¶ added in v0.151.0
NewAction builds an enabled, visible action with the given id, label and command body.
func (*Action) CanRun ¶ added in v0.151.0
CanRun reports whether Execute would run: the action has a body and is enabled.
func (*Action) Execute ¶ added in v0.151.0
Execute runs the action's body if Action.CanRun, returning whether it ran. A disabled or bodyless action is a safe no-op — every surface routes through this guard, so a stale menu click or palette pick on a just-disabled action does nothing.
func (*Action) IsEnabled ¶ added in v0.151.0
IsEnabled reports the action's enabled state, treating a nil observable as enabled.
func (*Action) IsVisible ¶ added in v0.151.0
IsVisible reports the action's visible state, treating a nil observable as visible.
func (*Action) MenuItem ¶ added in v0.151.0
MenuItem projects the action onto a MenuItem, filling Label, an Execute handler and — when keymap is non-nil and the action is bound — the shortcut hint from the keymap's most specific binding, so the menu always shows the live accelerator.
func (*Action) SetEnabled ¶ added in v0.151.0
SetEnabled sets the enabled state, allocating the observable if needed.
func (*Action) SetVisible ¶ added in v0.151.0
SetVisible sets the visible state, allocating the observable if needed.
func (*Action) ToolbarButton ¶ added in v0.151.0
func (a *Action) ToolbarButton() ToolbarItem
ToolbarButton projects the action onto a ToolbarItem, filling Label, an OnClick handler and Disabled from the action's enabled state. (ToolbarItem takes a raster Icon, not a draw callback, so Action.Icon is not copied here; a host that wants the glyph renders it into an Icon buffer itself.)
type ActionRegistry ¶ added in v0.151.0
type ActionRegistry struct {
// OnChange, when set, fires when the action set changes or any
// registered action's Enabled/Visible flips.
OnChange func()
// contains filtered or unexported fields
}
ActionRegistry is the lookup + iteration index for [Action]s: one place to register a command, find it by id, toggle it, run it, and enumerate the set (in registration order) to build a menu, a toolbar or a CommandPalette.
It observes each registered action's Enabled/Visible observables and fires OnChange when any of them changes (or when an action is added/removed), so a palette rebuilt from ActionRegistry.PaletteCommands stays current without the app polling.
func NewActionRegistry ¶ added in v0.151.0
func NewActionRegistry() *ActionRegistry
NewActionRegistry returns an empty registry.
func (*ActionRegistry) Action ¶ added in v0.151.0
func (r *ActionRegistry) Action(id string) *Action
Action returns the action for id, or nil if it is not registered.
func (*ActionRegistry) Actions ¶ added in v0.151.0
func (r *ActionRegistry) Actions() []*Action
Actions returns the registered actions in registration order.
func (*ActionRegistry) Add ¶ added in v0.151.0
func (r *ActionRegistry) Add(id, label string, run func()) *Action
Add is a convenience that builds an action via NewAction and registers it, returning the action so the caller can set Icon/Shortcut.
func (*ActionRegistry) BindDefaults ¶ added in v0.151.0
func (r *ActionRegistry) BindDefaults(keymap *Keymap, scope Scope) error
BindDefaults binds every registered action that declares a non-empty Action.Shortcut into keymap at the given scope, so a single pass wires the declared accelerators. It stops and returns the first conflict error (ErrConflict); on success it returns nil.
func (*ActionRegistry) Disable ¶ added in v0.151.0
func (r *ActionRegistry) Disable(id string) bool
Disable disables action id, returning whether it was found.
func (*ActionRegistry) Enable ¶ added in v0.151.0
func (r *ActionRegistry) Enable(id string) bool
Enable enables action id, returning whether it was found.
func (*ActionRegistry) Len ¶ added in v0.151.0
func (r *ActionRegistry) Len() int
Len reports how many actions are registered.
func (*ActionRegistry) Lookup ¶ added in v0.151.0
func (r *ActionRegistry) Lookup(id string) (*Action, bool)
Lookup returns the action for id and whether it was found.
func (*ActionRegistry) PaletteCommands ¶ added in v0.151.0
func (r *ActionRegistry) PaletteCommands() []PaletteCommand
PaletteCommands projects the visible actions (in registration order) onto [PaletteCommand]s for a CommandPalette. Each command's handler runs the action through its enabled guard, so a disabled-but-visible action shows in the palette yet does nothing when chosen. Pair with CommandPalette.SetActions and the registry's OnChange to keep the palette live.
func (*ActionRegistry) Register ¶ added in v0.151.0
func (r *ActionRegistry) Register(a *Action) *Action
Register adds (or replaces, by id) an action and returns it for chaining. Registering a new id appends it to the iteration order; re-registering an existing id keeps its position and swaps the action. The registry subscribes to the action's Enabled/Visible observables so later toggles fan out through OnChange. Panics on a nil action or an empty ID (programmer errors).
func (*ActionRegistry) Run ¶ added in v0.151.0
func (r *ActionRegistry) Run(id string) bool
Run executes action id (through its enabled guard), returning whether it both existed and ran.
func (*ActionRegistry) SetEnabled ¶ added in v0.151.0
func (r *ActionRegistry) SetEnabled(id string, enabled bool) bool
SetEnabled toggles the enabled state of action id, returning whether it was found.
func (*ActionRegistry) Unregister ¶ added in v0.151.0
func (r *ActionRegistry) Unregister(id string) bool
Unregister removes the action with id (unsubscribing its observers), returning whether one was present.
type ActionRow ¶ added in v0.8.0
type ActionRow struct {
Base
Title string
Subtitle string
Prefix Widget // optional left slot; nil = no prefix drawn
Suffix Widget // optional right slot; nil = no suffix drawn
}
ActionRow is a libadwaita-style structured list row: a large Title with an optional dim Subtitle, plus optional Prefix and Suffix widget slots on the left and right edges. Composes into a settings- style list: stack several ActionRows in a VBox and each row reads as one entry.
The row paints a Theme.Surface body with a 1-pixel Theme.Border divider along its bottom edge (the classic GTK list-row separator). Prefix / Suffix widget slots are fixed-width strips at the left and right; the Title (and, when non-empty, the Subtitle) flows in the remaining central column.
ActionRow forwards EventClick events to whichever child (Prefix or Suffix) the click's X coordinate lands on. Clicks in the central text region are ignored — a caller wanting an activatable row wraps the ActionRow's Bounds in a container that intercepts clicks or overlays a button in the Suffix slot.
func NewActionRow ¶ added in v0.8.0
NewActionRow constructs an ActionRow with the given title. Subtitle starts empty; Prefix and Suffix start nil. The caller may assign them before the first Draw.
func (*ActionRow) Children ¶ added in v0.137.0
Children yields the row's leading then trailing widget.
func (*ActionRow) Draw ¶ added in v0.8.0
Draw paints the row body + bottom divider, then positions + draws the optional Prefix / Suffix child widgets, then paints the title (and, when non-empty, the subtitle) in the remaining central column. Positioning side effect: Prefix / Suffix widgets have their Bounds updated to reflect their slot rectangle inside the row.
func (*ActionRow) OnEvent ¶ added in v0.8.0
OnEvent forwards EventClick to whichever Prefix / Suffix slot the click's X coordinate lands on, translating X into the child's widget-local space. Clicks in the central text region — or clicks on a slot whose child is nil — are ignored. Non-click events are dropped so keyboard input intended for a focused inner widget is not misrouted; a caller that needs richer keyboard routing wraps the ActionRow in its own dispatcher.
type ActionSheet ¶ added in v0.187.0
type ActionSheet struct {
Base
// Title is an optional heading drawn in a strip at the top of the panel,
// under the handle. Empty draws no strip.
Title string
// Actions is the vertical list of action buttons in ACTION mode. Each is
// drawn and hit-tested at a row height clamped up to the density's finger
// floor via [TouchTarget]. Populate it directly, or use [ActionSheet.AddAction]
// for the common "run then dismiss" wiring.
Actions []*Button
// Cancel is the optional trailing dismiss button in ACTION mode, drawn below
// the actions with a wider separating gap (the platform "Cancel" convention).
// Its own OnClick runs first; wire it with [ActionSheet.SetCancel] to also
// dismiss the sheet.
Cancel *Button
// Content is the hosted widget in BOTTOM-SHEET mode. When non-nil the sheet
// is a bottom sheet (Draggable defaults on) and the Actions/Cancel list is
// ignored; when nil the sheet is an action sheet.
Content Widget
// PreferredHeight is the bottom-sheet panel height in LOGICAL px (routed
// through [scaled]). Zero means half the surface height. Ignored in ACTION
// mode, whose height is computed from the rows.
PreferredHeight int
// Detents are the resting heights a bottom sheet snaps to on release, given
// as VISIBLE FRACTIONS of the panel in (0, 1] — 1.0 fully shown, 0.5 half
// shown. Empty means the single detent [DetentFull]. Kept ascending is
// conventional but not required; the nearest one wins.
Detents []float64
// Draggable enables the interactive drag-to-dismiss / detent behaviour. The
// constructors set it (on for a bottom sheet, off for an action sheet); a
// caller may override it. A drag is only started from the handle strip at
// the top of the panel, so it never steals a press aimed at the content.
Draggable bool
// ShowHandle draws the little grab bar at the top of the panel — the visual
// affordance for the drag. The constructors set it with Draggable.
ShowHandle bool
// DismissFraction is the visible-fraction floor for a slow (non-fling)
// release: let go with less than this fraction of the panel showing and the
// sheet dismisses instead of settling to a detent. Zero means the default
// [ActionSheetDismissFraction].
DismissFraction float64
// FlingVelocity is the release speed (device px/s, magnitude) at or above
// which a flick decides the outcome regardless of position: a downward fling
// dismisses, an upward fling settles to the fullest detent. Zero means the
// default [ActionSheetFlingVelocity].
FlingVelocity float64
// SlideDuration is the programmatic open / dismiss slide length in SECONDS.
// Zero means the default [ActionSheetSlideDuration]. The interactive settle
// is governed by the momentum spring, not this.
SlideDuration float64
// FrameSeconds is the per-frame time (seconds) [ActionSheet.OnEvent] hands
// the velocity tracker for a drag-move, since a raw pointer event carries no
// timestamp. Zero means the default [ActionSheetFrameSeconds] (~60 Hz). Tests
// and hosts that know their real frame period may drive the drag through the
// explicit [ActionSheet.DragMove] instead.
FrameSeconds float64
// ScrimAlpha is the opacity (0..255) of the dimming scrim at full visibility;
// it fades in proportion as the panel is dragged away. Zero means the default
// [ActionSheetScrimAlpha].
ScrimAlpha uint8
// OnDismiss fires once, after a dismiss slide has fully completed and the
// sheet has reached [ActionSheetClosed].
OnDismiss func()
// contains filtered or unexported fields
}
ActionSheet is a modal panel that rises from the bottom edge over a dimming scrim — the "sheet" every mobile platform reaches for when a choice or a piece of transient content belongs at the thumb, not in the middle of the screen. It fills the whole surface (like Dialog and a modal Overlay) so it owns the scrim and the z-order, and it slides its panel up on open and down on dismiss.
It serves the two shapes the pattern always comes in:
ACTION mode (the iOS/Material "action sheet"): a vertical stack of touch-sized action buttons plus an optional Cancel button. Every row is sized through TouchTarget so it is at least the density's finger floor (44 logical px under DensityTouch); a tap fires the action, and Cancel, the scrim, or Esc dismisses. There is no drag-to-dismiss — an action sheet is dismissed by choosing, cancelling or tapping away, exactly like the platform sheets it mirrors.
BOTTOM-SHEET mode: a panel hosting an arbitrary Content widget, with a grab handle, DRAG-TO-DISMISS (drag the panel down past a threshold, or fling it down, and it leaves) and optional DETENTS (half / full resting heights). The interactive drag + release runs through the toolkit's deterministic momentum model (Momentum + VelocityTracker); the programmatic open / dismiss slides run through the animator's easing (Easing). Both are clock-free and advanced by ActionSheet.Tick.
Modality and accessibility. While it is not ActionSheetClosed the sheet swallows every pointer event inside its bounds (a tap outside the panel is a scrim dismiss, not a click that leaks to the content beneath — see ActionSheet.HitTest); it reports itself to the a11y tree as a RoleDialog carrying its modal state (see ActionSheet.A11y); it exposes its action buttons / content to the tree walk (see ActionSheet.Children); and a Back / Esc key dismisses it. Focus is meant to live inside the sheet while it is up, the same modal-grab contract Dialog documents.
The presented state is published as an mvvm.Observable so app state can drive the sheet (Set it true to open) and react to it (subscribe to learn when a user-driven dismiss finished), without the app polling the state machine.
func NewActionSheet ¶ added in v0.187.0
func NewActionSheet(title string, actions ...*Button) *ActionSheet
NewActionSheet builds a closed ACTION sheet: a vertical list of the given action buttons under an optional title. Drag is off (an action sheet is dismissed by choosing, cancelling or tapping the scrim). Call ActionSheet.Open to present it.
func NewBottomSheet ¶ added in v0.187.0
func NewBottomSheet(content Widget) *ActionSheet
NewBottomSheet builds a closed BOTTOM sheet hosting content, with the grab handle and drag-to-dismiss enabled. Add detents via the Detents field (it defaults to a single full detent). Call ActionSheet.Open to present it.
func (*ActionSheet) A11y ¶ added in v0.187.0
func (a *ActionSheet) A11y() A11yInfo
A11y reports the sheet as a dialog named by its Title, carrying its modal state so a screen reader announces a live sheet as a modal dialog and a closed one as nothing of note. The action buttons / content are exposed separately through ActionSheet.Children so the a11y tree walk descends into them.
func (*ActionSheet) AddAction ¶ added in v0.187.0
func (a *ActionSheet) AddAction(label string, fn func()) *Button
AddAction appends an action row labelled label whose tap runs fn and then dismisses the sheet — the usual action-sheet wiring — and returns the created Button so the caller can restyle it (e.g. Style = ButtonDanger). fn may be nil.
func (*ActionSheet) Animating ¶ added in v0.187.0
func (a *ActionSheet) Animating() bool
Animating reports whether the sheet still needs frames: a live animator slide or a settling momentum spring. Implements Animator, so TickTree / TreeAnimating drive it.
func (*ActionSheet) Children ¶ added in v0.187.0
func (a *ActionSheet) Children() []Widget
Children yields the sheet's interactive contents in visual order — the hosted Content in bottom-sheet mode, or the action rows followed by Cancel in action mode — so WalkA11y and every other generic tree walk reaches them.
func (*ActionSheet) Dismiss ¶ added in v0.187.0
func (a *ActionSheet) Dismiss()
Dismiss slides the panel down to fully hidden via the animator easing, then (on completion) closes the sheet and fires OnDismiss. A no-op when already closed or dismissing. This is the programmatic / Esc / scrim / Cancel path; an interactive drag-release dismiss instead uses the momentum spring.
func (*ActionSheet) DragBegin ¶ added in v0.187.0
func (a *ActionSheet) DragBegin(y int)
DragBegin starts an interactive drag from surface-Y y. It seeds the momentum engine at the current position with the panel travel [0, sheetH] as bounds (so a drag up past the full detent rubber-bands), resets the velocity tracker and enters the dragging state. A no-op when the sheet is not draggable or not present.
func (*ActionSheet) DragMove ¶ added in v0.187.0
func (a *ActionSheet) DragMove(y int, dt float64)
DragMove feeds one drag sample: the finger is now at surface-Y y, dt seconds after the previous sample. Moving down (increasing y) hides the panel; the panel tracks the finger, rubber-banding past the full detent. dt feeds the velocity tracker so the release knows the flick speed. A no-op unless a drag is active.
func (*ActionSheet) DragRelease ¶ added in v0.187.0
func (a *ActionSheet) DragRelease()
DragRelease ends the drag and decides the outcome from the tracked release velocity and the current position, then hands the panel to the momentum spring to settle onto the chosen target:
- a downward fling (velocity >= FlingVelocity) dismisses;
- an upward fling (velocity <= -FlingVelocity) settles to the fullest detent;
- otherwise, a release with less than DismissFraction of the panel showing dismisses, and any other release settles to the nearest detent.
The spring is seeded with the release velocity, so a firmer flick reaches the target faster. A no-op unless a drag is active.
func (*ActionSheet) Draw ¶ added in v0.187.0
func (a *ActionSheet) Draw(p painter.Painter, theme *Theme)
Draw paints the dimming scrim (its alpha fading with the panel's visibility), then the panel: a rounded-top surface with a border, an optional grab bar and title, and the action rows or hosted content. Nothing is drawn while closed.
func (*ActionSheet) HitTest ¶ added in v0.187.0
func (a *ActionSheet) HitTest(px, py int) bool
HitTest makes the sheet a modal shield while it is visible: it catches every pointer event inside its bounds (so a tap outside the panel is a scrim dismiss, never a click leaking to the content beneath). While closed it is event-transparent, letting clicks pass through to whatever is behind it.
func (*ActionSheet) OnEvent ¶ added in v0.187.0
func (a *ActionSheet) OnEvent(ev Event)
OnEvent routes input while the sheet is visible: Back/Esc dismisses; a press outside the panel dismisses (scrim tap); a press on the grab strip starts a drag (bottom-sheet mode); and presses on the body reach the action buttons / content. Drag moves feed the momentum tracker using FrameSeconds, since a pointer event carries no timestamp. Widget-local coordinates are converted to surface coordinates for hit-testing, and translated into each child's frame for forwarding.
func (*ActionSheet) Open ¶ added in v0.187.0
func (a *ActionSheet) Open()
Open presents the sheet: it slides the panel up from fully hidden to its fullest detent via the animator easing. A no-op when already open or opening.
func (*ActionSheet) Presented ¶ added in v0.187.0
func (a *ActionSheet) Presented() *mvvm.Observable[bool]
Presented is the mvvm.Observable carrying the sheet's presented (open) state: it becomes true when a present begins and false once a dismiss has fully completed. Lazily allocated, so a caller can subscribe or Set it. Setting it true opens the sheet; setting it false dismisses it.
func (*ActionSheet) SetBounds ¶ added in v0.187.0
func (a *ActionSheet) SetBounds(r Rect)
SetBounds records the surface rectangle, recomputes the panel height for the current mode + density, and re-clamps the hidden offset into range.
func (*ActionSheet) SetCancel ¶ added in v0.187.0
func (a *ActionSheet) SetCancel(label string, fn func()) *Button
SetCancel installs the trailing Cancel button: its tap runs fn (if any) and dismisses. Returns the Button for restyling. fn may be nil.
func (*ActionSheet) State ¶ added in v0.187.0
func (a *ActionSheet) State() ActionSheetState
State returns the sheet's current state-machine position.
func (*ActionSheet) Tick ¶ added in v0.187.0
func (a *ActionSheet) Tick(dt float64)
Tick advances whichever motion is live — the programmatic animator slide or the interactive momentum spring — by dt seconds, updating the panel position and firing the terminal state transition (Open reached, or Closed + OnDismiss on a completed dismiss). A no-op when nothing is animating. Implements Animator.
func (*ActionSheet) Visible ¶ added in v0.187.0
func (a *ActionSheet) Visible() bool
Visible reports whether the sheet is anything other than fully closed.
type ActionSheetState ¶ added in v0.187.0
type ActionSheetState int
ActionSheetState is the sheet's open/dismiss state-machine position.
const ( // ActionSheetClosed is the resting hidden state: the sheet draws nothing, // is event-transparent ([ActionSheet.HitTest] returns false) and ignores // input. The zero value, so a freshly built sheet is closed. ActionSheetClosed ActionSheetState = iota // ActionSheetOpening is the programmatic slide-in (animator easing). ActionSheetOpening // ActionSheetOpen is fully shown at a detent, at rest, interactive. ActionSheetOpen // ActionSheetDragging is a finger-driven drag in progress (no animation; // the panel tracks the finger with a rubber band at the top edge). ActionSheetDragging // ActionSheetSettling is the post-release momentum spring toward a detent. ActionSheetSettling // ActionSheetDismissing is the slide/spring to fully hidden; on completion // the sheet reaches ActionSheetClosed and fires OnDismiss. ActionSheetDismissing )
type AddressBar ¶ added in v0.187.0
type AddressBar struct {
Base
// LeadingIcon / BookmarkIcon are optional host-drawn glyph slots at the left
// and right (appearance config, not reactive state — painter funcs are not
// Observable). BookmarkIcon takes the Bookmarked state so the host draws the
// on/off variant. Same painter seam as the toolbar icons.
LeadingIcon func(p painter.Painter, r Rect, ink RGBA)
BookmarkIcon func(p painter.Painter, r Rect, ink RGBA, on bool)
// Commit is executed on Enter when the trimmed edit buffer is non-empty; a
// host binds it to normalise + navigate to [AddressBar.Editing]. Nil → Enter
// just defocuses.
Commit *mvvm.Command
// Radius is the corner radius and TextPad the left/right text inset, both in
// device pixels so a HiDPI host scales them. Config, not state.
Radius int
TextPad int
// contains filtered or unexported fields
}
AddressBar is an editable URL / address field: a rounded Backdrop ground with an optional leading status-icon slot, an optional trailing bookmark-toggle slot, and head-clipped text that keeps a long URL's tail (its path) visible.
Its mutable state is MVVM-only: the value, edit buffer, focus, bookmark and copy flags are unexported [mvvm.Observable]s exposed solely through accessors (AddressBar.URL, AddressBar.Editing, AddressBar.Focused, AddressBar.Bookmarked, AddressBar.Copied). There is no settable state field, so a host can only change the field through its Observables (bind them on a view model, or Set them) — never by imperative assignment. Enter runs the AddressBar.Commit command (a host binds it to normalise + navigate).
It shows URL when unfocused and Editing while focused; a click focuses it (seeding Editing from URL), a click on the bookmark slot flips Bookmarked instead, and the container defocuses it with AddressBar.Blur when a click lands elsewhere. Reusable in any toolbar; the toolkit Browser composes one and shares its URL + Bookmarked Observables so neither side copies the other.
func (*AddressBar) A11y ¶ added in v0.187.0
func (a *AddressBar) A11y() A11yInfo
A11y reports the field as a textbox carrying its current value (the edit buffer while focused, else the URL).
func (*AddressBar) Blur ¶ added in v0.187.0
func (a *AddressBar) Blur()
Blur defocuses the field and clears the copy highlight. The container calls it when a click lands outside the field.
func (*AddressBar) Bookmarked ¶ added in v0.187.0
func (a *AddressBar) Bookmarked() *mvvm.Observable[bool]
Bookmarked is the bookmark toggle state. A host binds it to its bookmark store; clicking the bookmark slot flips it. Subscribe to it instead of a callback.
func (*AddressBar) Copied ¶ added in v0.187.0
func (a *AddressBar) Copied() *mvvm.Observable[bool]
Copied reports whether a select-all copy highlight is currently shown.
func (*AddressBar) CopySelectAll ¶ added in v0.187.0
func (a *AddressBar) CopySelectAll() (string, bool)
CopySelectAll copies the field's value to the toolkit-wide clipboard and flags a select-all highlight, reporting the text and whether anything was copied. It is a no-op returning ("", false) when the field is not focused or is empty.
func (*AddressBar) Draw ¶ added in v0.187.0
func (a *AddressBar) Draw(p painter.Painter, theme *Theme)
Draw paints the field: a rounded Backdrop ground (Surface fill + a border that turns Accent while focused), the optional icon slots, the head-clipped text (edit buffer when focused, else URL), a caret when focused, and a select-all highlight after a copy.
func (*AddressBar) Editing ¶ added in v0.188.0
func (a *AddressBar) Editing() *mvvm.Observable[string]
Editing is the in-progress edit buffer, shown while the field is focused.
func (*AddressBar) Focused ¶ added in v0.187.0
func (a *AddressBar) Focused() *mvvm.Observable[bool]
Focused reports (and drives) keyboard focus. A view model can observe it; the widget flips it on click / Blur.
func (*AddressBar) OnEvent ¶ added in v0.187.0
func (a *AddressBar) OnEvent(ev Event)
OnEvent routes a click (local coordinates, relative to the field's bounds origin), a typed rune or an edit key. A click on the bookmark slot toggles it; any other click focuses the field and seeds the edit buffer from URL. While focused, a rune appends, Backspace deletes and Enter commits.
func (*AddressBar) URL ¶ added in v0.188.0
func (a *AddressBar) URL() *mvvm.Observable[string]
URL is the address shown when the field is not focused (a host binds it to the current page URL). Set it through the Observable; never assign a field.
func (*AddressBar) Value ¶ added in v0.187.0
func (a *AddressBar) Value() string
Value returns the text the field shows: the edit buffer while focused, else URL.
type Agenda ¶ added in v0.82.0
type Agenda struct {
Base
Events []AgendaEvent
DayNames []string
StartHour, EndHour int
// OnDayActivate fires when a month-view click lands on an in-month day cell
// that is not an event chip, carrying that cell's (year, month, day). A
// host uses it to add an event on the clicked day. Nil is safe.
OnDayActivate func(year, month, day int)
// OnEventEdited fires after the inline editor (see EditEvent) commits a
// change to event i — a title edit or a calendar reassignment. Nil is safe.
OnEventEdited func(i int)
// Calendars are the named, colour-coded event sources (see
// AgendaCalendar). An event's colour is resolved from its Calendar index
// into this slice, and an event whose calendar is Hidden is not drawn or
// hit. Empty (the default) keeps the original behaviour: every event uses
// its own Fill (or the theme Accent) and none are ever hidden. Share this
// exact slice with an AgendaSidebar so toggling a row's visibility is seen
// here without any extra wiring.
Calendars []AgendaCalendar
// Year and Month are the focused period for the calendar views. When a
// needed field is zero it is derived from the first dated event (see
// focusYM/focusYear); if none is available the calendar grid stays empty.
Year int
Month int // 1..12
// contains filtered or unexported fields
}
Agenda is a week view of events: a top header row of day names, a left gutter of hour labels, and a day-column × hour-row grid on which each AgendaEvent paints as a rounded block positioned at its Day column and spanning its [StartMin, EndMin) range clamped to the visible hours. StartHour and EndHour bound that visible range; when they are unset (or EndHour <= StartHour) they fall back to a 08:00..18:00 working day so a caller can leave them zero. The Selected event (when its Observable indexes an event) tints that block and draws an accent border, and a click inside a block Sets Agenda.Selected to its index, notifying subscribers.
Agenda renders through painter.Painter, so the same week draws as pixels (WUI/GUI) or promoted cells (TUI). It is distinct from Calendar, which is a month date-picker; Agenda plots events on a day/time grid. An empty event slice draws just the header, gutter and grid.
func NewAgenda ¶ added in v0.82.0
func NewAgenda(events []AgendaEvent) *Agenda
NewAgenda builds an Agenda over the given events, seeded with a Monday-first week (Mon..Sun), an 08:00..18:00 visible day and no selection (Selected = -1). A nil slice is normalised to a non-nil empty slice so range loops and len() checks never special-case nil.
func (*Agenda) A11y ¶ added in v0.105.0
A11y reports the Agenda as a grid carrying its focused period as a "YYYY-MM" string (the month the calendar views centre on), or "" when no period can be resolved from the fields or events.
func (*Agenda) DayAt ¶ added in v0.84.0
DayAt maps widget-local (x, y) to the in-month day cell under it in the month view, returning the focused (year, month, day) and ok=true; ok=false for the header, outside the grid, or a spill cell. Exposed so a host can hit-test a right-click and offer an "add event here" menu. Only meaningful when View == AgendaMonth.
func (*Agenda) Draw ¶ added in v0.82.0
Draw dispatches to the active View's painter. AgendaWeek (the zero value) draws the original week grid unchanged; the calendar views draw a month, quarter or year of dated events.
func (*Agenda) DrawEditor ¶ added in v0.97.0
DrawEditor paints the open editor overlay (a no-op when none is open or the editing index has fallen out of range). The panel is clipped to itself so no control ever bleeds past its rounded border. The swatch of the event's current calendar carries an accent ring so the active calendar is obvious.
func (*Agenda) EditEvent ¶ added in v0.97.0
EditEvent opens the inline editor for event i, seeding the title Entry with its current Title. Out-of-range i is a no-op (no editor opens). Opening a new editor replaces any editor already open.
func (*Agenda) Editing ¶ added in v0.97.0
Editing returns the index of the event whose editor is open, or -1 when no editor is open. A host checks this to decide whether to route keystrokes to EditorChar/EditorKey and to draw the overlay.
func (*Agenda) EditorChar ¶ added in v0.97.0
EditorChar feeds a printable character (post-IME) to the open title Entry. A no-op when no editor is open.
func (*Agenda) EditorClick ¶ added in v0.97.0
EditorClick routes a click at (x, y) — in the SAME (absolute) coordinate space as the widget's Bounds, like DropDown.PopoverClick — while the editor is open, and reports whether it consumed the event (always true while open, so the host doesn't also treat the click as a grid selection). A click on a calendar swatch reassigns the event (fires OnEventEdited); a click in the title Entry focuses it; a click anywhere outside the panel commits the edit and closes. When no editor is open it returns false and does nothing.
func (*Agenda) EditorKey ¶ added in v0.97.0
EditorKey feeds a key press to the open editor: "Enter" commits + closes, "Escape" cancels + closes (discarding the title edit), and every other key (Backspace, arrows, Home/End, clipboard shortcuts) is forwarded to the title Entry. A no-op when no editor is open.
func (*Agenda) OnEvent ¶ added in v0.82.0
OnEvent selects the event under an EventClick in whatever View is active and Sets Agenda.Selected to its index (notifying subscribers). Each view has a hit-test that shares its paint geometry so the two can't drift: hitWeek walks the day/time blocks, hitMonth the day-cell chips, hitMini the mini-month day cells. Clicks that land on no event — headers, gutters, dead-space, "+N" markers — and any non-click event are no-ops. Overlapping candidates resolve to the visually-topmost (last-drawn) one.
func (*Agenda) Selected ¶ added in v0.82.0
func (a *Agenda) Selected() *mvvm.Observable[int]
Selected is the selected event index as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Selected field. A click on an event Sets it (an index, or -1 for no selection); subscribers are notified. On a zero-value Agenda it lazy-inits to 0; NewAgenda seeds it to -1.
func (*Agenda) View ¶ added in v0.82.0
func (a *Agenda) View() *mvvm.Observable[AgendaView]
View is the active layout (week/month/quarter/year) as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable View field. It lazy-inits to the zero AgendaView (AgendaWeek), so a zero-value Agenda still draws the week grid.
type AgendaCalendar ¶ added in v0.96.0
AgendaCalendar is one named, colour-coded source of events — a "calendar" in the Google/Apple Calendar sense (typically a remote CalDAV/ICS feed the host syncs into Agenda.Events). Color tints every event that belongs to it, and Hidden hides all of its events at once (the toggle an AgendaSidebar row drives) without removing them from Agenda.Events. The toolkit does not fetch anything — the host feeds events in and assigns each event's Calendar index; AgendaCalendar only carries the presentation (name + colour + visibility).
type AgendaEvent ¶ added in v0.82.0
type AgendaEvent struct {
Title string
Day int
StartMin, EndMin int
Y, M, D int
Fill RGBA
Calendar int
}
AgendaEvent is one appointment. In the week view (AgendaWeek) it draws as a coloured block: Title labels it, Day is the weekday column — an index in [0, len(DayNames)); events whose Day falls outside that range are skipped (no column to place them in) — and StartMin/EndMin are minutes-from-midnight (0..1440) bounding the block vertically as the half-open range [StartMin, EndMin), so EndMin must be greater than StartMin. In the calendar views (AgendaMonth/AgendaQuarter/AgendaYear) the event is placed by its absolute date instead: Y is the year, M the month (1..12) and D the day-of-month (1..31); Day/StartMin/EndMin are ignored there.
Colour resolution (see Agenda.eventFill): an explicit non-zero Fill always wins; otherwise, when Calendar indexes a calendar in Agenda.Calendars that carries a non-zero Color, that calendar's colour is used (so every event on a "Work" calendar shares one colour without repeating it per event); otherwise it falls back to the theme's Accent. Calendar is an index into Agenda.Calendars; its zero value points at the first calendar, and any value outside [0, len(Calendars)) means "no calendar" (Fill/Accent only). An event whose calendar is Hidden is not drawn or hit-tested (see eventVisible).
type AgendaSidebar ¶ added in v0.96.0
type AgendaSidebar struct {
Base
// Calendars is the shared list (see the type doc). Rows render + hit-test
// in this order.
Calendars []AgendaCalendar
// Title is the header label above the rows; "" hides the header row
// entirely (the first calendar then sits at the very top).
Title string
// OnToggle fires after a single click flips Calendars[i].Hidden, with that
// row's index. Nil is safe. The flip has already been applied when it runs,
// so a host can persist the new state or re-sync.
OnToggle func(i int)
// OnRename fires after CommitEdit writes a new Name onto Calendars[i] (open
// the inline editor by double-clicking a row, then Enter to commit). It
// carries the row index and the new name, already applied to Calendars[i]
// when it runs, so the host/VM persists it — the MVVM seam, analogous to
// OnToggle. Nil is safe.
OnRename func(i int, name string)
// contains filtered or unexported fields
}
AgendaSidebar is the calendar list that sits beside an Agenda (Google/Apple Calendar's left rail): an optional title row above one row per AgendaCalendar, each showing a colour swatch, the calendar name, and its visibility state. A single click on a row flips that calendar's Hidden flag and fires OnToggle; a double-click opens an inline editor over the row's name to rename the calendar (the remote agenda), firing OnRename on commit.
It shares the SAME AgendaCalendar slice as its Agenda — set both from one slice value (agenda.Calendars = cals; sidebar := NewAgendaSidebar(cals)) — so a visibility toggle here is reflected in the Agenda's rendering with no extra wiring: they mutate the one backing array. (Because the toggle mutates an element in place it never reallocates, so the shared view holds; only appending/replacing the slice would break the link.)
A host lays it out to the left of the Agenda with an HBox (a fixed-width sidebar column + a flexible Agenda column), exactly as it composes any other two widgets — the sidebar is a plain Widget, not a mode of the Agenda.
func NewAgendaSidebar ¶ added in v0.96.0
func NewAgendaSidebar(cals []AgendaCalendar) *AgendaSidebar
NewAgendaSidebar builds a sidebar over cals (the same slice the Agenda uses), titled "Calendars". A nil slice is normalised to a non-nil empty slice so range loops never special-case nil.
func (*AgendaSidebar) A11y ¶ added in v0.130.0
func (s *AgendaSidebar) A11y() A11yInfo
A11y reports the AgendaSidebar as navigation named by its title.
func (*AgendaSidebar) CancelEdit ¶ added in v0.99.0
func (s *AgendaSidebar) CancelEdit()
CancelEdit closes the rename editor without changing any Name. Safe (a no-op) when no editor is open.
func (*AgendaSidebar) CommitEdit ¶ added in v0.99.0
func (s *AgendaSidebar) CommitEdit()
CommitEdit writes the editor's text back onto Calendars[i].Name, fires OnRename with (i, name), and closes the editor. A no-op when no editor is open; guarded against an editing index gone stale (e.g. the calendar was removed while editing).
func (*AgendaSidebar) Draw ¶ added in v0.96.0
func (s *AgendaSidebar) Draw(p painter.Painter, theme *Theme)
Draw paints the sidebar: a SurfaceAlt background with a right divider, the optional title row, then one row per calendar. A visible calendar shows a filled swatch and full-strength name; a Hidden one shows a hollow (outline) swatch and a dimmed name, so visibility reads at a glance. Every row is clipped to its rectangle so a long name never bleeds into the next row or past the rail. While a row is being renamed (see EditName) its inline Entry is painted over the name area in place of the static name; the swatch is unchanged.
func (*AgendaSidebar) EditName ¶ added in v0.99.0
func (s *AgendaSidebar) EditName(i int)
EditName opens the inline rename editor on calendar row i, seeding the Entry with its current Name and focusing it. Out-of-range i is a safe no-op (no editor opens). Opening a new editor replaces any editor already open, without committing it. It never touches Hidden.
func (*AgendaSidebar) Editing ¶ added in v0.99.0
func (s *AgendaSidebar) Editing() int
Editing returns the index of the calendar whose rename editor is open, or -1 when none is open. A host checks this to route keystrokes into OnEvent while editing (Enter/Esc commit/cancel) and to know the editor overlay is live.
func (*AgendaSidebar) OnEvent ¶ added in v0.96.0
func (s *AgendaSidebar) OnEvent(ev Event)
OnEvent drives the sidebar. When no rename editor is open: a double-click on a row (an EventClick tagged with AgendaSidebarDoubleClick) opens the inline rename editor over that row; an ordinary EventClick toggles the row's Hidden flag and fires OnToggle; clicks on the header or dead space, and any non-click event, are no-ops.
While a rename editor is open (Editing() >= 0), events route to it instead: EventChar and text-editing EventKeyDown feed the Entry (caret + text); "Enter" commits (CommitEdit), "Escape" cancels (CancelEdit); a click inside the editor keeps it focused, and a click anywhere else commits — the Google/Apple Calendar convention that clicking away saves the rename (the same rule the Agenda event editor uses). No visibility toggle happens while editing.
func (*AgendaSidebar) ScrollBy ¶ added in v0.106.0
func (s *AgendaSidebar) ScrollBy(delta int)
ScrollBy shifts scroll by delta rows (negative scrolls up), clamped to [0, maxScroll()] and written back immediately.
type AgendaView ¶ added in v0.82.0
type AgendaView int
AgendaView selects which of the four calendar layouts an Agenda draws. The zero value AgendaWeek keeps the original day/time week grid (so a zero-value Agenda is byte-identical to before this type existed); the other three plot events by their absolute Y/M/D date.
const ( // AgendaWeek is the day-column × hour-row week grid (the default). AgendaWeek AgendaView = iota // AgendaMonth is a single month grid: a weekday header, up to six week // rows of day cells, event chips per day and a "+N" overflow marker. AgendaMonth // AgendaQuarter is three compact month grids (Month, Month+1, Month+2) // side by side, each dotting the days that carry events. AgendaQuarter // AgendaYear is twelve very compact month grids for Year in a 4×3 layout, // each dotting the days that carry events. AgendaYear )
type Alert ¶ added in v0.7.0
Alert is a persistent banner sitting at the top or bottom of a view, carrying a Text message coloured by Kind. Shares Notification's filled-panel-with-border shape but differs in three ways:
- No Life field: an Alert stays on screen until the host removes it (the "you are offline" banner). No Tick(), no auto-hide.
- No Visible toggle: an Alert that exists is drawn. To hide an alert the host stops rendering it (or drops it from the tree).
- Coloured by Kind: Notification is always Accent; Alert varies colour by severity so success + error read differently at a glance.
The banner is not interactive; the parent view supplies a dismiss button as a separate Button if the design calls for one.
func NewAlert ¶ added in v0.7.0
NewAlert constructs an Alert with the given Text + Kind. Bounds are zero-initialised; the host is responsible for positioning + sizing the banner (typically full-width across the top of the parent view).
type AlertKind ¶ added in v0.7.0
type AlertKind int
AlertKind selects the semantic colour of an Alert banner. Info reuses the theme's Accent (the same blue used by focus rings + link text); the other three carry hard-coded shades tuned for meaning — green for success, amber for warning, red for error — because the theme palette doesn't carry semantic slots and adding them would blow up the Theme surface for every app.
const ( // AlertInfo is a neutral heads-up ("Backup started"). Rendered in // Theme.Accent so it matches the app's own accent colour. AlertInfo AlertKind = iota // AlertSuccess signals a completed operation ("Saved!"). Green. AlertSuccess // AlertWarning flags a non-fatal issue ("Battery low"). Amber. AlertWarning // AlertError signals a failure the user must address ("Sync // failed"). Red. AlertError )
type Align ¶ added in v0.26.0
type Align int
Align is a widget's horizontal text alignment within its bounds. The zero value is AlignLeft, so an unset Align keeps the original left-aligned layout.
type Animator ¶ added in v0.155.0
type Animator interface {
// Tick advances the animation by dt seconds (the elapsed wall-clock time
// since the previous frame).
Tick(dt float64)
// Animating reports whether the widget still needs fresh frames. When every
// Animator in a tree returns false the host can stop repainting until the
// next interaction restarts one.
Animating() bool
}
Animator is an optional Widget capability: a widget that animates (a spinner, an indeterminate progress bar, a skeleton shimmer) implements it so a host present loop can advance it each frame and learn whether it still needs frames — so an idle UI stops repainting and a stopped animation costs nothing.
The contract is the same manual-clock one the rest of the toolkit uses: the widget owns no goroutine and no timer. The host calls Tick(dt) once per frame with the elapsed wall-clock seconds, then consults Animating to decide whether to schedule another frame. This retires the hand-rolled "am I still spinning?" bookkeeping that applications otherwise keep beside every spinner — the source of the classic frozen-spinner bug where the flag and the animation drift apart.
A container does not implement Animator: TickTree and TreeAnimating descend the widget tree through [childContainer] (the same convention WalkA11y and CollectRuns use) and apply the capability to whichever leaves carry it, so a host drives a whole composed UI with one call and never wires a spinner through by hand.
type AppDock ¶ added in v0.178.0
type AppDock struct {
Base
// Items are the entries, left to right.
Items []AppDockItem
// Magnify enables the hover swell. MaxScale (default 1.6) is the peak factor
// under the pointer; Radius (default 1.5) is the falloff reach in item
// widths. A non-positive Radius or a MaxScale <= 1 disables the swell.
Magnify bool
MaxScale float64
Radius float64
// OnActivate fires with the clicked item's index.
OnActivate func(i int)
// Style paints the ground + item faces + running/active indicators. Nil uses
// ModernDockStyle (the macOS look); set BevelDockStyle{} for Fluxbox,
// WindowsDockStyle{} for a taskbar, or a custom DockStyle.
Style DockStyle
// contains filtered or unexported fields
}
AppDock is a horizontal launcher bar (a macOS-style application dock) with optional hover magnification: the item under the pointer, and its neighbours with a smooth raised-cosine falloff, swell and the row reflows so swollen items never overlap and the point under the cursor stays put.
It is composed entirely from toolkit primitives — a Backdrop ground and, per item, a rounded Backdrop face, the host's icon painter, a clipped label, a running dot and an attention Badge — so it carries the toolkit look under any theme with no hand-drawn chrome. ItemRects publishes the live geometry and HitTest maps a point to an item, so paint and hit-testing read one layout.
(Not to be confused with Dock, the edge-docking LAYOUT container: AppDock is a visual launcher; Dock arranges bars around a body.)
func NewAppDock ¶ added in v0.178.0
func NewAppDock(items ...AppDockItem) *AppDock
NewAppDock builds a dock over items with magnification on at the default feel.
func (*AppDock) A11y ¶ added in v0.178.0
A11y describes the dock to a screen reader as a toolbar — a single node, the same way the other data-driven item widgets (ListBox) expose themselves.
func (*AppDock) HitTest ¶ added in v0.178.0
HitTest returns the index of the item at the absolute point (x,y), or -1 when the point hits no item.
func (*AppDock) ItemRects ¶ added in v0.178.0
ItemRects returns each item's current on-screen rectangle (magnified while hovered, resting otherwise), one per item in order — the geometry a host publishes and hit-tests against.
func (*AppDock) OnEvent ¶ added in v0.178.0
OnEvent drives magnification from EventMouseMove (the pointer position sets the swell; a move that leaves the bounds flattens the row) and activation from EventClick (the hit item fires OnActivate). Coordinates are widget-local.
type AppDockItem ¶ added in v0.178.0
type AppDockItem struct {
// Id is the caller's opaque identifier (echoed nowhere by the widget; handy
// for the host's OnActivate switch).
Id string
// Label is drawn to the right of the icon; "" makes the item icon-only.
Label string
// Icon is the host-supplied leaf painter (the same seam as Browser's toolbar
// icons): it is called with the glyph box and the item's ink. Nil draws no
// glyph, leaving the label (or an empty face).
Icon func(p painter.Painter, r Rect, ink RGBA)
// Running raises the "app is open" dot under the glyph.
Running bool
// Active fills the item face in the accent (the "current app") instead of the
// resting surface.
Active bool
// Badge, when > 0, overlays an attention count as a Badge at the top-right.
Badge int
// Width overrides the item's resting width in device pixels; 0 uses the
// scaled default (AppDockItemW). A host with variable-width entries — window
// task buttons sized to their title, say — sets it per item; layout,
// magnification and hit-testing all honour it.
Width int
}
AppDockItem is one entry in an AppDock: a leaf icon, an optional label, and the state flags that drive its face, running dot and attention badge.
type AreaChart ¶ added in v0.82.0
type AreaChart struct {
Base
Series [][]float64
Min, Max float64 // shared Y bounds; when equal, taken from the data
Colors []RGBA // optional per-series palette override; cycles by index
// contains filtered or unexported fields
}
AreaChart plots one or more series of Y values as polylines whose region down to the baseline is filled with a semi-transparent tint of the series colour -- the shaded sibling of LineChart. Values in each series spread evenly across the plot width and share a single Y scale (auto-derived from every series when Min == Max). Series paint back-to-front so an earlier (larger) band sits behind a later one. Colours cycle through the shared categorical palette unless Colors is set. Display-only.
It renders through painter.Painter, so the same chart draws as anti-aliased pixels (WUI/GUI) or promoted cells (TUI). An empty Series draws just the axes; a lone point per series marks a dot over a single filled column.
func NewAreaChart ¶ added in v0.82.0
NewAreaChart builds an AreaChart over the given series with auto Y bounds.
func (*AreaChart) A11y ¶ added in v0.105.0
A11y reports the AreaChart as an img carrying its series count, mirroring the LineChart/BarChart/PieChart convention (AreaChart plots one band per series).
func (*AreaChart) Draw ¶ added in v0.82.0
Draw paints the axis frame, then each series back-to-front as a filled band under its polyline plus the polyline stroke on top.
func (*AreaChart) Hover ¶ added in v0.89.0
func (c *AreaChart) Hover() *mvvm.Observable[bool]
Hover is the reactive hover-crosshair toggle as a shared mvvm.Observable; false draws no crosshair. Lazily created, defaulting to off.
func (*AreaChart) HoverIndex ¶ added in v0.89.0
func (c *AreaChart) HoverIndex() *mvvm.Observable[int]
HoverIndex is the reactive hovered sample index on the first series as a shared mvvm.Observable. Lazily created, defaulting to 0.
func (*AreaChart) OnEvent ¶ added in v0.100.0
OnEvent tracks the hover crosshair from the pointer (see LineChart.OnEvent).
type ArticleCard ¶ added in v0.155.0
type ArticleCard struct {
Base
// Title is the headline, wrapped to the content width over as many lines as
// it needs. Empty draws no title.
Title string
// Body is the summary, wrapped to the content width and then clamped to at
// most BodyLines lines. Empty draws no body.
Body string
// BodyLines caps the wrapped body. Zero or negative selects the default
// (DefaultArticleBodyLines) so a caller can leave it unset.
BodyLines int
// Meta is the optional byline strip drawn under the body; nil (or an
// all-hidden strip) draws nothing and reserves no space.
Meta *CardMeta
}
ArticleCard is a text-led content card: a wrapped headline, a summary body wrapped and truncated to at most a few lines, and an optional CardMeta strip. It is the row a news / blog / discussion feed is built from, where the words carry the item and there is no lead image.
Layout (top to bottom, inside the CardPadX/Y inset):
┌──────────────────────────┐ │ Wrapped headline over as │ ← Title, wrapped to the content width │ many lines as it needs │ │ A summary that wraps and │ ← Body, wrapped then clamped to BodyLines, │ is cut to BodyLines with… │ the last kept line ellipsised on overflow │ author · 3h · ▲12 · 💬4 │ ← Meta (optional) └──────────────────────────┘
The body is wrapped to the content width and then truncated to at most BodyLines lines; when the summary is longer, the last kept line ends in an ellipsis so the cut reads as deliberate. ArticleCard is passive content — no hover, no selection.
func NewArticleCard ¶ added in v0.155.0
func NewArticleCard(title, body string, meta *CardMeta) *ArticleCard
NewArticleCard builds an ArticleCard with a title, a summary body and an optional meta strip (nil for none). The body uses DefaultArticleBodyLines; set the BodyLines field afterwards to change the cap.
func (*ArticleCard) A11y ¶ added in v0.155.0
func (c *ArticleCard) A11y() A11yInfo
A11y reports the article card as a group named by its title.
func (*ArticleCard) Children ¶ added in v0.155.0
func (c *ArticleCard) Children() []Widget
Children yields the meta strip when present so a generic walk (accessibility, text selection) reaches it. The title and body are drawn directly and are not sub-widgets.
func (*ArticleCard) Draw ¶ added in v0.155.0
func (c *ArticleCard) Draw(p painter.Painter, theme *Theme)
Draw paints the frame, the wrapped title, the clamped body and the meta strip. Content fills exactly Measure(Bounds().W): the same layout drives both.
func (*ArticleCard) Measure ¶ added in v0.155.0
func (c *ArticleCard) Measure(width int) int
Measure reports the card's height at the given outer width — the wrapped title, the clamped body and the meta strip stacked with CardGapY between them, plus the CardPadY inset top and bottom.
type Avatar ¶ added in v0.8.0
type Avatar struct {
Base
Initials string
// Color is the body fill. Leave at the zero RGBA to fall through to
// Theme.Accent (the theme-tracking default); set to any opaque RGBA
// to pin the avatar to a per-user tint.
Color RGBA
}
Avatar renders a user identity chip: a rounded-square body filled in a solid colour with the user's Initials centred inside it in the accent-inverted ink. Colour resolves to Theme.Accent when the caller-supplied Color is the zero RGBA (a natural default that respects the theme); a caller wanting a per-user tint sets Color to any opaque RGBA and it will be honoured verbatim.
The rounded shape is faked by clipping the four corner pixels — the same three-band recipe Badge uses to look like a pill without touching a curve primitive. This keeps Avatar allocation-free and portable to every Painter back-end (PixelPainter, CellPainter, SvgPainter).
Auto-sizing: if Bounds().W is zero the first Draw() resizes the avatar to AvatarSize x AvatarSize (or AvatarSize x preserved-H when H is non-zero). A pre-sized Bounds is honoured verbatim so a fixed layout column doesn't shift when the widget is dropped in.
Avatar is passive: it displays and does not respond to input. The parent view is responsible for positioning it (typically top-left of a message row or the leading edge of a menu item).
func NewAvatar ¶ added in v0.8.0
NewAvatar constructs an Avatar carrying the given initials. Bounds default to zero so the first Draw() auto-sizes the widget to AvatarSize x AvatarSize. Color defaults to the zero RGBA so the body tracks Theme.Accent unless the caller pins it.
func (*Avatar) Draw ¶ added in v0.8.0
Draw paints the rounded-square body then centres Initials on top. If Bounds().W is zero the widget resizes itself to AvatarSize x AvatarSize (H preserved when already non-zero) before painting.
Body colour is Color when non-zero, otherwise Theme.Accent. Ink is accentInk(theme) so a GTK-loaded theme's OnAccent override is honoured with a fall-through to Theme.Background — the same rule Table + Button use for their accent-face branches.
type Backdrop ¶ added in v0.71.0
type Backdrop struct {
Base
// Fill is the solid background colour. The zero value uses theme.Background.
Fill painter.RGBA
// Grid is the grid-line colour. The zero value uses theme.Border.
Grid painter.RGBA
// Step is the grid spacing in painter units. Step <= 0 draws no grid.
Step int
// Radius rounds the filled rectangle's corners by that many units. The zero
// value (0) fills a plain rectangle, byte-identical to before this field
// existed; a positive value fills a rounded rectangle — the ground of a pill /
// chip / badge a host composites an icon or label over, so that ground is a
// widget rather than a hand-drawn FillRoundRect. A grid (Step > 0) is drawn as
// before, unaffected by the rounding.
Radius int
// Stroke, when its alpha is non-zero, outlines the (optionally rounded) fill in
// that colour — the border of a pill / chip. The zero value (A==0) draws no
// border, byte-identical to before this field existed.
Stroke RGBA
// StrokeWidth is the border thickness in units; it applies only when Stroke is
// set, and a value < 1 is treated as 1.
StrokeWidth int
// NoFill suppresses the ground fill, leaving only the Stroke (and the grid, if
// any): an outline-only decoration drawn OVER content that has to stay
// visible — a focus ring around a pane, a drop-target highlight, a selection
// marquee. Without it such an outline is a hand-drawn StrokeRoundRect in the
// host, because a zero-value Fill means "the theme's Background" rather than
// "no background", and there is no transparent colour that says otherwise.
// The zero value (false) fills as before, byte-identical.
NoFill bool
// GradientTo, when its alpha is non-zero, fills the ground as a linear
// gradient from Fill (the start edge) to GradientTo (the end edge) along
// GradientDir, instead of a solid Fill — the toolbar/panel face a host would
// otherwise hand-draw with a per-pixel PutPixel loop. Gradient fills a
// rectangle (Radius is ignored while it is set). The zero value (A==0) keeps
// the solid Fill, byte-identical to before this field existed.
GradientTo painter.RGBA
// GradientDir is the gradient's direction — vertical (the default), horizontal,
// diagonal or cross-diagonal. Meaningful only when GradientTo is set.
GradientDir GradientDir
// Bevel draws a 1-pixel 3D bevel around the fill: none (the default), raised
// (a bright top+left over a dark bottom+right — a pushed-out Fluxbox toolbar
// section) or sunken (the inverse). The zero value (BevelNone) draws no bevel,
// byte-identical to before this field existed.
Bevel BevelKind
// Interactive makes the Backdrop catch pointer events. The zero value
// (false) is event-transparent: HitTest returns false so clicks pass
// through to whatever is composited over the backdrop — the least-
// surprising default for a decorative ground. Set it true for a backdrop
// that should consume clicks (a modal scrim shielding the content beneath).
Interactive bool
}
Backdrop is a decorative full-bounds ground: it fills its rectangle with a solid colour and, when Step > 0, overlays a regular grid of 1-unit lines every Step units. It draws no children and handles no events — the plain backing a host composites the rest of a scene on top of (a desktop wallpaper, a canvas backing sheet, a chart plotting area).
Both colours are optional: a zero-value Fill falls back to the theme's Background and a zero-value Grid to the theme's Border, so a Backdrop dropped in with no configuration reads sensibly under any theme. A host that wants an exact palette (a compositor matching its own desktop colours) sets Fill and Grid explicitly.
The grid is painted as 1-unit FillRects rather than StrokeRect hairlines so it renders identically on both the pixel and cell back-ends (a CellPainter has no sub-cell stroke); the lines start at the top-left of Bounds and repeat every Step, matching a host that draws a world-aligned grid from the origin.
A Backdrop is event-transparent by default. It is typically the first, full-cover child of a scene, over which a host composites the interactive widgets. Because a container routes an event to the first child whose HitTest covers the point (see Overlay), a full-cover Backdrop that reported hits would intercept every click meant for a widget drawn on top of it. So its HitTest returns false by default and pointer events pass THROUGH to the siblings/content behind it — the same "decorative, non-interactive" idiom as Label and Scrollbar. Set Interactive to opt back in (e.g. a modal scrim that deliberately swallows clicks aimed at the content beneath it).
func NewBackdrop ¶ added in v0.71.0
NewBackdrop builds a Backdrop with a solid fill and a grid every step units (step <= 0 = no grid). Passing the zero RGBA for either colour selects the theme's Background (fill) or Border (grid) at draw time.
func (*Backdrop) A11y ¶ added in v0.130.0
A11y reports the Backdrop as presentational: it dims what is behind a modal and holds nothing to read.
func (*Backdrop) Draw ¶ added in v0.71.0
Draw fills the bounds and overlays the grid. An empty rectangle paints nothing; a non-positive Step paints only the fill. A positive Radius fills a rounded rectangle instead of a plain one; a non-zero Stroke outlines it. With NoFill set the fill is skipped entirely and only the outline (and grid) is painted, leaving whatever is already there showing through.
func (*Backdrop) HitTest ¶ added in v0.78.0
HitTest reports whether the Backdrop should receive a pointer event at (px, py). It returns false unless Interactive is set, so by default a full-cover backdrop lets clicks pass through to the widgets composited over it (the Label/Scrollbar pass-through idiom). When Interactive is set it behaves like any other widget, hit-testing against its Bounds.
type Badge ¶ added in v0.7.0
type Badge struct {
Base
Text string
Fill RGBA // pill body colour; zero (A==0) => Theme.Accent
Ink RGBA // text colour; zero (A==0) => Theme.Background
}
Badge is a small pill-shaped counter or indicator — the "12" that hangs off an inbox icon, the "NEW" beside a menu item. Renders Text inside a rounded-pill body filled in Fill (Theme.Accent by default) with the ink in Ink (Theme.Background by default) for contrast.
A Badge is passive: it displays a value + does not respond to input. The parent widget (button, menu item, ...) is responsible for positioning it in the top-right corner or wherever the design puts it.
Per-badge colour: Fill overrides the pill body colour and Ink the text colour. Both default to the zero RGBA, in which case Draw falls back to Theme.Accent / Theme.Background — so a plain NewBadge keeps the theme look, while a caller that needs a categorical colour (a per-source tag, a severity chip, ...) sets Fill/Ink without having to hand-draw its own pill. A fully-transparent colour (A==0) is treated as "unset"; callers wanting a see-through badge is not a use case the widget serves.
Auto-sizing: if the caller sets Bounds().W to 0, the first Draw() resizes the Bounds to the text width plus BadgePadX on each side (plus GlyphHeight() + BadgePadY on each side vertically if H is also 0). This spares the caller from having to compute glyph widths just to paint a two-digit counter. A pre-sized Bounds is honoured verbatim so a fixed-width layout column doesn't shift when the digit count changes.
func NewBadge ¶ added in v0.7.0
NewBadge constructs a Badge with the given text. Bounds default to zero so the first Draw() auto-sizes the pill to the text.
func (*Badge) Draw ¶ added in v0.7.0
Draw paints the pill body + centred text. If Bounds().W is zero the widget resizes itself to fit its Text (and Bounds().H is filled in too if it was zero) before painting; a pre-sized Bounds is preserved.
The pill body is a full rounded-rect painted through the painter's FillRoundRect (radius = half the shorter side, so short pills read as a stadium and tall ones as a circle). Back-ends that cannot round (a cell grid) degrade to a square fill. Fill/Ink override the body/text colours; an unset (transparent) colour falls back to the theme.
type Banner ¶ added in v0.8.0
type Banner struct {
Base
Text string
ButtonLabel string
OnAction func()
Icon func(p painter.Painter, r Rect, ink RGBA)
// contains filtered or unexported fields
}
Banner is a full-width persistent inline message strip, modelled on GTK 4's AdwBanner. Distinct from Alert (persistent, coloured by severity) in two ways:
- Banner is REVEAL-driven: Revealed toggles the whole strip on and off, letting the host wire dismiss and re-show without dropping the widget from the tree.
- Banner carries an optional right-aligned action button; a click inside the button fires OnAction. Alert has no interactive slot.
The banner paints in Theme.Accent so it reads as a system message rather than a semantic-severity Alert; the action button is drawn as a bordered box in the accent-inverted ink so it stays legible.
An optional leading Icon lets the host prefix the message with a glyph (a padlock for a sign-in prompt, a warning triangle, ...). When set, Draw reserves a GlyphHeight square at the leading edge, invokes Icon with that rect + the banner ink, and shifts the Text right past it. Icon is nil by default, leaving the text flush against BannerPadX as before, so existing callers are unaffected.
func NewBanner ¶ added in v0.8.0
NewBanner constructs a Banner with the given Text. Revealed starts true so a freshly-constructed banner is visible; ButtonLabel is empty by default (no action slot rendered).
func (*Banner) A11y ¶ added in v0.40.0
A11y reports the Banner as a status region named by its message.
func (*Banner) Draw ¶ added in v0.8.0
Draw paints the accent-filled strip + the Text ink. A non-nil Icon is drawn first as a leading square inset by BannerPadY top and bottom (so the icon scales with the banner height rather than a fixed font glyph box, keeping it legible on a high-DPI / scaled surface), and the Text is shifted right past it. When ButtonLabel is non-empty an outlined action button is drawn right-aligned inside BannerPadX of the trailing edge. Nothing drawn when !Revealed.
func (*Banner) OnEvent ¶ added in v0.8.0
OnEvent handles a click inside the action button. Events with a Kind other than EventClick are ignored; a click that falls outside the button rect is dropped; a click on a Banner without an action button (empty ButtonLabel) is dropped; a click with a nil OnAction is dropped silently -- the button is drawable but inert.
func (*Banner) Revealed ¶ added in v0.8.0
func (b *Banner) Revealed() *mvvm.Observable[bool]
Revealed is the banner's reactive reveal toggle as a shared mvvm.Observable: the host Sets it false to dismiss and true to re-show without dropping the widget from the tree. Lazily created, defaulting to hidden (the zero-value Banner{}); NewBanner starts it revealed.
type BarChart ¶ added in v0.13.0
type BarChart struct {
Base
Values []float64
Max float64 // top of the Y axis; when <= 0, taken from the data
// contains filtered or unexported fields
}
BarChart plots one series of non-negative Values as vertical bars over a left+bottom axis frame -- the categorical companion to LineChart. Bars share the plot width evenly with a 1-unit gutter between them and scale to the tallest value (or an explicit Max). Display-only.
It renders through painter.Painter, so the same chart draws as pixels (WUI/GUI) or promoted cells (TUI). An empty series draws just the axes.
Example ¶
ExampleBarChart plots non-negative values as vertical bars.
package main
import (
"github.com/go-widgets/painter"
"github.com/go-widgets/toolkit"
)
// newSurface returns a PixelPainter over a fresh w×h RGBA buffer — the render
// target the examples draw into. A CellPainter would render the same widgets to
// a terminal grid instead.
func newSurface(w, h int) *painter.PixelPainter {
return painter.NewPixelPainter(make([]byte, 4*w*h), w, h)
}
func main() {
chart := toolkit.NewBarChart([]float64{4, 7, 2, 8, 5})
chart.SetBounds(toolkit.Rect{X: 0, Y: 0, W: 200, H: 80})
chart.Draw(newSurface(200, 80), toolkit.DefaultLight())
}
Output:
func NewBarChart ¶ added in v0.13.0
NewBarChart builds a BarChart over the given values with an auto Y max.
func (*BarChart) A11y ¶ added in v0.40.0
A11y reports the BarChart as an img carrying its bar count.
func (*BarChart) Hover ¶ added in v0.90.0
func (c *BarChart) Hover() *mvvm.Observable[bool]
Hover is the reactive hover-highlight toggle as a shared mvvm.Observable; false draws no hover affordance. Lazily created, defaulting to off.
func (*BarChart) HoverIndex ¶ added in v0.90.0
func (c *BarChart) HoverIndex() *mvvm.Observable[int]
HoverIndex is the reactive hovered index as a shared mvvm.Observable. Lazily created, defaulting to 0.
func (*BarChart) OnEvent ¶ added in v0.100.0
OnEvent outlines the bar column under the pointer, clearing when it leaves.
type BarSegment ¶ added in v0.36.0
BarSegment is one band of a SegmentedBar: a non-negative Value (its share of the whole), the RGBA it paints with, and an optional Label (reserved for a future legend / tooltip — Draw does not render it today).
type Base ¶
type Base struct {
// Font, when non-nil, overrides the global active font for this widget
// only. nil means "inherit the active font" (the default).
Font Font
// contains filtered or unexported fields
}
Base provides default Bounds/SetBounds/HitTest impls so a widget embedding it only has to implement Draw + OnEvent. Embedding is optional but convenient.
Font is an optional per-widget font override. When nil (the zero value, the default for every widget) the widget lays out + renders against the package-level active font (see SetFont / CurrentFont), so it behaves exactly as if per-widget fonts did not exist. When set — e.g.
badge.Font, _ = NewTrueTypeFont(myFontTTF, 10) // small tag title.Font, _ = NewTrueTypeFont(myFontTTF, 22) // large heading
that single widget measures + paints its text with that font while every other widget keeps using the global one. Widgets consult it through the font-aware helpers (EffectiveFont / textWidth / drawText / glyphHeight / glyphAdvance) instead of the package-level TextWidth / DrawText / GlyphHeight, so a font swap is scoped to the widget that sets it.
func (*Base) Disabled ¶ added in v0.100.0
func (b *Base) Disabled() *mvvm.Observable[bool]
Disabled is the widget's reactive inert-state flag as a shared mvvm.Observable: true makes an interactive widget inert (its OnEvent early-returns — no click / drag / scroll / hover / key effect — and its Draw paints a muted, greyed face), false is the normal interactive state. Inherited by every widget that embeds Base, so a caller disables any control with `w.Disabled().Set(true)`. Lazily created, defaulting to enabled (false), so every widget is interactive by default.
func (*Base) EffectiveFont ¶ added in v0.34.0
EffectiveFont is the font this widget renders with: its own Font override if one is set, otherwise the package-level active font (CurrentFont). It never returns nil, so callers can measure/draw through it unconditionally.
type BevelDockStyle ¶ added in v0.179.0
type BevelDockStyle struct{}
BevelDockStyle is the Fluxbox look: a flat ground and square, 3D-bevelled item faces — a RAISED bevel at rest, a SUNKEN one when active (the "pressed in" current app) — with a running dot. Ink stays OnSurface on the light face.
func (BevelDockStyle) DrawFace ¶ added in v0.179.0
func (BevelDockStyle) DrawFace(p painter.Painter, theme *Theme, r Rect, st DockItemState) RGBA
func (BevelDockStyle) DrawGround ¶ added in v0.179.0
func (BevelDockStyle) DrawGround(p painter.Painter, theme *Theme, r Rect)
type BevelKind ¶ added in v0.183.0
type BevelKind int
BevelKind selects a Backdrop's 1-pixel 3D edge bevel.
const ( // BevelNone draws no bevel (the default). BevelNone BevelKind = iota // BevelRaised draws a bright top+left over a dark bottom+right, so the face // reads as pushed out toward the viewer. BevelRaised // BevelSunken is the inverse — dark top+left, bright bottom+right — so the // face reads as pressed in. BevelSunken )
type Binding ¶ added in v0.151.0
Binding pairs a chord with the action it triggers and the scope it applies in. Returned by Keymap.Bindings as an immutable snapshot.
type BlockKind ¶ added in v0.218.0
type BlockKind int
BlockKind names the block types SetBlockType can convert the caret's block to: a paragraph, one of six heading levels, a code block or a block quote. The heading levels are contiguous so BlockH1+n is level n+1.
type Border ¶ added in v0.58.0
type Border struct {
Base
North, South, East, West, Center Widget
NorthSize, SouthSize, EastSize, WestSize int
// NorthSplit/… add a draggable splitter between that edge region and the
// centre (with an optional resizable split). The app drives the drag — like Paned —
// via SplitHandleAt (which handle a point is on) and ResizeSplit (set the new
// size); OnResize fires after each resize.
NorthSplit, SouthSplit, EastSplit, WestSplit bool
OnResize func(side DockSide, size int)
// contains filtered or unexported fields
}
Border arranges up to five named regions — North, South, West, East and Center — the classic five-region border layout for application shells. North and South span the full width and take a fixed height; West and East then span the height that remains between them and take a fixed width; Center fills whatever is left. The precedence is structural, so regions may be assigned in any order and still lay out correctly (unlike Dock, which carves in insertion order).
Any region may be nil (that edge simply contributes no band). Sizes are the extent along each region's own axis — NorthSize/SouthSize are heights, West/EastSize are widths — clamped to what the container can give (negative → 0).
Border is a Widget: Draw paints every non-nil region; OnEvent routes by Bounds, translating into the matched region's local space.
func NewBorder ¶ added in v0.58.0
func NewBorder() *Border
NewBorder builds an empty Border; assign the region fields and their sizes directly before the first SetBounds.
func (*Border) Children ¶ added in v0.137.0
Children yields the five regions in reading order: the edges clockwise from the top, then the centre.
func (*Border) Draw ¶ added in v0.58.0
Draw paints every non-nil region, then any splitter handles over the seams.
func (*Border) OnEvent ¶ added in v0.58.0
OnEvent forwards to the first region whose Bounds contains the point, translated into that region's local space.
func (*Border) ResizeSplit ¶ added in v0.63.0
ResizeSplit sets the given edge region's size (clamped to [0, the border's extent on that axis]), re-lays out, and fires OnResize. The app computes size from the drag — e.g. NorthSize + dy for the north handle.
type BorderLayout ¶ added in v0.59.0
type BorderLayout struct{}
BorderLayout arranges items by Region: North/South span the full width, then West/East span the height between them, then Center fills the rest. Item.Size is the edge band's thickness. The border layout, over the shared dockCarve.
func (BorderLayout) Arrange ¶ added in v0.59.0
func (BorderLayout) Arrange(r Rect, items []Item)
Arrange carves the edge regions off in N,S,W,E order, then fills Center.
type BoxAlign ¶ added in v0.56.0
type BoxAlign int
BoxAlign controls how HBox/VBox position each child on the CROSS axis (the axis perpendicular to the flow: vertical for HBox, horizontal for VBox). The zero value BoxStretch fills the cross axis — the historical behaviour — so existing layouts are unchanged. The others place the child at its natural cross size (reported via the optional Measurer, else its current cross Bounds) against the start, centre, or end of the box. The box `align` model: stretch | start | center | end.
type BoxLayout ¶ added in v0.59.0
BoxLayout stacks items along one axis (horizontal by default; set Vertical for a column), honouring per-item Flex/Size and the Align/Pack options — the same horizontal/vertical box. It reuses the same sizing/alignment primitives as HBox/VBox.
Spacing is taken LITERALLY (negatives clamped to 0), matching HBox/VBox: the zero-value BoxLayout{} therefore has a flush, zero-gap axis. Use NewBoxLayout to get a layout pre-seeded with the DefaultBoxSpacing (4px) gap.
func NewBoxLayout ¶ added in v0.64.0
func NewBoxLayout() *BoxLayout
NewBoxLayout returns a *BoxLayout with Spacing seeded to DefaultBoxSpacing, the constructor analogue of NewHBox/NewVBox. The zero-value BoxLayout{} keeps a literal 0-gap axis; set fields on the returned value to configure it further.
type BoxPack ¶ added in v0.56.0
type BoxPack int
BoxPack controls how HBox/VBox distribute SLACK on the MAIN axis (the flow axis) when the children do not fill it — i.e. when no flex child absorbs the space. The zero value PackStart leaves the slack after the last child (historical). PackCenter splits it either side; PackEnd puts it all before the first child. The box `pack` model (start|center|end). With any flex child the slack is zero, so Pack has no visible effect then.
type Breadcrumbs ¶ added in v0.7.0
type Breadcrumbs struct {
Base
Segments []string
// OnSelect, when non-nil, fires with the 0-based index of the crumb the
// user clicked. Nil (the zero value) keeps the widget passive.
OnSelect func(i int)
}
Breadcrumbs is a horizontal navigation path — "Home > Docs > Reference" — rendered as a sequence of Segments separated by a chevron character. Segment text uses Theme.OnBackground; each chevron uses Theme.Border so it reads as a subtle divider rather than another clickable label.
A click on a crumb fires OnSelect with that segment's index, so "Home > Docs > Reference" navigates up when the user clicks an ancestor crumb. OnSelect nil leaves the widget an inert display: the same per-segment X layout Draw builds is walked by OnEvent to hit-test the clicked crumb, so the click target and the drawn glyph can never drift apart.
func NewBreadcrumbs ¶ added in v0.7.0
func NewBreadcrumbs(segments []string) *Breadcrumbs
NewBreadcrumbs constructs a Breadcrumbs with the given segments. A nil or empty Segments slice renders as a no-op — Draw exits without painting anything.
func (*Breadcrumbs) A11y ¶ added in v0.40.0
func (b *Breadcrumbs) A11y() A11yInfo
A11y reports the Breadcrumbs as navigation named by its full path.
func (*Breadcrumbs) Draw ¶ added in v0.7.0
func (b *Breadcrumbs) Draw(p painter.Painter, theme *Theme)
Draw paints each segment followed by a separator (except after the last one). Segments are vertically centred inside Bounds when Bounds.H exceeds GlyphHeight(), otherwise they anchor at Bounds.Y.
func (*Breadcrumbs) OnEvent ¶ added in v0.104.0
func (b *Breadcrumbs) OnEvent(ev Event)
OnEvent fires OnSelect(i) when a click lands on the i-th crumb. It walks the exact per-segment X layout Draw builds (textWidth(seg) then, between crumbs, gap + separator + gap), so the hit region matches the painted glyphs. Clicks in the inter-crumb separator gap, or when OnSelect is nil, are ignored. Event coordinates are widget-local, so the first crumb starts at local x == 0.
type Browser ¶ added in v0.112.0
type Browser struct {
Base
// with the target to render and the pixel width the content area currently
// offers; the host renders off-thread and calls Deliver / SetProgress back.
// Nil is safe (navigation still updates history + loading state).
OnNavigate func(target string, width int)
// OnOpenExternal, when set, is the seam for an "open in the system browser"
// affordance: OpenExternal() invokes it with the current URL. Optional; nil
// is safe.
OnOpenExternal func(url string)
// HideScrollbar suppresses the Browser's OWN content scrollbars (both axes),
// for a host that overlays its own — e.g. a reader that draws one shared
// Scrollbar style down every panel and wants the preview's web view to match
// the feed and sidebar exactly rather than show the embedded house style.
// Only the paint is suppressed; wheel scrolling still works. The host reads
// ScrollExtent to size and place its replacement bar, exactly as it does with
// TreeView.HideScrollbar + TreeView.ScrollExtent.
HideScrollbar bool
// BackIcon / ForwardIcon / ReloadIcon / ZoomOutIcon / ZoomInIcon are the
// host-supplied vector-icon painters for the toolbar buttons — the same seam
// as SearchEntry.Icon. Each is invoked with its button's rect and the button
// face ink (which already carries the enabled / disabled tint), so the host
// draws a real arrow / refresh / minus / plus glyph centred in the button.
// The toolkit ships no icon set of its own (keeping its zero-dependency
// contract); a host wires these to, e.g., an Iconoir binding. Each is
// nil-safe: a nil hook falls back to the plain text label, so headless
// renders and existing callers keep working unchanged.
BackIcon func(p painter.Painter, r Rect, ink RGBA)
ForwardIcon func(p painter.Painter, r Rect, ink RGBA)
ReloadIcon func(p painter.Painter, r Rect, ink RGBA)
ZoomOutIcon func(p painter.Painter, r Rect, ink RGBA)
ZoomInIcon func(p painter.Painter, r Rect, ink RGBA)
// FitIcon is the host-supplied painter for the best-fit zoom button (the
// third member of the zoom group, next to zoom-out / zoom-in). Same nil-safe
// seam as the other toolbar icons: a nil hook falls back to the text label.
FitIcon func(p painter.Painter, r Rect, ink RGBA)
// LeadingIcon, when set, paints a status glyph at the LEFT of the address
// field — e.g. an SSL padlock whose look the host varies by certificate state
// (secure / insecure / none). Same painter seam as the toolbar icons; the
// address text indents to its right. Nil → no leading slot (text starts at
// the normal inset).
LeadingIcon func(p painter.Painter, r Rect, ink RGBA)
// BookmarkIcon, when set, paints a toggle glyph at the RIGHT of the address
// field — e.g. a star, filled when on. It takes the current bookmark state so
// the host can draw the on/off variant. Clicking the slot flips the shared
// [Browser.Bookmarked] Observable. Nil → no bookmark slot.
BookmarkIcon func(p painter.Painter, r Rect, ink RGBA, on bool)
// OnChange fires once whenever any observable-relevant state mutates
// (navigation, tab add/close/switch, loading/progress change, address edit,
// delivered page). A mvvm binder subscribes to push state into Observables.
// It is additive to OnNavigate and never replaces it. Nil is safe.
OnChange func()
// Phase drives the indeterminate loading bar animation (0..1); advance it
// from the host frame loop via Tick, exactly like Spinner.Phase.
Phase float64
// Scale multiplies every chrome metric — the tab-strip and toolbar heights,
// the pads, the button squares, the address slot, the loading-bar thickness
// and the tab-pill sizing — for HiDPI / device-pixel hosts. A host that lays
// the widget out in DEVICE pixels (e.g. a Retina surface at devicePixelRatio
// 2, optionally times a UI zoom) sets Scale = devicePixelRatio*zoom so the
// chrome stays physically the right size instead of shrinking to half. The
// zero value (and 1) mean "no scaling": layout is byte-identical to a build
// without the field, so existing callers are unaffected. Values <= 0 are
// treated as 1. Metrics are rounded (not truncated) at every use so the
// scaled buttons/tabs stay pixel-aligned and do not drift. The host-supplied
// icon hooks fill the now-larger button rects, so the glyphs grow with the
// buttons automatically.
Scale float64
// HideChrome, when true, hides BOTH the toolbar and the tab strip: neither is
// drawn and neither takes any vertical space, so the page content area fills
// the entire widget bounds. Toolbar clicks and address-field editing are then
// inert (there are no hit targets), but navigation still works
// programmatically (Open / Navigate / Back / Forward / Reload / SetZoom / …),
// so a host can drive a chromeless page view. The loading bar still shows over
// the content while a load is in flight. The zero value is false → the chrome
// is shown exactly as before, so existing callers are unaffected.
HideChrome bool
// contains filtered or unexported fields
}
Browser is a reusable mini web-browser chrome: a tab strip, a Back / Forward / Reload toolbar, an editable address field, a loading progress bar and a scrollable content area that shows a page render. It is deliberately renderer-agnostic and fully synchronous — the widget NEVER fetches or renders a page itself and imports no networking or HTML engine. Instead it exposes a seam: the host sets OnNavigate, and whenever the widget needs a page rendered it invokes OnNavigate(target, width). The host runs the actual fetch/render asynchronously elsewhere and calls back into the widget's synchronous Deliver and SetProgress methods on its own UI thread. This mirrors the proven callback seam used by other host-driven widgets and keeps the toolkit's zero-dependency, no-network-in-tests contract intact.
Browser is a plain MVVM View: it holds view state, exposes it through exported getters (CurrentURL, CanBack, CanForward, TabCount, Loading, Progress, ActiveTitle, TabTitle), and offers command-style methods (Open, Navigate, Back, Forward, Reload, CloseTab) each with a matching Can… guard where relevant. A single OnChange hook fires whenever any observable-relevant state mutates, so a binder in the mvvm layer can push state into Observables WITHOUT the toolkit ever importing mvvm (which would invert the toolkit↔mvvm layering).
func NewBrowser ¶ added in v0.112.0
func NewBrowser() *Browser
NewBrowser builds an empty Browser in the default MultiTab mode at 1.0 zoom.
func (*Browser) A11y ¶ added in v0.130.0
A11y reports the Browser as a document named by the page it is showing.
func (*Browser) ActiveIndex ¶ added in v0.112.0
ActiveIndex reports the active tab index (0 when there are no tabs).
func (*Browser) ActiveTitle ¶ added in v0.112.0
ActiveTitle returns the active tab's title, falling back to its URL when the title is empty; "" when there are no tabs.
func (*Browser) AddressFocused ¶ added in v0.124.0
AddressFocused reports whether the address field currently holds keyboard focus (a prior click landed in it), so a host can route a copy chord to Browser.CopyAddress instead of its own copy action.
func (*Browser) AddressText ¶ added in v0.124.0
AddressText returns the text the address field shows: the editable buffer while focused, else the current page URL.
func (*Browser) Back ¶ added in v0.112.0
func (b *Browser) Back()
Back moves the active tab's cursor one step back and re-fetches that URL. It is a no-op with no active tab or at the start of history.
func (*Browser) Bookmarked ¶ added in v0.118.0
func (b *Browser) Bookmarked() *mvvm.Observable[bool]
Bookmarked is the shared bookmark-toggle Observable of the address field: the host binds it to its bookmark store (Set it, or Subscribe to it), and clicking the bookmark slot flips it. It replaces the former Bookmarked field + OnBookmarkToggle callback — bookmark state now flows only through MVVM.
func (*Browser) CanBack ¶ added in v0.112.0
CanBack reports whether the active tab can go back (history behind the cursor).
func (*Browser) CanFit ¶ added in v0.134.0
CanFit reports whether a best-fit zoom is possible: there is an active tab with a delivered render and a non-empty content rect to fit it into.
func (*Browser) CanForward ¶ added in v0.112.0
CanForward reports whether the active tab can go forward (history ahead of the cursor).
func (*Browser) CanZoomIn ¶ added in v0.113.0
CanZoomIn reports whether the zoom can still increase (below BrowserMaxZoom).
func (*Browser) CanZoomOut ¶ added in v0.113.0
CanZoomOut reports whether the zoom can still decrease (above BrowserMinZoom).
func (*Browser) CloseTab ¶ added in v0.112.0
CloseTab drops tab i and its state; if it was the active tab a neighbour is activated. Out-of-range indices are ignored.
func (*Browser) CopyAddress ¶ added in v0.124.0
CopyAddress copies the address field's text to the toolkit-wide clipboard and flags a select-all highlight (visual feedback of what was copied), reporting the text and whether anything was copied. It is a no-op returning ("", false) when the field is not focused or is empty — so a host can try it first and fall back to another copy action. Mirrors Entry's "no selection → copy the whole value" model.
func (*Browser) CurrentURL ¶ added in v0.112.0
CurrentURL returns the active tab's current URL, or "" when there are no tabs.
func (*Browser) Deliver ¶ added in v0.112.0
func (b *Browser) Deliver(target string, pixels []byte, imgW, imgH, width int, links []BrowserLink, title string)
Deliver hands the widget a finished render for target: it delivers a final stage (loading clears). See DeliverStage. The scroll position was reset when the navigation to target began (startLoad), so a delivered render is shown from wherever the user has scrolled to — deliveries do not yank the page.
func (*Browser) DeliverStage ¶ added in v0.133.0
func (b *Browser) DeliverStage(target string, pixels []byte, imgW, imgH, width int, links []BrowserLink, title string, final bool)
DeliverStage delivers one render for target, distinguishing a final render from an intermediate progressive frame. When target matches the active tab's current URL the render (pixels + dimensions + width), links and title are stored; a stale or non-active delivery is ignored.
With final=true the load is complete and loading clears. With final=false it is one staged frame of a still-running progressive render (a fast first paint, then refinements): the content updates but loading stays on, so the progress indicator keeps animating and the page does not read as "done" until the final frame lands. Neither form resets the scroll position — that happens once when the navigation begins (startLoad) — so a staged render refines in place instead of snapping to the top on every frame.
func (*Browser) Draw ¶ added in v0.112.0
Draw paints the chrome (tab strip when shown, toolbar) and the content area (page render + loading bar), strictly within Bounds.
func (*Browser) FitZoom ¶ added in v0.134.0
func (b *Browser) FitZoom()
FitZoom sets the zoom so the WHOLE current page/image fits within the content rect on both axes. The natural display size at zoom 1 is dispW0 = cr.W (pages render fit-to-width) and dispH0 = imgH*cr.W/imgW; the fit factor is min(1, cr.W/dispW0, cr.H/dispH0) — capped at 1 so a page already smaller than the pane is not blown up — then clamped to [BrowserMinZoom, BrowserMaxZoom] by SetZoom (which also re-clamps scroll). It is a no-op when there is no render or the content rect is empty.
func (*Browser) Forward ¶ added in v0.112.0
func (b *Browser) Forward()
Forward moves the active tab's cursor one step forward and re-fetches that URL. It is a no-op with no active tab or at the end of history.
func (*Browser) Loading ¶ added in v0.112.0
Loading reports whether the active tab has an in-flight load.
func (*Browser) Navigate ¶ added in v0.112.0
Navigate performs in-tab navigation to href (a link click or a typed address): it truncates any forward history, appends href, marks the tab loading and invokes OnNavigate. With no active tab it falls back to Open.
func (*Browser) OnEvent ¶ added in v0.112.0
OnEvent routes widget-local input: clicks to the tab strip / toolbar / address field / page links, character + Backspace + Enter to the focused address field, and wheel scroll to the content. It early-returns when Disabled.
func (*Browser) Open ¶ added in v0.112.0
Open opens target in a tab. In MultiTab it adds a new active tab (evicting the oldest past BrowserMaxTabs); in SingleTab it replaces the one tab. It seeds history, marks the tab loading, sets a pending render width and invokes OnNavigate.
func (*Browser) OpenExternal ¶ added in v0.112.0
func (b *Browser) OpenExternal()
OpenExternal invokes OnOpenExternal with the current URL, the seam for an "open in the system browser" affordance. No-op when the hook is unset or there is no current URL.
func (*Browser) Progress ¶ added in v0.112.0
Progress reports the active tab's determinate download fraction (0 when there is no tab or SetProgress was never called this load).
func (*Browser) Reload ¶ added in v0.112.0
func (b *Browser) Reload()
Reload re-fetches the active tab's current URL. It is a no-op with no active tab.
func (*Browser) ResetZoom ¶ added in v0.113.0
func (b *Browser) ResetZoom()
ResetZoom returns the zoom to 1.0 (no-op when already there).
func (*Browser) ScrollExtent ¶ added in v0.173.0
ScrollExtent reports the active page's VERTICAL scroll position in content pixels — the offset, the viewport height and the total (zoomed) page height — and whether the page overflows. A host that sets HideScrollbar and paints its own bar reads this to size and place a matching one, exactly as TreeView.ScrollExtent serves the same purpose for a windowed tree. It reports not-shown when there is no active tab, no render yet, or the page fits.
func (*Browser) SetProgress ¶ added in v0.112.0
SetProgress sets the active tab's determinate download progress (clamped to 0..1) for the in-flight load. If it is never called during a load the bar renders indeterminate (driven by Phase). No-op with no active tab.
func (*Browser) SetTabMode ¶ added in v0.112.0
SetTabMode selects MultiTab or SingleTab for subsequent Open calls.
func (*Browser) SetZoom ¶ added in v0.113.0
SetZoom sets the page-display zoom, clamped to [BrowserMinZoom, BrowserMaxZoom]. A real change re-clamps the active tab's scroll to the new (smaller) extent and fires OnChange; setting the current value is a no-op (no notification).
func (*Browser) TabTitle ¶ added in v0.112.0
TabTitle returns tab i's display title (title, or its URL when the title is empty); "" for an out-of-range index.
func (*Browser) Tick ¶ added in v0.112.0
Tick advances Phase by deltaSeconds, wrapping modulo 1 (like Spinner.Tick), so the indeterminate loading bar animates in step with the host frame loop.
func (*Browser) Zoom ¶ added in v0.113.0
Zoom reports the current page-display zoom factor (1.0 is 1:1 fit-to-width).
type BrowserLink ¶ added in v0.112.0
BrowserLink is one clickable region of a delivered page render. Rect is in RENDER-pixel coordinates (the coordinate space of the pixels the host handed to Deliver, at the render width the host was told to use); the widget maps a content-area click back into that space to hit-test it.
type Button ¶
type Button struct {
Base
OnClick func()
Style ButtonStyle // resting appearance; default is ButtonDefault
// Icon, when set, lets the host paint a real vector glyph in the button's
// face instead of the text Label — the seam other widgets use for
// host-supplied icons (mirrors Banner.Icon / SearchEntry.Icon). Draw invokes
// Icon with the button's full bounds and the current face ink (which already
// carries the pressed / disabled tint), so the glyph tracks every button
// state; the callback is responsible for centring + sizing itself within the
// rect. When nil the button falls back to drawing Label, so existing callers
// are unaffected and headless renders still show text.
Icon func(p painter.Painter, r Rect, ink RGBA)
// PressFeedback shows the pressed face on EventClick (until EventMouseUp).
// NewButton enables it; set it false to opt a button out (e.g. one whose
// action already navigates away so the flash would just flicker).
PressFeedback bool
// Flat suppresses the button's own rounded border + fill rounding, painting a
// square-cornered face only — so it can sit inside a container that owns the
// shared chrome (see ButtonGroup, which sets it on its members). The zero
// value (false) keeps the standalone rounded look, so existing callers are
// unaffected.
Flat bool
// contains filtered or unexported fields
}
Button is a clickable rectangle with a centred label. Paints a 1-pixel border in Theme.Border on a Theme.Surface body; hovered / pressed states cycle through SurfaceAlt + Accent so the user sees click feedback before the callback fires.
Wire a handler via OnClick; the button calls it from OnEvent when it receives an EventClick. Callers re-paint via Draw after any state mutation (the toolkit doesn't drive its own frame loop -- the wasmbox compositor's tick is the redraw trigger).
func NewButton ¶
NewButton constructs a Button with the given label + click handler. Handler may be nil (a no-op button is still rendered).
func (*Button) A11y ¶ added in v0.19.0
A11y reports the Button as a button role named by its label. Value surfaces the button's state when it is not resting: "selected" for the sticky, app-managed Selected flag (a pill in a selector, the current tab), else "pressed" while the transient press-feedback face is showing — so a screen reader no longer hears an active or held button identically to an idle one.
func (*Button) Draw ¶
Draw paints the button through p using theme's palette. Face cycles through Surface / SurfaceAlt (hovered) / Accent (pressed); the Label is centred in the body using the toolkit's 5x7 bitmap font. When the button is pressed the ink swaps to the theme's Background so the label stays legible against the Accent face.
func (*Button) Focused ¶ added in v0.101.0
func (f *Button) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*Button) HitRect ¶ added in v0.187.0
HitRect is the button's interactive rectangle: its drawn Bounds clamped up to the density hit-target and centred over them. Byte-identical to Bounds under DensityCompact; a small button exposes a >=44px finger target under DensityTouch without changing what it draws.
func (*Button) HitTest ¶ added in v0.187.0
HitTest reports whether a surface point falls on the button's (touch-clamped) hit rect. At compact the clamp is a pass-through so this equals the default Bounds().Contains; at touch the reachable area grows to the finger floor.
func (*Button) Label ¶
func (b *Button) Label() *mvvm.Observable[string]
Label is the button caption as a shared mvvm.Observable: a host binds it (or subscribes) instead of touching a field. Lazily created so a bare &Button{} works. When Icon is set it is drawn instead of the caption.
func (*Button) OnEvent ¶
OnEvent drives the button from pointer events: EventClick presses it (shows the pressed face + fires OnClick) and EventMouseUp releases it. Self-managing the pressed state means any host that routes the press/release pair gets the click feedback for free, without also wiring SetPressed. Other event kinds are ignored. (SetPressed remains for hosts that drive press state their own way, e.g. enter/leave dispatch.)
func (*Button) Selected ¶ added in v0.66.0
func (b *Button) Selected() *mvvm.Observable[bool]
Selected is the sticky app-managed "active" state as a shared mvvm.Observable: the host drives it from its own model (true fills the button with Accent regardless of Style); the button never flips it itself. Lazily created.
func (*Button) SetFocused ¶ added in v0.101.0
func (f *Button) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
func (*Button) SetHovered ¶
SetHovered/SetPressed are wired by the parent container's mouse dispatcher so the button can render its hover/press visual states. Direct setters (vs deducing from OnEvent kinds) keep the parent in control of state propagation -- enter/leave events would duplicate the same logic in every leaf widget.
func (*Button) SetPressed ¶
type ButtonGroup ¶ added in v0.116.0
type ButtonGroup struct {
Base
Orientation Orientation
Buttons []*Button
}
ButtonGroup is a segmented cluster of adjacent Buttons rendered as one connected control: a single rounded border around the whole group, 1-pixel dividers between members, and no per-button border (the members are drawn Flat, so the group owns the chrome). Use it for related actions that read as a unit — a Back/Forward/Reload nav cluster, a zoom -/+ pair, a view switcher.
The members are ordinary *Button widgets: set each one's Icon / Label / OnClick / Disabled / Selected as usual; the group lays them out equally along its axis, routes clicks to the member under the pointer, and paints the shared frame. Orientation is Horizontal (the zero value) or Vertical.
func NewButtonGroup ¶ added in v0.116.0
func NewButtonGroup(buttons ...*Button) *ButtonGroup
NewButtonGroup builds a group over the given buttons, marking each Flat so the group draws the shared border instead of per-button outlines.
func (*ButtonGroup) A11y ¶ added in v0.130.0
func (g *ButtonGroup) A11y() A11yInfo
A11y reports the ButtonGroup as a group carrying how many buttons it holds.
func (*ButtonGroup) Draw ¶ added in v0.116.0
func (g *ButtonGroup) Draw(p painter.Painter, theme *Theme)
Draw paints the group background, each Flat member, the inter-member dividers, and one rounded border around the whole cluster.
func (*ButtonGroup) OnEvent ¶ added in v0.116.0
func (g *ButtonGroup) OnEvent(ev Event)
OnEvent forwards the event to the member under its (group-local) coordinates. Button.OnEvent handles the press/release itself, so a routed EventClick fires that member's OnClick.
func (*ButtonGroup) SetBounds ¶ added in v0.116.0
func (g *ButtonGroup) SetBounds(r Rect)
SetBounds positions the members: equal slices along the layout axis (the last member absorbs any rounding remainder so the group fills its bounds exactly).
type ButtonStyle ¶ added in v0.10.0
type ButtonStyle int
ButtonStyle selects a button's resting fill, giving a layout visual hierarchy (macOS "prominent"/default/secondary buttons). Hover + press still override the fill on top of the style.
const ( // ButtonDefault is a Surface-faced button (the plain look). ButtonDefault ButtonStyle = iota // ButtonProminent is filled with Accent + accent-foreground text -- the // primary/default action (e.g. a calculator's operator keys, "OK"). ButtonProminent // ButtonSecondary is filled with SurfaceAlt -- a muted grey key that sits // between Default and Prominent (e.g. a calculator's C / +/- / % keys). ButtonSecondary // ButtonDanger is a Surface-faced button with a red border + red label -- a // destructive action (Delete, Remove). ButtonDanger )
type Calendar ¶
type Calendar struct {
Base
// TodayY / TodayM / TodayD are the set-once "today" pill the calendar
// highlights regardless of the viewed (Y/M); (0, 0, 0) disables it. They are
// appearance config, not reactive state.
TodayY int
TodayM int
TodayD int
// contains filtered or unexported fields
}
Calendar renders a month grid (Mon..Sun columns, up to 6 rows) for a given (Year, Month). The currently-selected day is highlighted; clicking a day-cell selects it by Setting the Calendar.Day Observable (and Calendar.Year / Calendar.Month when the click lands in an adjacent month), notifying subscribers.
Calendar takes no time-source dep; the host must pass it the current year/month/day. A "today" pill can be drawn by setting Today (year/month/day); set to (0, 0, 0) to disable it.
The reactive state is MVVM-only: the viewed year / month and the selected day live in unexported Observables exposed via Calendar.Year, Calendar.Month and Calendar.Day. A host binds them (Set / Subscribe / two-way); there are no settable Year/Month/Day fields.
The header carries prev/next arrows ("<" / ">"): clicking them steps the viewed month (wrapping the year at the Dec/Jan boundary) by Setting the Year / Month Observables. PrevMonth / NextMonth expose the same navigation programmatically.
func NewCalendar ¶
NewCalendar builds a Calendar for the given (year, month, day).
func (*Calendar) A11y ¶ added in v0.40.0
A11y reports the Calendar as a grid carrying its selected date.
func (*Calendar) Day ¶
func (c *Calendar) Day() *mvvm.Observable[int]
Day is the selected day (in [1, daysInMonth]) as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way). There is no settable Day field; a day-click or a keyboard move Sets it, notifying subscribers.
func (*Calendar) Focused ¶ added in v0.101.0
func (f *Calendar) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*Calendar) Month ¶
func (c *Calendar) Month() *mvvm.Observable[int]
Month is the viewed month (1..12) as a shared mvvm.Observable: a host binds it. There is no settable Month field; prev/next navigation Sets it.
func (*Calendar) NextMonth ¶ added in v0.86.0
func (c *Calendar) NextMonth()
NextMonth advances the view one month, wrapping December to the next January (Setting the Year / Month Observables) and re-clamps the selected day into the new month. Subscribers are notified through the Observables.
func (*Calendar) OnEvent ¶
OnEvent dispatches a header-arrow click to Prev/NextMonth and a day-cell click to the Day Observable (Setting the selected day).
func (*Calendar) PrevMonth ¶ added in v0.86.0
func (c *Calendar) PrevMonth()
PrevMonth steps the view one month back, wrapping January to the previous December (Setting the Year / Month Observables) and re-clamps the selected day into the new month. Subscribers are notified through the Observables.
func (*Calendar) SetDate ¶
SetDate moves the calendar to (year, month, day), Setting the Year / Month / Day Observables (notifying subscribers) and re-clamping into legal ranges.
func (*Calendar) SetFocused ¶ added in v0.101.0
func (f *Calendar) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
func (*Calendar) SetToday ¶
SetToday records the "today" pill the calendar should highlight regardless of which (Y/M) is being viewed.
func (*Calendar) Year ¶
func (c *Calendar) Year() *mvvm.Observable[int]
Year is the viewed year as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way). There is no settable Year field; prev/next navigation and clicks in an adjacent month Set it.
type Card ¶ added in v0.7.0
Card is a bordered container laid out as three optional zones: a header strip at the top (title text on a SurfaceAlt background), a body area with multi-line text (each '\n'-separated line rendered on its own row) and a footer strip at the bottom (SurfaceAlt like the header). The whole card sits on a Theme.Surface fill and is framed by a 1-px Theme.Border stroke — the same visual grammar Button and Menu use so a Card composes cleanly next to them.
Any zone may be empty:
- Title == "" -> the header strip is skipped, the body starts at r.Y.
- Body == "" -> no text lines are drawn (the surface fill still shows).
- Footer== "" -> the footer strip is skipped, the body flows to r.Y+r.H.
Card is a passive display container — it does not intercept input (HitTest / OnEvent stay as Base defaults) so a caller that needs an interactive Card wraps it with an outer container or overlays a Button on top.
func NewCard ¶ added in v0.7.0
NewCard constructs a Card with the given title, body + footer. Any of the three may be "" to skip that zone.
type CardLayout ¶ added in v0.59.0
type CardLayout struct {
Active int
}
CardLayout shows exactly one item — the one at Active — filling the container, and collapses the rest to an empty rectangle so the Container skips them. The card layout (wizards, tab bodies, view switching).
func (*CardLayout) Arrange ¶ added in v0.59.0
func (l *CardLayout) Arrange(r Rect, items []Item)
Arrange fills the active item and empties the others.
type CardMeta ¶ added in v0.155.0
type CardMeta struct {
Base
// Author is the byline (a user / source name); empty hides it.
Author string
// Time is a pre-formatted relative or absolute time ("3h", "2026-08-14");
// empty hides it. CardMeta does not format time — the caller passes a string.
Time string
// Score is an up-vote / points count; negative hides it (use −1).
Score int
// Comments is a reply count; negative hides it (use −1).
Comments int
}
CardMeta is a horizontal strip of small metadata for a content card: an author, a relative time, a score and a comment count, laid out left to right as "author · time · ▲score · 💬comments" and elided to its width. It is the reusable footer/byline the MediaCard, ArticleCard and LinkCard all share, so the byline of a feed reads the same whatever the card type.
A field is shown only when it carries a value: Author / Time when non-empty, Score / Comments when NON-NEGATIVE. Set Score or Comments to a negative value (the sentinel −1 reads well) to hide that count entirely — a story with no score, an item with comments disabled. An all-hidden strip measures and paints as nothing, so a card can carry an empty CardMeta without reserving space for it.
CardMeta is passive content: it never reads input. Colour is the theme's dim-label tone (see dimInk) so the strip reads as subordinate to the title above it.
func NewCardMeta ¶ added in v0.155.0
NewCardMeta builds a meta strip. Pass −1 for score or comments to hide that count; pass "" for author or time to hide those.
func (*CardMeta) A11y ¶ added in v0.155.0
A11y reports the meta strip as static text carrying its joined byline; a hidden strip names nothing.
func (*CardMeta) Draw ¶ added in v0.155.0
Draw paints the strip within Bounds, vertically centred when the bounds are taller than one glyph row, and ellipsised to the bounds width. A hidden (empty) strip paints nothing.
func (*CardMeta) Measure ¶ added in v0.155.0
Measure reports the strip's height at the given width — one glyph row when any field is shown, zero when the strip is entirely hidden. Width does not change the height (the strip is a single elided row); it is accepted for the uniform Measure(width) signature the card family shares.
type Carousel ¶ added in v0.35.0
type Carousel struct {
Base
// Slides + Wrap are config. The reactive slide index is MVVM-only: it lives
// in an unexported Observable exposed via [Carousel.Current].
Slides []Widget
Wrap bool
// contains filtered or unexported fields
}
Carousel shows one child Widget (a "slide") at a time from Slides, picked by Current. A gutter on each side hosts a ◂ / ▸ arrow affordance for stepping to the previous / next slide, and a row of dot indicators below the content marks the total slide count + the active one. Navigation wraps around the ends when Wrap is set; otherwise it clamps.
Suitable for image galleries, onboarding panels, or featured-content rotators — anywhere a single "card" from a set is shown with an obvious way to step through the rest.
func NewCarousel ¶ added in v0.35.0
NewCarousel builds a Carousel over slides, starting at Current = 0 with Wrap = false (clamp at the ends).
func (*Carousel) A11y ¶ added in v0.40.0
A11y reports the Carousel as a group carrying its "current/total" slide position.
func (*Carousel) Children ¶ added in v0.137.0
Children yields every slide, including those currently off-screen: a walker asking for structure wants the whole model, and a reader can say which one is showing from the widget's own state.
func (*Carousel) Current ¶ added in v0.35.0
func (c *Carousel) Current() *mvvm.Observable[int]
Current is the shown slide index as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Current field. A gutter-arrow step (Prev/Next), a dot-indicator click or an arrow key Sets it; subscribers are notified on change (an unchanged value is a no-op, so re-selecting the shown slide is silent).
func (*Carousel) Draw ¶ added in v0.35.0
Draw paints the Current slide clipped to the content rect, the left/right arrow affordances, and the dot indicators. A Carousel with no Slides paints nothing.
func (*Carousel) Focused ¶ added in v0.102.0
func (f *Carousel) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*Carousel) Next ¶ added in v0.35.0
func (c *Carousel) Next()
Next advances Current by one slide. At the last slide it wraps to the first when Wrap is set, otherwise it stays put (clamped). A no-op when Slides is empty.
func (*Carousel) OnEvent ¶ added in v0.35.0
OnEvent: a click in the left/right gutter steps Prev/Next; a click on dot i jumps Current to i; a click inside the content rect forwards to the Current slide, translated into its local frame. Non-click events + a Carousel with no Slides are no-ops.
func (*Carousel) Prev ¶ added in v0.35.0
func (c *Carousel) Prev()
Prev retreats Current by one slide. At the first slide it wraps to the last when Wrap is set, otherwise it stays put (clamped). A no-op when Slides is empty.
func (*Carousel) SetFocused ¶ added in v0.102.0
func (f *Carousel) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
type CellEditor ¶ added in v0.150.0
type CellEditor interface {
Widget
// CellValue returns the editor's current text, read when the edit commits
// and written back into the row.
CellValue() string
// SetCellValue seeds the editor with the cell's current text when the edit
// opens.
SetCellValue(string)
// OnCellSubmit registers the callback the editor fires when the user
// accepts the value (e.g. Enter) so the Table commits the edit.
OnCellSubmit(func())
// Focus gives (true) or removes (false) keyboard focus.
Focus(bool)
}
CellEditor is the editing control a Table overlays on a cell while an inline edit is in progress. The default is a text field (see newTextCellEditor); TableColumn.Editor is the per-column seam that swaps in another control -- a numeric field, a drop-down, a date picker. A CellEditor is a Widget (so the Table sizes and draws it over the cell) plus the four hooks the commit machinery needs.
type ChatBubble ¶ added in v0.8.0
type ChatBubble struct {
Base
Text string
Sender ChatSender
}
ChatBubble is a chat-transcript speech bubble: a small rounded rectangle (borrowed shape only — the toolkit's raster stays sharp- cornered) holding a short message string. Multi-line text is supported by splitting Text on '\n'; each line renders on its own glyph row.
Sizing: the bubble grows to fit the widest text line plus 2*PadX, capped at ChatBubbleMaxW so a runaway paste doesn't spill the widget's Bounds. Height is len(lines) * lineH + 2*PadY where lineH = GlyphHeight() + ChatBubbleLineSpacing.
ChatBubble is a passive display widget — it does not intercept input (HitTest / OnEvent stay as Base defaults). A caller that wants a tap-to-copy or long-press-menu bubble wraps this with an outer container that handles the gesture.
func NewChatBubble ¶ added in v0.8.0
func NewChatBubble(text string, sender ChatSender) *ChatBubble
NewChatBubble constructs a ChatBubble carrying text sent by sender.
func (*ChatBubble) A11y ¶ added in v0.40.0
func (c *ChatBubble) A11y() A11yInfo
A11y reports the ChatBubble as text carrying its message.
func (*ChatBubble) Draw ¶ added in v0.8.0
func (c *ChatBubble) Draw(p painter.Painter, theme *Theme)
Draw paints the bubble: fill in Theme.Accent (user) or SurfaceAlt (other), 1-pixel Theme.Border stroke, and one DrawText per '\n'- separated line in Text. Width is derived from the widest line's TextWidth capped by ChatBubbleMaxW; height is derived from the line count. Position is right-aligned within Bounds() for ChatFromUser and left-aligned for ChatFromOther, mirroring the canonical chat-transcript convention.
type ChatSender ¶ added in v0.8.0
type ChatSender int
ChatSender enumerates which side of a chat transcript a ChatBubble belongs to. The two roles paint differently: user messages align to the right of the widget's Bounds in Theme.Accent, other-party messages align to the left in Theme.SurfaceAlt. The distinction is purely visual — the sender does not affect layout otherwise.
const ( // ChatFromUser marks a message sent by the local user. Bubble // right-aligned in Theme.Accent; ink = accent-inverted colour. ChatFromUser ChatSender = iota // ChatFromOther marks a message from a remote party. Bubble // left-aligned in Theme.SurfaceAlt; ink = Theme.OnSurface. ChatFromOther )
type CheckButton ¶
type CheckButton struct {
Base
// Label + Size are config. The reactive checked state is MVVM-only: it lives
// in an unexported Observable exposed via [CheckButton.Checked].
Label string
Size int // box side length in px; 0 uses the 12px default
// contains filtered or unexported fields
}
CheckButton is a square checkbox + a label. A click toggles the reactive Checked state, notifying its Observable's subscribers. Visual: 12 x 12 px box (left-aligned), Theme.Border outline, Theme.Surface fill, Theme.Accent fill + two diagonal "checkmark" strokes in Theme.Background when Checked. Label rendered in Theme.OnBackground to the right of the box.
func NewCheckButton ¶
func NewCheckButton(label string, checked bool) *CheckButton
NewCheckButton constructs a CheckButton with the given label + initial Checked state.
func (*CheckButton) A11y ¶ added in v0.19.0
func (c *CheckButton) A11y() A11yInfo
A11y reports the CheckButton as a checkbox with its checked state.
func (*CheckButton) Checked ¶
func (c *CheckButton) Checked() *mvvm.Observable[bool]
Checked is the current checked state as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Checked field. A click or a Space/Enter key press Sets it (flipping the value); subscribers are notified on change.
func (*CheckButton) Draw ¶
func (c *CheckButton) Draw(p painter.Painter, theme *Theme)
Draw paints the box + checkmark + label.
func (*CheckButton) Focused ¶ added in v0.101.0
func (f *CheckButton) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*CheckButton) HitRect ¶ added in v0.187.0
func (c *CheckButton) HitRect() Rect
HitRect is the checkbox's interactive rectangle: its drawn Bounds clamped up to the density hit-target and centred over them (see [touchHitRect]). A checkbox row is only a dozen logical pixels tall, so under DensityTouch its hit height grows to the >=44px finger floor while the drawn 12px box is untouched; byte-identical to Bounds under DensityCompact.
func (*CheckButton) HitTest ¶ added in v0.187.0
func (c *CheckButton) HitTest(px, py int) bool
HitTest reports whether a surface point falls on the checkbox's (touch-clamped) hit rect.
func (*CheckButton) OnEvent ¶
func (c *CheckButton) OnEvent(ev Event)
OnEvent flips the Checked Observable on click. A Disabled checkbox ignores every kind.
func (*CheckButton) SetFocused ¶ added in v0.101.0
func (f *CheckButton) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
type Chip ¶ added in v0.9.0
type Chip struct {
Base
Text string
Closable bool
OnClose func()
// Dot is an optional leading swatch colour. When Dot.A != 0 the
// widget draws a small filled circle near the left edge (vertically
// centred) and shifts the Text right so it does not overlap the
// swatch -- handy for prefixing a chip with a category/source colour
// (e.g. a coloured dot before "Reddit · golang"). The zero value
// (A == 0) draws no dot and reproduces the original layout exactly,
// so the field is fully backward-compatible.
Dot RGBA
}
Chip is a small labelled pill with an optional "x" close affordance. Unlike Badge -- which is a passive counter / status indicator with no interaction surface -- Chip is a removable tag: when Closable is true the widget renders a click target at the right edge that fires OnClose when tapped. Two constructors keep the two personalities distinct at the callsite: NewChip for a passive tag, NewClosableChip for the removable variant.
Auto-sizing follows Badge's convention: if Bounds().W is zero the first Draw() sets W to the text width plus ChipPadX on each side (plus the close slot's ChipCloseGap + ChipCloseW when Closable is true), and H to GlyphHeight() + 2*ChipPadY when it is also zero. A pre-sized Bounds is honoured verbatim so a fixed-width layout row does not shift when the chip label changes.
func NewChip ¶ added in v0.9.0
NewChip constructs a passive (non-closable) Chip carrying the given Text. OnClose stays nil; the widget ignores clicks. Bounds default to zero so the first Draw() auto-sizes the pill.
func NewClosableChip ¶ added in v0.9.0
NewClosableChip constructs a Chip whose right edge exposes an "x" close affordance. onClose may be nil (clicks on the affordance become a no-op rather than a panic) so callers can wire the callback after construction without ordering constraints.
func (*Chip) A11y ¶ added in v0.40.0
A11y reports the Chip as a button when it exposes a close affordance (Closable), or as plain text otherwise.
func (*Chip) Draw ¶ added in v0.9.0
Draw paints the pill body + optional leading dot + text + optional close affordance. Auto-sizes Bounds when W is zero (adding ChipDotD + ChipDotGap when a dot is present). The pill body is a filled SurfaceAlt rectangle stroked with a Border outline; when Dot.A != 0 a small filled circle in Dot colour is drawn at the left inset and the Text is shifted right past it; the Text is otherwise drawn left- aligned inside the pad, and (when Closable) an "x" glyph in Border colour marks the close slot at the right edge.
func (*Chip) OnEvent ¶ added in v0.9.0
OnEvent fires OnClose when an EventClick lands in the right-hand close slot and Closable is true. Non-click events, non-closable chips, and clicks outside the slot are ignored. A nil OnClose is treated as a no-op so callers can toggle Closable without wiring a callback in the same statement.
Event coordinates are widget-local (as documented on Event), so the slot's horizontal extent is measured against r.W rather than r.X; no localisation is required at the callsite.
type Chord ¶ added in v0.151.0
type Chord []Accelerator
Chord is an ordered sequence of accelerators pressed in turn — the "Ctrl+K Ctrl+S" or "g d" multi-stroke binding pattern. A single-accelerator chord is the common case; a Keymap resolves longer chords stroke-by-stroke.
func MustParseChord ¶ added in v0.151.0
MustParseChord is ParseChord that panics on error, for package-level chord literals known to be valid at author time.
func ParseChord ¶ added in v0.151.0
ParseChord parses a whitespace-separated sequence of accelerators into a Chord ("Ctrl+K Ctrl+S", "g d"). An empty string, or any segment that is not a valid accelerator, returns an error.
func (Chord) String ¶ added in v0.151.0
String renders the chord as space-joined canonical accelerators, the inverse of ParseChord.
type Clipboard ¶ added in v0.42.0
type Clipboard interface {
// ClipboardText returns the current clipboard contents, or ""
// when the clipboard is empty or unavailable.
ClipboardText() string
// SetClipboardText replaces the clipboard contents.
SetClipboardText(s string)
}
Clipboard is a back-end-neutral text clipboard shared by every text widget in the toolkit (Entry, TextView, ...). Copy/cut write to it, paste reads from it, so text copied in one widget can be pasted into any other -- including across widget types.
The default implementation is an in-process memory buffer, which is adequate for tests and headless rendering but does not reach the real OS clipboard. A host that wants OS integration implements Clipboard itself -- e.g. the WAI/HTML5 Clipboard API on wasm, an OSC-52 escape sequence written to the TTY, NSPasteboard / the win32 clipboard via cgo -- and installs it once at startup with SetClipboard. From then on every widget's copy/cut/paste goes through the host's implementation transparently.
func CurrentClipboard ¶ added in v0.42.0
func CurrentClipboard() Clipboard
CurrentClipboard returns the toolkit-wide active Clipboard.
type CodeEditor ¶ added in v0.180.0
type CodeEditor struct {
*TextView
// Language is the lexer hint handed to Syntax.Highlight (e.g. "go",
// "ruby", "python"). An empty string lets the Highlighter decide
// (guess / leave plain). Changing it re-lexes on the next Draw.
Language string
// Syntax is the pluggable highlighter. When nil (the zero value) the
// buffer is painted in the theme's default ink exactly like a bare
// TextView — the core toolkit ships no lexer, so a CodeEditor is
// uncoloured until a consumer sets this to e.g. rougelex.New().
Syntax Highlighter
// HighlightCurrentLine paints a full-width tint behind the caret's
// line. NewCodeEditor enables it; the zero value (a struct literal
// built without the constructor) leaves it off.
HighlightCurrentLine bool
// CurrentLineColor overrides the current-line band colour. Its zero
// value (A == 0, "unset") derives a subtle, theme-safe tint from the
// theme passed to Draw.
CurrentLineColor RGBA
// CompletionSource, when non-nil, supplies the candidate list for the
// caret's context: the editor hands it the whole buffer + the caret
// (line, col) and it returns LSP-shaped CompletionItems. The WIDGET then
// prefix-filters those against the word before the caret and drives the
// popup — so a host feeds it from a language server (loom) or from a
// static list (the go-tex playground) without re-implementing the UI. A
// host that filters server-side returns already-filtered items (and sets
// FilterText to match). Nil (the zero value) disables completion
// entirely: the editor behaves exactly like a plain highlighted view.
CompletionSource func(doc []string, line, col int) []CompletionItem
// CompletionWordChar decides which runes form the "current word" the
// popup filters against (and which typed rune opens/refreshes it). Nil
// uses defaultWordChar (letters, digits, underscore) — the identifier
// rule for code. A LaTeX host sets it to also admit '\\' so a command
// like "\section" filters as one word.
CompletionWordChar func(r rune) bool
// contains filtered or unexported fields
}
CodeEditor is a multi-language source editor: a TextView (the editing model — lines, cursor, insert / split / backspace, undo/redo, selection, IME, scrolling) enriched with a line-number gutter, pluggable syntax highlighting and a current-line highlight. It is the one shared widget every wasmdesk code surface builds on (the wasmbox "code" client, go-loom, the reader source-preview) so they converge on a single implementation instead of each re-wiring a TextView by hand.
It embeds *TextView, so the whole editing API is available directly on a CodeEditor (Text, SetText, OnEvent, Undo, Lines, CursorLine, …); Draw is overridden to refresh the highlight cache, wire the gutter + current-line band, and paint through the embedded view.
func NewCodeEditor ¶ added in v0.180.0
func NewCodeEditor(initial string) *CodeEditor
NewCodeEditor builds a CodeEditor pre-loaded with initial source (split on "\n", empty yields a single empty line, per NewTextView). The line-number gutter and current-line highlight are on by default; Syntax is nil until a caller plugs a highlighter in.
func (*CodeEditor) A11y ¶ added in v0.180.0
func (c *CodeEditor) A11y() A11yInfo
A11y reports the editor as a textbox whose accessible name is the language (a hint to assistive tech about what is being edited) and whose value is the current buffer text. It shadows the promoted TextView.A11y so a screen reader hears the code editor, not a bare textbox.
func (*CodeEditor) CompletionActive ¶ added in v0.214.0
func (c *CodeEditor) CompletionActive() bool
CompletionActive reports whether the popup is currently shown — the plain convenience read over CompletionOpen for a headless host or a test.
func (*CodeEditor) CompletionBounds ¶ added in v0.214.0
func (c *CodeEditor) CompletionBounds() Rect
CompletionBounds is the popup's rectangle, anchored so its top-left sits just below the start of the word being completed (via CaretPixel, the shared caret geometry) and flipped ABOVE the caret line when a below-placement would overflow the editor's bottom edge. Width fits the widest visible row (kind glyph + label + detail), floored at CompletionMinW and clamped within the editor; height is one row per visible candidate. A closed or empty popup returns the zero Rect.
func (*CodeEditor) CompletionItems ¶ added in v0.214.0
func (c *CodeEditor) CompletionItems() []CompletionItem
CompletionItems returns a copy of the current filtered candidate list — the exact rows the popup renders, in display order. A host reads it to mirror the editor's filtering (a live count, its own overlay) without duplicating the match logic. The copy is the caller's to mutate.
func (*CodeEditor) CompletionOpen ¶ added in v0.214.0
func (c *CodeEditor) CompletionOpen() *mvvm.Observable[bool]
CompletionOpen is the popup-shown flag as a shared mvvm.Observable: a host binds it (Get / Set / Subscribe) to learn when completion opens or closes — there is no settable Open field. Typing, an explicit trigger, accept, Escape and a caret move all Set it; subscribers are notified on change. A bare &CodeEditor{} lazy-inits it to false.
func (*CodeEditor) CompletionSelected ¶ added in v0.214.0
func (c *CodeEditor) CompletionSelected() int
CompletionSelected is the highlighted row index within the filtered list — the plain convenience read over CompletionSelection.
func (*CodeEditor) CompletionSelection ¶ added in v0.214.0
func (c *CodeEditor) CompletionSelection() *mvvm.Observable[int]
CompletionSelection is the highlighted row within the FILTERED list as a shared mvvm.Observable: a host binds it exactly like a ListBox's Selected. Key navigation, a row click and every refresh Set it (always re-clamped into the current list). A bare &CodeEditor{} lazy-inits it to 0.
func (*CodeEditor) Draw ¶ added in v0.180.0
func (c *CodeEditor) Draw(p painter.Painter, theme *Theme)
Draw refreshes the highlight cache and paints the editor through the embedded TextView (which draws the gutter, the current-line band via the wired RowBackground hook, and the coloured text via the wired Highlighter hook).
func (*CodeEditor) OnEvent ¶ added in v0.214.0
func (c *CodeEditor) OnEvent(ev Event)
OnEvent overrides the promoted TextView handler to run the completion popup. While the popup is open it consumes the keys that drive it (Up/Down/PageUp/ PageDown navigate, Enter/Tab accept, Escape closes) so they never reach the buffer, scrolls on the wheel, and accepts a click on a row; a caret-moving key or an off-popup click closes it and falls through. Typing an identifier character opens/refreshes the popup (after the character is inserted); the explicit Ctrl/Cmd+Space chord opens it anywhere. Everything else is delegated unchanged to TextView, so an editor with no CompletionSource behaves exactly as before.
type CodeMinimap ¶ added in v0.204.0
type CodeMinimap struct {
Base
// OnScrollToLine, when set, is invoked with the 0-based buffer line a click
// or drag maps to, so the host scrolls its editor there. A func hook (not
// reactive state), so it stays a plain field under the MVVM gate. Left nil,
// a click/drag is a no-op — a host that reads [CodeMinimap.LineAt] itself
// need not set it.
OnScrollToLine func(line int)
// contains filtered or unexported fields
}
CodeMinimap is a VS Code-style code overview: a scaled-down thumbnail of a source buffer where each line is a row of tiny coloured segments — one short segment per run of non-space characters, coloured by the syntax token under it (reusing the very spans an editor paints), with leading whitespace left as a blank gap so indentation reads. A translucent accent band marks the currently-visible line range. It is a scroll thumbnail: a click or drag maps back to a buffer line and the host scrolls its editor there.
It is draw-only reactive-state-free chrome: the host refreshes lines/spans/top/visible before each paint with CodeMinimap.Update and reads the clicked line back through CodeMinimap.OnScrollToLine (or the CodeMinimap.LineAt accessor), so the widget owns no persistent editor state of its own — the buffer stays the editor's, and the minimap is a passive overview. That is why it carries no MVVM Observable: its inputs are per-paint snapshots, not settable widget state (see the mvvm_gate allowlist entry).
Rows are a FIXED small height laid CONTIGUOUSLY from the top, exactly like the VS Code minimap: a short file sits as a compact block at the top with blank space below (it does NOT stretch to fill the column). Only when the buffer is taller than the widget (n*rowH > height) does it compress — sampling one line per drawn row — so the overview never spills past its bounds.
func NewCodeMinimap ¶ added in v0.204.0
func NewCodeMinimap() *CodeMinimap
NewCodeMinimap constructs an empty minimap. The zero value is also usable (all state is fed per-paint through Update), so this is a convenience for symmetry with the other widget constructors.
func (*CodeMinimap) A11y ¶ added in v0.204.0
func (m *CodeMinimap) A11y() A11yInfo
A11y reports the minimap as an img carrying its buffer line count — a scaled-down picture of the source, like the charts, with nothing a reader can read off it beyond how much code it overviews.
func (*CodeMinimap) Draw ¶ added in v0.204.0
func (m *CodeMinimap) Draw(p painter.Painter, theme *Theme)
Draw paints the SurfaceAlt panel, its left divider, every token segment, and the translucent viewport indicator band. Zero-area bounds and an empty buffer both short-circuit to (at most) the panel, so a minimap with no content never panics or paints a stray band.
func (*CodeMinimap) LineAt ¶ added in v0.204.0
func (m *CodeMinimap) LineAt(y int) int
LineAt maps a SURFACE-space y (device pixels, the same space as Bounds) to a 0-based buffer line, in the fixed-row-height geometry: a y above the drawn block clamps to line 0, a y in or below it clamps to the last drawn row's line. It is the pointer→line mapping a host uses to turn a click on the overview into an editor scroll target; OnEvent calls it for its own click/drag handling. Returns 0 when there is nothing drawn.
func (*CodeMinimap) OnEvent ¶ added in v0.204.0
func (m *CodeMinimap) OnEvent(ev Event)
OnEvent maps a click or a press-drag on the overview to a buffer line and hands it to OnScrollToLine, so dragging up/down the minimap scrolls the editor. Event X/Y are widget-local, so it re-anchors y to surface space (Bounds().Y + ev.Y) for LineAt. Every other event kind is ignored, and a disabled minimap consumes nothing.
func (*CodeMinimap) Update ¶ added in v0.204.0
func (m *CodeMinimap) Update(lines []string, spans [][]TextSpan, top, visible int)
Update refreshes the overview inputs before a paint. lines is the editor buffer; spans is the per-line highlighter output (the editor's own colours, same shape a TextView.Highlighter produces) and may be nil or shorter than lines — uncovered rows fall back to a neutral ink. top is the first visible buffer line and visible the viewport's visible-line count, together placing the accent band. Passing the editor's live slices by reference is intended: the minimap only reads them during Draw.
type CollabSheet ¶ added in v0.203.0
type CollabSheet struct {
// contains filtered or unexported fields
}
CollabSheet is the collaborative backing model for a Spreadsheet: an A1-addressed grid whose rows, columns and cells live in the shared structured.Sheet CRDT core, so any number of replicas may edit it at once — offline, in any delivery order — and every replica converges to the same sheet. It adds NO merge logic of its own; the convergence, commutativity, idempotence and associativity are the structured/crdt package's, inherited whole. This type is only the two things a widget needs on top: an A1 (column, row) façade over the CRDT's stable row and column identities, and an observable revision that ticks on every change so a bound view repaints.
Stable addressing ¶
A cell is stored against the identities of its row and column, not their positions, so a concurrent insertion or deletion of some other row on a peer renumbers positions but leaves every cell where its author put it. The positional methods here (CollabSheet.SetCellText and friends) resolve an index through the current CollabSheet.RowCount/CollabSheet.ColCount order at the moment of the call, which is exactly how a spreadsheet UI addresses a cell.
Transport ¶
Every mutation returns the crdt.PartOps to broadcast to peers, already applied locally; a replica integrates a peer's batches with CollabSheet.Apply. A late joiner loads a CollabSheet.Snapshot with LoadCollabSheet, and a delta sync uses CollabSheet.Version and CollabSheet.OpsSince. All of this is the structured.Sheet's own transport, re-exposed unchanged.
Fan-out through MVVM ¶
State crossing into a view crosses through go-widgets/mvvm: a local edit and an applied remote batch both tick an mvvm.Observable revision, and CollabSheet.Subscribe registers a change observer over it — the seam SyncSpreadsheet uses to mirror a merged remote edit back into a widget.
A CollabSheet is not safe for concurrent use; drive it from one goroutine and exchange operations, not the value, between replicas.
func LoadCollabSheet ¶ added in v0.203.0
func LoadCollabSheet(site crdt.SiteID, snapshot []byte) (*CollabSheet, error)
LoadCollabSheet rebuilds a collaborative sheet from a CollabSheet.Snapshot, to be edited as site. A malformed snapshot is returned as an error, never a panic.
func NewCollabSheet ¶ added in v0.203.0
func NewCollabSheet(site crdt.SiteID) *CollabSheet
NewCollabSheet returns an empty collaborative sheet that issues operations as site. Every replica editing one sheet concurrently must pass a distinct crdt.SiteID.
func (*CollabSheet) AppendCol ¶ added in v0.203.0
func (c *CollabSheet) AppendCol() (crdt.PartOps, error)
AppendCol adds a column after the last and returns the operation to broadcast.
func (*CollabSheet) AppendRow ¶ added in v0.203.0
func (c *CollabSheet) AppendRow() (crdt.PartOps, error)
AppendRow adds a row after the last and returns the operation to broadcast.
func (*CollabSheet) Apply ¶ added in v0.203.0
func (c *CollabSheet) Apply(batches ...crdt.PartOps) error
Apply integrates batches of operations from peers, tolerating duplicates and reordering, and ticks the revision so a bound view repaints. An invalid batch is reported as an error and changes nothing.
func (*CollabSheet) CellText ¶ added in v0.203.0
func (c *CollabSheet) CellText(col, row int) string
CellText returns the raw text stored in the cell at column col, row row — the formula or literal an editor re-opens — or "" for an empty or out-of-range cell.
func (*CollabSheet) ColCount ¶ added in v0.203.0
func (c *CollabSheet) ColCount() int
ColCount returns the number of columns present.
func (*CollabSheet) DeleteCol ¶ added in v0.203.0
func (c *CollabSheet) DeleteCol(pos int) (crdt.PartOps, error)
DeleteCol removes the column at index pos and returns the operation to broadcast, on the same terms as CollabSheet.DeleteRow.
func (*CollabSheet) DeleteRow ¶ added in v0.203.0
func (c *CollabSheet) DeleteRow(pos int) (crdt.PartOps, error)
DeleteRow removes the row at index pos and returns the operation to broadcast. The cells of a removed row are left addressed by their (now unreferenced) row identity, exactly as the CRDT core keeps them. A pos outside [0, RowCount) is an error.
func (*CollabSheet) InsertCol ¶ added in v0.203.0
func (c *CollabSheet) InsertCol(pos int) (crdt.PartOps, error)
InsertCol adds a column at index pos and returns the operation to broadcast.
func (*CollabSheet) InsertRow ¶ added in v0.203.0
func (c *CollabSheet) InsertRow(pos int) (crdt.PartOps, error)
InsertRow adds a row at index pos and returns the operation to broadcast. pos may equal CollabSheet.RowCount, which appends; a pos outside [0, RowCount] is an error.
func (*CollabSheet) MoveCol ¶ added in v0.212.0
func (c *CollabSheet) MoveCol(from, to int) (crdt.PartOps, error)
MoveCol moves the column at index from to index to, on the same terms as CollabSheet.MoveRow.
func (*CollabSheet) MoveRow ¶ added in v0.212.0
func (c *CollabSheet) MoveRow(from, to int) (crdt.PartOps, error)
MoveRow moves the row at index from to index to and returns the operation to broadcast. It is one operation, and the row keeps its identity, so its cells and every formula naming them move with it and nothing else does.
A position outside [0, RowCount) is an error, and so is moving a row to where it already is.
func (*CollabSheet) OpsSince ¶ added in v0.203.0
func (c *CollabSheet) OpsSince(v crdt.CompositeVersion) []crdt.PartOps
OpsSince returns the operations this replica holds that v does not, ready to send to the peer that produced v. Pass a nil version for everything.
func (*CollabSheet) Pending ¶ added in v0.203.0
func (c *CollabSheet) Pending() int
Pending reports how many received operations are still waiting for the operations they depend on. It is zero once a replica has caught up.
func (*CollabSheet) Rev ¶ added in v0.203.0
func (c *CollabSheet) Rev() uint64
Rev returns the current revision counter, which ticks on every change. A view or a test reads it to tell whether the sheet moved.
func (*CollabSheet) RowCount ¶ added in v0.203.0
func (c *CollabSheet) RowCount() int
RowCount returns the number of rows present.
func (*CollabSheet) SetCellText ¶ added in v0.203.0
SetCellText stores raw in the cell at column col, row row and returns the operation to broadcast. An empty raw clears the cell. The cell is addressed by the row's and column's stable identities, so the write lands on the same cell on every replica however the sheet's shape has since changed. An out-of-range address is ErrCellOutOfRange and changes nothing.
func (*CollabSheet) Site ¶ added in v0.203.0
func (c *CollabSheet) Site() crdt.SiteID
Site returns the replica identity this sheet issues operations as.
func (*CollabSheet) Snapshot ¶ added in v0.203.0
func (c *CollabSheet) Snapshot() []byte
Snapshot encodes the whole sheet, for a joining peer or for persistence. Two replicas holding the same operations produce identical bytes, so it doubles as a convergence oracle.
func (*CollabSheet) Subscribe ¶ added in v0.203.0
func (c *CollabSheet) Subscribe(fn func()) (unsubscribe func())
Subscribe registers fn to run after every change — a local edit or an applied remote batch — and returns a function that unsubscribes it. It is the seam a view binds to so it repaints when the sheet, however edited, changes.
func (*CollabSheet) Version ¶ added in v0.203.0
func (c *CollabSheet) Version() crdt.CompositeVersion
Version returns what this replica holds, to hand a peer that will send back what it is missing; see CollabSheet.OpsSince.
type CollabText ¶ added in v0.221.0
type CollabText struct {
// Name is this participant's display name, published with every caret move so
// a peer can tag the remote caret. Set it before the first caret move; an
// empty name publishes no name key.
Name string
// Color is this participant's caret colour, published with every caret move.
// Its zero value (A == 0) publishes no colour key, and a peer paints the
// caret in the theme default.
Color RGBA
// OnRemoteChange, when set, is called at the end of every remote application —
// a repaint seam for the host, the analogue of [CollabSheet.Subscribe]. It
// runs on the UI goroutine, inside the apply closure the host ran.
OnRemoteChange func()
// contains filtered or unexported fields
}
CollabText is the live binding between a CodeEditor (or its embedded TextView) and one text part of a github.com/go-crdt/collab session: it turns the editor into one replica of a shared document, so any number of peers edit the same buffer at once and each sees the others' carets and selections painted in place. It is the text-editor analogue of SyncSpreadsheet over a CollabSheet and of an IsoDiagram over an IsoCRDTDocument — the reusable "CRDT ⇄ editor" glue, with no widget change: the CodeEditor edits exactly as before, its edits now flow into the shared document, and a merged remote edit (text or presence) flows back into it.
It adds NO merge logic of its own. The convergence, commutativity, idempotence and associativity are the collab/crdt packages', inherited whole. This type is only the mapping between the editor's MVVM state and the session:
- Local → shared. A user edit ticks the editor's Text() mvvm.Observable; the binding diffs the previous buffer against the new one (crdt.ChangesFrom) and replays that one contiguous edit into the text part as rune-offset Insert/Delete. A caret or selection move ticks CursorLine()/CursorCol()/ Selection() and is published as an awareness.Cursor (anchor + head rune offsets) carrying this participant's CollabText.Name and CollabText.Color.
- Shared → local. A pump goroutine wakes on the session's coalesced change signal, loads the part's current text into the editor, and rebuilds the editor's TextView.Decorations from the session's peers — one Decoration per other participant, its caret and selection mapped from rune offsets to (line, col) and painted in the peer's own colour and name.
Threading model ¶
The editor's MVVM state is not safe for concurrent use — like the rest of the toolkit it must be driven from one goroutine (call it the UI goroutine). The binding owns a pump goroutine that consumes the session's asynchronous change signal, but it NEVER touches editor state from that goroutine. Instead it hands each remote application to the host over CollabText.Updates as a closure the host runs on its own UI goroutine — the same "the host calls back on its own UI thread" contract Browser uses, marshalled here through a channel so there is a clean happens-before and no field to race on. A host drains it with
for apply := range ct.Updates() {
apply() // on the UI goroutine — applies remote text + rebuilds decorations
}
The channel closes when the pump stops (a CollabText.Close or the session ending), which ends the range. Local edits, that drain loop and CollabText.Close all run on the one UI goroutine.
CollabText.Close stops the pump, waits for it to exit and unsubscribes from the editor; it must be called on the UI goroutine and is idempotent. Because the pump selects on its stop signal even while offering an update, a host that has stopped draining never wedges Close.
func NewCollabText ¶ added in v0.221.0
func NewCollabText(ed *CodeEditor, client *collab.Client, textName string) (*CollabText, error)
NewCollabText binds ed to the text part named textName of client's session and returns the live binding. The part's current text is loaded into the editor at once, and from then on local edits flow into the session and remote edits flow back once the host drains CollabText.Updates. An invalid textName (empty or malformed; see crdt.Part) is returned as an error and nothing is wired.
The returned binding owns a pump goroutine; call CollabText.Close to stop it.
func (*CollabText) Close ¶ added in v0.221.0
func (c *CollabText) Close() error
Close stops the pump goroutine, waits for it to exit, and unsubscribes from the editor. It must be called on the UI goroutine and is idempotent.
func (*CollabText) Updates ¶ added in v0.221.0
func (c *CollabText) Updates() <-chan func()
Updates delivers remote-application closures for the host to run on its UI goroutine; see the threading model on CollabText. The channel closes when the pump stops.
type ColorChooser ¶
type ColorChooser struct {
Base
// contains filtered or unexported fields
}
ColorChooser is a 3-channel R/G/B picker with a live preview. Each channel is rendered as a horizontal track with a 1-pixel knob the user drags to change the value.
The reactive colour is MVVM-only: it lives in an unexported mvvm.Observable exposed via ColorChooser.Color — a host binds it (Set / Subscribe / two-way) rather than reading a plain field or registering a change callback. A swatch scrub or a SetHex edit Sets the Observable, notifying subscribers.
func NewColorChooser ¶
func NewColorChooser(initial RGBA) *ColorChooser
NewColorChooser builds a chooser starting at initial. Alpha is forced to 0xFF so a freshly-constructed chooser always reads as fully-opaque.
func (*ColorChooser) A11y ¶ added in v0.40.0
func (c *ColorChooser) A11y() A11yInfo
A11y reports the ColorChooser as a group carrying its current colour as a "#RRGGBB" hex string.
func (*ColorChooser) Color ¶
func (c *ColorChooser) Color() *mvvm.Observable[RGBA]
Color is the current pick as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Color field. A channel scrub or a SetHex edit Sets it, notifying subscribers. A bare &ColorChooser{} lazily initialises the Observable to the zero RGBA on first access.
func (*ColorChooser) Draw ¶
func (c *ColorChooser) Draw(p painter.Painter, theme *Theme)
Draw paints the 3 sliders + preview swatch + hex label.
func (*ColorChooser) HitRect ¶ added in v0.187.0
func (c *ColorChooser) HitRect() Rect
HitRect is the ColorChooser field's tap target: Bounds clamped up to the touch minimum on each axis and centred. Byte-identical to Bounds at DensityCompact.
func (*ColorChooser) OnEvent ¶
func (c *ColorChooser) OnEvent(ev Event)
OnEvent moves a channel knob by press + drag. An EventClick on a track grabs that channel (remembered in active) and sets it from the pointer X; each following EventMouseDrag re-runs the set for the grabbed channel from the new X -- so a drag scrubs the value continuously, even once the pointer strays out of the row -- and EventMouseUp releases the grab. A click that misses every track (e.g. on the preview/hex area) grabs nothing. Coordinates are widget-local.
func (*ColorChooser) SetHex ¶
func (c *ColorChooser) SetHex(s string)
SetHex parses "#RRGGBB" or "RRGGBB" into the chooser's color. Bad input is silently ignored so a malformed Entry payload can't break the picker state.
type ColorPicker ¶ added in v0.36.0
type ColorPicker struct {
Base
// OnEyedrop fires when the eyedropper affordance is clicked. Actual
// pixel sampling is the host's responsibility -- see the type doc.
OnEyedrop func()
// contains filtered or unexported fields
}
ColorPicker is a rich HSV colour picker: a saturation/value square for the current hue, a vertical hue strip, a horizontal alpha slider (checkerboard under a live transparent-to-opaque gradient of the current colour), a solid preview swatch, and an eyedropper affordance.
Unlike ColorChooser (3 independent R/G/B sliders), ColorPicker keeps its state in HSV -- the natural coordinate system for a 2D saturation/value surface -- and derives the RGBA on demand via Color().
The eyedropper button only *signals intent*: OnEyedrop fires on click, but sampling an actual screen pixel is inherently host-specific (it needs a screenshot/compositor hook the toolkit doesn't have), so that part is the host's job. A typical host response is to enter a "pick" mode, read the pixel under the next click anywhere on screen, and feed it back in via SetColor.
func NewColorPicker ¶ added in v0.36.0
func NewColorPicker(initial RGBA) *ColorPicker
NewColorPicker builds a picker seeded from initial, converting its RGB to HSV and carrying its alpha through unchanged.
func (*ColorPicker) A11y ¶ added in v0.40.0
func (c *ColorPicker) A11y() A11yInfo
A11y reports the ColorPicker as a group carrying its current colour (derived from the HSV+alpha state) as a "#RRGGBB" hex string.
func (*ColorPicker) Color ¶ added in v0.36.0
func (c *ColorPicker) Color() *mvvm.Observable[RGBA]
Color is the picked colour as a shared mvvm.Observable: a host binds it two-way (or subscribes instead of the old OnChange callback) rather than reading a field, and each SV/hue/alpha drag Sets it. A host that Sets it directly reseeds the HSV working state. Lazily created so a bare &ColorPicker{} works.
func (*ColorPicker) Draw ¶ added in v0.36.0
func (c *ColorPicker) Draw(p painter.Painter, theme *Theme)
Draw paints the SV square, hue strip, alpha slider, swatch + eyedropper button onto the widget's Bounds.
func (*ColorPicker) EyedropHitRect ¶ added in v0.187.0
func (c *ColorPicker) EyedropHitRect() Rect
EyedropHitRect is the finger target for the eyedropper button, in the same widget-local frame OnEvent hit-tests: the drawn button clamped up to the touch minimum on each axis and centred over it, so the 20-logical-pixel chip reaches the 44px floor under DensityTouch. At DensityCompact it equals the drawn button byte-for-byte.
func (*ColorPicker) OnEvent ¶ added in v0.36.0
func (c *ColorPicker) OnEvent(ev Event)
OnEvent handles clicks + drags across the SV square, hue strip, and alpha slider (each grabs "active" on EventClick so a subsequent EventMouseDrag keeps moving the same control even after the cursor leaves its rect), and a plain click on the eyedropper button.
func (*ColorPicker) SetColor ¶ added in v0.36.0
func (c *ColorPicker) SetColor(rgba RGBA)
SetColor reseeds the HSV working state from an RGBA -- e.g. the host feeding back an eyedropper sample or a sibling hex Entry's parsed value -- and publishes it through Color().
type ColumnBrowser ¶ added in v0.136.0
type ColumnBrowser struct {
Base
// ColumnWidth is the pixel width of each directory column and the preview
// pane. Set before SetRoot / SetBounds; defaults via NewColumnBrowser.
ColumnWidth int
// OnActivate fires when an already-selected leaf is picked again, with its
// node — the "open this file" gesture. Nil-guarded.
OnActivate func(node ColumnNode)
// contains filtered or unexported fields
}
ColumnBrowser is a Miller-column ("columns") view of a tree: N side-by-side columns, each listing the children of a node, where picking a container opens the next column to its right and picking a leaf opens a compact preview column. The strip scrolls horizontally to keep the deepest columns visible. Each row carries a leading type icon and, for a container, a disclosure chevron.
It is driven entirely by a caller-supplied ColumnProvider, so it navigates any tree (a filesystem, a settings hierarchy, an object graph) without the widget knowing anything about the domain. Internally each column is a toolkit ListBox (composed over its public API — the ColumnBrowser never modifies ListBox), so a column inherits vertical scrolling, keyboard roving and selection for free.
Layout: a body filled with Theme.Surface; columns laid out left to right at ColumnWidth, anchored so the newest column stays in view, with a hairline Theme.Border between them; an optional preview pane (Theme.SurfaceAlt) after the last column showing a leaf's big icon, name and provider-supplied detail lines. Everything is clipped to the widget bounds.
Example ¶
ExampleColumnBrowser navigates a two-level tree and reports the open column count after drilling into a folder.
cv := NewColumnBrowser(sampleTree())
cv.SetBounds(Rect{X: 0, Y: 0, W: 700, H: 300})
cv.SetRoot("root")
cv.Draw(newP(makeSurface(700, 300), 700), DefaultLight())
cv.OnEvent(Event{Kind: EventClick, X: 50, Y: 10}) // open "Docs"
fmt.Printf("open columns: %d\n", cv.ColumnCount())
Output: open columns: 2
func NewColumnBrowser ¶ added in v0.136.0
func NewColumnBrowser(provider ColumnProvider) *ColumnBrowser
NewColumnBrowser builds a ColumnBrowser over provider with a default column width. Call SetRoot to list the first column, then SetBounds to lay it out.
func (*ColumnBrowser) A11y ¶ added in v0.136.0
func (cv *ColumnBrowser) A11y() A11yInfo
A11y reports the ColumnBrowser as a tree. Value names the deepest picked node (the leaf/folder at the end of the open chain), or is empty when nothing has been picked yet.
func (*ColumnBrowser) ColumnCount ¶ added in v0.136.0
func (cv *ColumnBrowser) ColumnCount() int
ColumnCount is the number of open directory columns (excluding the preview).
func (*ColumnBrowser) Draw ¶ added in v0.136.0
func (cv *ColumnBrowser) Draw(p painter.Painter, theme *Theme)
Draw paints the columns, their separators and the preview pane, clipped to the widget bounds so the horizontally-scrolled strip stays within its region.
func (*ColumnBrowser) OnEvent ¶ added in v0.136.0
func (cv *ColumnBrowser) OnEvent(ev Event)
OnEvent routes a click/scroll to the column under the pointer, translating the widget-local pointer X into that column's own local space; inert while Disabled.
func (*ColumnBrowser) SetBounds ¶ added in v0.136.0
func (cv *ColumnBrowser) SetBounds(r Rect)
SetBounds records bounds and lays out the columns.
func (*ColumnBrowser) SetRoot ¶ added in v0.136.0
func (cv *ColumnBrowser) SetRoot(rootKey string)
SetRoot resets the strip to a single column listing rootKey (or to an empty strip when the provider rejects it).
type ColumnInfo ¶ added in v0.180.0
type ColumnInfo struct {
Name string
// Type is the optional SQL data type (e.g. "INTEGER", "TEXT"); "" when the
// adapter does not report one.
Type string
}
ColumnInfo is one column of a table or view.
type ColumnNode ¶ added in v0.136.0
ColumnNode is one entry a ColumnProvider lists for a container. Container marks a node that opens a further column when picked (a folder); a non-container is a leaf that opens the preview pane. Icon is the optional leading type icon, Name the displayed label, and Key the opaque identity the provider uses to list the node's children and describe it.
type ColumnProvider ¶ added in v0.136.0
type ColumnProvider interface {
Children(key string) (nodes []ColumnNode, ok bool)
Preview(node ColumnNode) []string
}
ColumnProvider supplies the tree a ColumnBrowser navigates. Children returns the entries under the container identified by key (SetRoot's key for the first column); ok=false rejects the key — a permission error, a leaf mistaken for a container, an empty listing the caller wants to suppress — and no column opens. Preview returns the detail lines (kind, size, ...) shown under a picked leaf's name in the preview pane, or nil for none.
type ComboBox ¶ added in v0.73.0
type ComboBox struct {
Base
// Options is the set-once list of choices (config); Filtered() narrows it
// against the current field text.
Options []string
// Placeholder is shown in the muted tone when the field text is empty (a
// hint such as "search…" or "pick a colour").
Placeholder string
// contains filtered or unexported fields
}
ComboBox is an editable, type-to-filter dropdown: a single-line text field the user can type into, backed by a popover list of Options filtered to those containing the typed text. It sits between Entry (a free-text field with no list) and DropDown (a closed list with no typing): the field accepts free text AND offers the matching options for one-click / Enter selection.
Like DropDown and DatePicker, the popover appears just below the field. The widget renders that list itself when Open (so it works standalone), while a host that composites overlays on a separate surface can instead read Open + PopoverBounds and draw the list there.
Like DropDown, its reactive state is MVVM-only: the field value and the open/closed flag live in unexported Observables exposed via ComboBox.Text and ComboBox.Open — a host binds/Subscribes to them, there are no settable Text or Open fields and no OnChange/OnSelect callbacks.
func NewComboBox ¶ added in v0.73.0
NewComboBox builds a ComboBox with the given options and an empty field.
func (*ComboBox) A11y ¶ added in v0.105.0
A11y reports the ComboBox as a combobox named by its current field text (either free text typed or a picked option).
func (*ComboBox) Draw ¶ added in v0.73.0
Draw paints the field (rounded border, text or muted Placeholder, an end-of-text caret, and a right-side chevron) and, when Open, the filtered options as a plain list in PopoverBounds.
func (*ComboBox) Filtered ¶ added in v0.73.0
Filtered returns the Options whose lowercased text contains the lowercased field text. When the field is empty every option matches, so the full list is returned.
func (*ComboBox) Focused ¶ added in v0.101.0
func (f *ComboBox) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*ComboBox) HitRect ¶ added in v0.187.0
HitRect is the ComboBox field's tap/toggle target: Bounds clamped up to the touch minimum on each axis and centred, byte-identical to Bounds at DensityCompact.
func (*ComboBox) OnEvent ¶ added in v0.73.0
OnEvent drives the type-to-filter behaviour: printable characters and Backspace edit the text (Setting Text) and open the popover; a click on the field toggles Open; a click on a listed option selects it; Enter selects the first filtered option.
func (*ComboBox) Open ¶ added in v0.73.0
func (c *ComboBox) Open() *mvvm.Observable[bool]
Open is the popover open/closed flag as a shared mvvm.Observable: a host binds it to know whether to render the filtered list — there is no settable Open field. A click toggles it; typing opens it; selecting an option or pressing Escape Sets it false. Subscribers are notified on change.
func (*ComboBox) PopoverBounds ¶ added in v0.73.0
PopoverBounds returns the Rect the filtered list occupies below the field: same X and W as the field, height proportional to the visible option count. Mirrors DropDown.PopoverBounds / DatePicker.PopoverBounds.
func (*ComboBox) SetFocused ¶ added in v0.101.0
func (f *ComboBox) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
func (*ComboBox) Text ¶ added in v0.73.0
func (c *ComboBox) Text() *mvvm.Observable[string]
Text is the current field value — either free text the user typed or an option they selected — as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Text field. A keystroke edit or a selection Sets it; subscribers are notified on change. Filtered() narrows Options against Text().Get().
type CommandPalette ¶ added in v0.35.0
type CommandPalette struct {
Base
Commands []PaletteCommand
OnDismiss func()
// contains filtered or unexported fields
}
CommandPalette is a centered overlay that combines a search-query input row with a filtered, keyboard-navigable list of commands — the "Ctrl+Shift+P" palette pattern. It layers a SearchEntry-style query field over a ListBox- style result list and, like ContextMenu, catches an outside-click anywhere on its surface to dismiss itself.
The palette's own Bounds is the whole surface it may cover (so it can catch an outside-click anywhere); the panel is measured and centered inside that frame, and incoming event coordinates are in that same surface frame.
The selection index always addresses the FILTERED list (the indices returned by filtered()), never Commands directly, and is re-clamped after any mutation to the query so it can never point past the end of a shrinking list.
The query string and selection index are private so every mutation flows through the clamping accessors (SetQuery / SetSelected / MoveSelection) or the key-feed (HandleKey); this lets a host — e.g. the wasmdesk Spotlight — drive and read the palette (Query, Selected, FilteredCommands) instead of re-implementing the filter + navigation itself, while the invariant above always holds.
func NewCommandPalette ¶ added in v0.35.0
func NewCommandPalette(cmds []PaletteCommand) *CommandPalette
NewCommandPalette builds a hidden CommandPalette over the given commands. Query starts empty and Selected at 0; call Open to show it.
func (*CommandPalette) A11y ¶ added in v0.40.0
func (c *CommandPalette) A11y() A11yInfo
A11y reports the CommandPalette as a dialog carrying its typed query.
func (*CommandPalette) Dismiss ¶ added in v0.35.0
func (c *CommandPalette) Dismiss()
Dismiss hides the palette and resets its query + selection. It does NOT call OnDismiss itself: OnDismiss is a cancellation signal invoked only by the event handlers that dismiss on user intent (Escape / outside-click), mirroring how ContextMenu keeps activation and cancellation on separate paths.
func (*CommandPalette) Draw ¶ added in v0.35.0
func (c *CommandPalette) Draw(p painter.Painter, theme *Theme)
Draw paints the centered panel when Visible: a query row (the current Query plus a trailing caret marker) followed by one row per filtered command, with the Selected filtered row highlighted in Theme.Accent. Nothing is drawn when hidden. An empty filtered list still renders the panel with just the query row.
func (*CommandPalette) FilteredCommands ¶ added in v0.86.0
func (c *CommandPalette) FilteredCommands() []PaletteCommand
FilteredCommands returns the commands currently visible under the query, in display order — the exact list the result rows render. A host can read it to mirror the palette's filtering (e.g. to show a live count) without duplicating the match logic.
func (*CommandPalette) HandleKey ¶ added in v0.86.0
func (c *CommandPalette) HandleKey(ev Event)
HandleKey feeds one keyboard event to the palette so a host can drive it directly (the wasmdesk Spotlight forwards its key events here): a printable EventChar extends the query + re-filters, Backspace trims it, ArrowUp/ ArrowDown move the selection, Enter activates the selected command, and Escape dismisses (firing OnDismiss). Non-keyboard events are ignored. Unlike OnEvent it does not gate on Visible, so a host managing its own visibility can still feed keys; it is the exact keyboard path OnEvent routes through.
func (*CommandPalette) MoveSelection ¶ added in v0.86.0
func (c *CommandPalette) MoveSelection(delta int)
MoveSelection shifts the selection by delta (negative = up, positive = down) within the filtered list, clamped at both ends (no wraparound), matching the ArrowUp/ArrowDown behaviour.
func (*CommandPalette) OnEvent ¶ added in v0.35.0
func (c *CommandPalette) OnEvent(ev Event)
OnEvent drives the palette while Visible: EventChar appends to Query, Backspace trims it, ArrowUp/ArrowDown move Selected within the filtered list (clamped, no wraparound — matching ListBox/ContextMenu), Enter/row-click runs the selected command then dismisses, and Escape / outside-click dismisses and fires OnDismiss. Events while hidden are ignored.
func (*CommandPalette) Open ¶ added in v0.35.0
func (c *CommandPalette) Open()
Open shows the palette, clearing any prior query and selection so it always reopens in a fresh state.
func (*CommandPalette) Query ¶ added in v0.35.0
func (c *CommandPalette) Query() string
Query returns the current search text. Host-driver accessor: pair with SetQuery to read/write the query without touching internal state.
func (*CommandPalette) Selected ¶ added in v0.35.0
func (c *CommandPalette) Selected() int
Selected returns the current selection index within the FILTERED list.
func (*CommandPalette) SetActions ¶ added in v0.151.0
func (c *CommandPalette) SetActions(r *ActionRegistry)
SetActions replaces the palette's commands with the registry's current visible actions. Wire it to the registry's OnChange (c.SetActions(r)) so the palette rebuilds whenever an action's visibility flips or the set changes.
func (*CommandPalette) SetQuery ¶ added in v0.86.0
func (c *CommandPalette) SetQuery(q string)
SetQuery replaces the search text and re-clamps the selection into the newly filtered list, exactly as typing would. Use it to seed or override the query from a host.
func (*CommandPalette) SetSelected ¶ added in v0.86.0
func (c *CommandPalette) SetSelected(i int)
SetSelected sets the selection index (clamped into the filtered list).
func (*CommandPalette) Visible ¶ added in v0.35.0
func (c *CommandPalette) Visible() *mvvm.Observable[bool]
Visible is reactive state as a shared mvvm.Observable; edits Set it. Lazily created.
type CompletionItem ¶ added in v0.214.0
type CompletionItem struct {
// Label is the text shown in the list and the fallback insert text (used
// when InsertText is empty).
Label string
// Kind drives the row's kind glyph + colour.
Kind CompletionItemKind
// Detail is a short, dim, right-aligned hint (a signature or type).
Detail string
// Documentation is a longer description shown for the selected item in the
// optional doc panel below the list. Empty hides the panel.
Documentation string
// InsertText is what accepting the item inserts, replacing the current
// word. Empty falls back to Label. It may carry a single final-cursor
// "$0" tab-stop marker (the caret lands there after insertion); "${0:x}"
// is honoured too, and any other "$n"/"${n:default}" marker is stripped
// gracefully, keeping only its default text.
InsertText string
// FilterText is what the prefix filter matches the current word against.
// Empty falls back to Label — so a host that wants the user to type "\se"
// to reach a "\section" whose Label reads differently sets FilterText.
FilterText string
}
CompletionItem is one candidate the popup can offer, shaped after the LSP CompletionItem so a host adapts language-server results field-for-field. Only Label is required; every other field has a sensible empty-value fallback.
type CompletionItemKind ¶ added in v0.214.0
type CompletionItemKind int
CompletionItemKind classifies a completion candidate so the popup can draw a small, colour-coded kind glyph beside it. The set mirrors the subset of the LSP CompletionItemKind enumeration that a code/markup editor actually needs, so a host adapting language-server results maps its lsp.CompletionItemKind onto these 1:1 (loom does exactly that); a static provider — the go-tex playground's LaTeX list — picks the closest kind for each entry.
const ( // CompletionText is a plain textual match with no richer classification — // the fallback kind, and the zero value. CompletionText CompletionItemKind = iota // CompletionMethod / CompletionFunction / CompletionConstructor are // callables. CompletionMethod CompletionFunction CompletionConstructor // CompletionField / CompletionVariable / CompletionProperty are values. CompletionField CompletionVariable CompletionProperty // CompletionClass / CompletionInterface / CompletionModule / CompletionEnum // are types and namespaces. CompletionClass CompletionInterface CompletionModule CompletionEnum // CompletionKeyword / CompletionSnippet / CompletionConstant round out the // language-level kinds (a LaTeX command is a Keyword, an environment // template a Snippet, a math symbol a Constant, say). CompletionKeyword CompletionSnippet CompletionConstant )
type Container ¶ added in v0.59.0
Container holds a list of Items and positions them via its Layout. It is a Widget: Draw paints every item with a non-empty rectangle (so a CardLayout's inactive cards and collapsed box cells are skipped), and OnEvent routes by Bounds into the matched item's local space.
func NewContainer ¶ added in v0.59.0
NewContainer builds a Container with the given layout (nil = items keep the bounds they are given). Add items with Add/AddWidget.
func (*Container) Add ¶ added in v0.59.0
Add appends a configured item and re-arranges. Returns the container for fluent, declarative construction.
func (*Container) AddWidget ¶ added in v0.59.0
AddWidget appends a plain widget with the zero item config (equal flex share in a box, centre in a border, a fit/card cell otherwise).
func (*Container) Children ¶ added in v0.123.0
Children yields the container's child widgets in insertion order. It lets generic tree walkers (e.g. CollectRuns) descend without knowing the concrete container type.
func (*Container) OnEvent ¶ added in v0.59.0
OnEvent forwards to the first non-empty item whose Bounds contains the point, translated into that item's local space. EventMouseMove is the exception: it is forwarded to EVERY non-empty item (translated), so the item under the pointer raises its hover face while the ones the pointer just left clear theirs — hover-enter and hover-leave both propagate without host wiring.
Keyboard events are handled by the focus system first: Tab/Shift+Tab move focus through the focusable descendants, and any other key/char is routed to the currently-focused descendant (routeFocusKey), never positionally. A click additionally moves focus to whichever focusable descendant it lands on.
func (*Container) SetBounds ¶ added in v0.59.0
SetBounds positions the container and re-arranges its items.
type ContextMenu ¶ added in v0.17.0
type ContextMenu struct {
Base
Menu *Menu
AnchorX, AnchorY int
// contains filtered or unexported fields
}
ContextMenu is a right-click popup: a Menu that appears at an arbitrary point (the cursor), auto-sizes to its items, clamps itself inside the surface so it never spills off an edge, and dismisses when the user clicks outside it. It is the overlay wrapper the widget model was missing around the bare Menu, mirroring how DropDown/DatePicker own their pop-ups.
The ContextMenu's own Bounds is the whole surface it may cover (so it can catch an outside-click anywhere); AnchorX/AnchorY and incoming event coordinates are in that same frame. Call Popup(x, y) to show it at a point.
Example ¶
ExampleContextMenu shows a right-click popup that auto-sizes, clamps inside the surface, and dismisses on an outside click.
package main
import (
"github.com/go-widgets/painter"
"github.com/go-widgets/toolkit"
)
// newSurface returns a PixelPainter over a fresh w×h RGBA buffer — the render
// target the examples draw into. A CellPainter would render the same widgets to
// a terminal grid instead.
func newSurface(w, h int) *painter.PixelPainter {
return painter.NewPixelPainter(make([]byte, 4*w*h), w, h)
}
func main() {
menu := toolkit.NewMenu([]toolkit.MenuItem{
{Label: "Cut", Action: func() {}},
{Label: "Copy", Action: func() {}, Shortcut: "Ctrl+C"},
})
cm := toolkit.NewContextMenu(menu)
cm.SetBounds(toolkit.Rect{X: 0, Y: 0, W: 200, H: 160})
cm.Popup(8, 8) // open at the cursor
cm.Draw(newSurface(200, 160), toolkit.DefaultLight())
}
Output:
func NewContextMenu ¶ added in v0.17.0
func NewContextMenu(menu *Menu) *ContextMenu
NewContextMenu wraps the given Menu as a (closed) context menu.
func (*ContextMenu) A11y ¶ added in v0.40.0
func (c *ContextMenu) A11y() A11yInfo
A11y reports the ContextMenu as a menu carrying its open/closed state.
func (*ContextMenu) Draw ¶ added in v0.17.0
func (c *ContextMenu) Draw(p painter.Painter, theme *Theme)
Draw paints the Menu at its clamped bounds when open; nothing when closed.
func (*ContextMenu) MenuBounds ¶ added in v0.17.0
func (c *ContextMenu) MenuBounds() Rect
MenuBounds is the rect the Menu occupies: the measured size placed at the anchor, then shifted so it stays fully inside the surface (c.Bounds()).
func (*ContextMenu) OnEvent ¶ added in v0.17.0
func (c *ContextMenu) OnEvent(ev Event)
OnEvent routes a click inside the menu to the Menu (translated to its local frame, so the hit row's Action fires and closes the overlay via OnClose); a click anywhere outside dismisses the menu.
func (*ContextMenu) Open ¶ added in v0.17.0
func (c *ContextMenu) Open() *mvvm.Observable[bool]
Open is reactive state as a shared mvvm.Observable; edits Set it. Lazily created.
func (*ContextMenu) Popup ¶ added in v0.17.0
func (c *ContextMenu) Popup(x, y int)
Popup opens the menu anchored at (x, y) and wires the Menu's OnClose so that activating an item (or the menu closing itself) also closes the overlay.
type Corner ¶ added in v0.33.0
type Corner int
Corner names one of the six standard docking positions inside a host rectangle, used to anchor transient overlays (Toast, Notification) to a screen edge. TopLeft is the zero value.
The two *Center corners centre the overlay horizontally; the four true corners inset it from the nearer horizontal edge. All six inset from the nearer vertical edge, so top corners stack downward and bottom corners stack upward.
const ( // TopLeft docks against the top + left edges. TopLeft Corner = iota // TopRight docks against the top + right edges. TopRight // BottomLeft docks against the bottom + left edges. BottomLeft // BottomRight docks against the bottom + right edges. BottomRight // TopCenter docks against the top edge, horizontally centred. TopCenter // BottomCenter docks against the bottom edge, horizontally centred. BottomCenter )
type CycleButton ¶ added in v0.76.0
CycleButton is a button that steps through a fixed set of Options, showing the active one and advancing to the next on each click (wrapping past the end). It is the compact alternative to a radio group or dropdown when the choice set is small and cycling is natural (e.g. a view mode: List → Grid → Compact).
Options is set-once config. The shown option is identified by an index whose reactive state is MVVM-only: it lives in an unexported Observable exposed via CycleButton.Index. A host binds that handle (Set / Subscribe / two-way) and reads Options[Index] itself — there is no settable Index field and no change callback.
func NewCycleButton ¶ added in v0.76.0
func NewCycleButton(options ...string) *CycleButton
NewCycleButton builds a CycleButton over options (the first shown).
func (*CycleButton) A11y ¶ added in v0.105.0
func (c *CycleButton) A11y() A11yInfo
A11y reports the CycleButton as a button named by its currently-shown option (the value that advances on each click), or "" when it has no options.
func (*CycleButton) Draw ¶ added in v0.76.0
func (c *CycleButton) Draw(p painter.Painter, theme *Theme)
Draw paints the button body + the active option's label, centred, using the widget's font.
func (*CycleButton) Focused ¶ added in v0.101.0
func (f *CycleButton) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*CycleButton) HitRect ¶ added in v0.187.0
func (c *CycleButton) HitRect() Rect
HitRect is the cycle button's interactive rectangle: its drawn Bounds clamped up to the density hit-target and centred over them (see [touchHitRect]). Byte-identical to Bounds under DensityCompact; a finger-sized target under DensityTouch.
func (*CycleButton) HitTest ¶ added in v0.187.0
func (c *CycleButton) HitTest(px, py int) bool
HitTest reports whether a surface point falls on the cycle button's (touch-clamped) hit rect.
func (*CycleButton) Index ¶ added in v0.76.0
func (c *CycleButton) Index() *mvvm.Observable[int]
Index is the currently-shown option's index as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Index field. A click or a key step Sets it (wrapping within Options); subscribers are notified. It lazy-inits to 0 so a bare &CycleButton{} yields a usable handle.
func (*CycleButton) OnEvent ¶ added in v0.76.0
func (c *CycleButton) OnEvent(ev Event)
OnEvent advances to the next option on a click (wrapping), Setting the Index Observable. A Disabled cycle button ignores every kind.
func (*CycleButton) SetFocused ¶ added in v0.101.0
func (f *CycleButton) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
func (*CycleButton) Value ¶ added in v0.76.0
func (c *CycleButton) Value() string
Value returns the currently shown option, or "" when there are none (or Index is out of range). It is the value-oriented read over the reactive Index handle.
type DataSource ¶ added in v0.180.0
type DataSource interface {
// Schema returns the object tree the left pane renders: databases, each
// with its tables/views, each with its columns.
Schema() (Schema, error)
// Query runs a read statement and returns the result set that fills the
// grid: the column titles and the rows (each already stringified, one cell
// per column).
Query(sql string) (columns []string, rows [][]string, err error)
}
DataSource is the driver-agnostic seam a DatabaseEditor renders over. The toolkit deliberately keeps it tiny so the widget stays a lean pure-UI layer: a host injects an adapter that maps these methods onto a real engine, and a test injects an in-memory fake.
type DatabaseEditor ¶ added in v0.180.0
type DatabaseEditor struct {
Base
// OnCellEdit forwards the results grid's committed inline edits: row and col
// index into the last result set plus the new value. Nil is safe.
OnCellEdit func(row, col int, value string)
// OnError fires when an operation fails, with the surfaced error. Nil is
// safe; the error is painted in the error strip regardless of this hook.
OnError func(err error)
// OnQuery fires after a successful Run with the fetched result set, so a host
// can update a status bar ("42 rows"). Nil is safe.
OnQuery func(columns []string, rows [][]string)
// Layout metrics. A non-positive value selects the constant default, so a
// zero-valued field is the stock layout (see the dbEditorDefault* consts).
TreeWidth int
BarHeight int
EditorHeight int
ErrorHeight int
// contains filtered or unexported fields
}
DatabaseEditor assembles the toolkit's leaf widgets into a database workbench — the missing piece vs a standalone TreeView / TextView / Table: a schema/object tree on the left, a SQL editor at the top-right and an editable results grid at the bottom-right, wired to a run/execute toolbar.
Layout (absolute pixel regions, computed in SetBounds):
+---------------------------------------------------+ | toolbar (Run · Refresh) full width | BarHeight +----------------+----------------------------------+ | | SQL editor (TextView) | EditorHeight | schema tree +----------------------------------+ | (TreeView) | error strip | ErrorHeight | +----------------------------------+ | | results grid (Table, editable) | remainder +----------------+----------------------------------+ TreeWidth
CRITICAL — driver-agnostic: the toolkit bundles NO real database drivers. DatabaseEditor is pure UI over an injected DataSource; a host wires in a go-ruby-{pg,mysql,sqlite3,mongodb,redis} adapter (or, in tests, an in-memory fake) that speaks the three-method seam below.
func NewDatabaseEditor ¶ added in v0.180.0
func NewDatabaseEditor(source DataSource) *DatabaseEditor
NewDatabaseEditor builds a DatabaseEditor over source and eagerly loads its schema into the tree. A schema-load error is surfaced (lastErr / OnError) but does not stop construction, so a caller always gets a usable widget it can Refresh later. source must be non-nil.
func (*DatabaseEditor) A11y ¶ added in v0.180.0
func (d *DatabaseEditor) A11y() A11yInfo
A11y reports the DatabaseEditor as a labelled group; WalkA11y then descends through Children to announce the toolbar, tree, editor and grid.
func (*DatabaseEditor) Children ¶ added in v0.180.0
func (d *DatabaseEditor) Children() []Widget
Children yields the interactive child widgets in visual order so the a11y walker (WalkA11y) descends into them. The error strip is self-painted, not a child, so it is not listed.
func (*DatabaseEditor) Draw ¶ added in v0.180.0
func (d *DatabaseEditor) Draw(p painter.Painter, theme *Theme)
Draw paints the toolbar, tree, SQL editor, error strip and results grid.
func (*DatabaseEditor) Editor ¶ added in v0.180.0
func (d *DatabaseEditor) Editor() *TextView
Editor is the SQL editor pane (top-right). A host reads Editor().Text().Get() or seeds it with SetText. Swapping this TextView for a future CodeEditor widget is a follow-up; the highlighter seam (SQLHighlight) already lives here.
func (*DatabaseEditor) Err ¶ added in v0.180.0
func (d *DatabaseEditor) Err() error
Err reports the most recent operation error, or nil once an operation succeeded. It is the programmatic counterpart of the painted error strip.
func (*DatabaseEditor) Exec ¶ added in v0.180.0
func (d *DatabaseEditor) Exec() (affected int64, ok bool)
Exec runs the SQL editor's text as a non-result statement through the source's optional Execer half. It reports the affected-row count and true on success; on a missing Execer or an execution error it surfaces the error and returns false.
func (*DatabaseEditor) Grid ¶ added in v0.180.0
func (d *DatabaseEditor) Grid() *Table
Grid is the results Table (bottom-right). Its columns are made Editable by Run so a cell edit fires OnCellEdit.
func (*DatabaseEditor) OnEvent ¶ added in v0.180.0
func (d *DatabaseEditor) OnEvent(ev Event)
OnEvent routes a widget-local event to the child whose absolute bounds contain it, translated into that child's local space. A click that lands on the self-painted error strip (no child) is a no-op.
func (*DatabaseEditor) Refresh ¶ added in v0.180.0
func (d *DatabaseEditor) Refresh() error
Refresh reloads the schema from the source and rebuilds the tree. On error it surfaces the error (setError) and leaves the previous tree in place, returning the error so a caller can react.
func (*DatabaseEditor) Run ¶ added in v0.180.0
func (d *DatabaseEditor) Run()
Run executes the SQL editor's current text as a query and fills the results grid. A query error is surfaced and leaves the previous grid contents intact; a success clears the error, replaces the grid's columns + rows and fires OnQuery.
func (*DatabaseEditor) SQL ¶ added in v0.180.0
func (d *DatabaseEditor) SQL() string
SQL returns the current text of the SQL editor.
func (*DatabaseEditor) SetBounds ¶ added in v0.180.0
func (d *DatabaseEditor) SetBounds(r Rect)
SetBounds positions every child region. Bounds in this toolkit are absolute (surface) coordinates, so children receive absolute rects (see WalkA11y).
func (*DatabaseEditor) SetSQL ¶ added in v0.180.0
func (d *DatabaseEditor) SetSQL(sql string)
SetSQL replaces the SQL editor's text.
func (*DatabaseEditor) Toolbar ¶ added in v0.180.0
func (d *DatabaseEditor) Toolbar() *Toolbar
Toolbar is the run/execute action strip (top). Item 0 is Run, item 2 is Refresh (item 1 is a separator).
func (*DatabaseEditor) Tree ¶ added in v0.180.0
func (d *DatabaseEditor) Tree() *TreeView
Tree is the schema/object TreeView (left pane). Exposed so a host wires selection (Tree().OnActivate) — e.g. to seed a "SELECT * FROM <table>" query.
type DatabaseInfo ¶ added in v0.180.0
DatabaseInfo is one database (schema / catalog) and its tables and views.
type Date ¶ added in v0.35.0
type Date struct {
Y, M, D int
}
Date is a plain (Year, Month, Day) triple — the same time-source-free representation Calendar uses (see calendar.go, which takes year/month/day ints and never touches time.Time). The zero Date{} means "unset": a real selection always has Month in 1..12, so a Month of 0 is a reliable sentinel.
type DatePicker ¶ added in v0.11.0
type DatePicker struct {
Base
// Cal is the embedded month grid the picker drives (config); its date is
// read/bound via its own Year() / Month() / Day() accessors.
Cal *Calendar
// contains filtered or unexported fields
}
DatePicker is a form input for a single calendar date: a field showing the selected date as ISO YYYY-MM-DD text with a small grid icon, and a drop-down Calendar that opens beneath it when the field is clicked. Picking a day in the calendar updates the field and closes the popup.
Where the display-only Calendar just renders a month, DatePicker is the composite entry control built around it — the pixel sibling of a native date field. It owns its Calendar (exposed as Cal, config) and renders the popup itself when open, so it works standalone; a host that composites overlays on a separate surface can instead read DatePicker.Open + PopoverBounds and draw Cal there.
The reactive state is MVVM-only: the popover open/closed flag lives in an unexported Observable exposed via DatePicker.Open. A host binds it (Set / Subscribe / two-way) and reads the selected date through the embedded Calendar's own accessors (dp.Cal.Year() / dp.Cal.Month() / dp.Cal.Day()). There is no settable Open field and no OnChange callback.
Example ¶
ExampleDatePicker shows a date field with a drop-down calendar.
package main
import (
"fmt"
"github.com/go-widgets/toolkit"
)
func main() {
dp := toolkit.NewDatePicker(2026, 7, 10)
// A host observes the picked date through the embedded Calendar's accessors.
dp.Cal.Day().Subscribe(func(d int) {
fmt.Printf("picked %04d-%02d-%02d\n", dp.Cal.Year().Get(), dp.Cal.Month().Get(), d)
})
dp.SetBounds(toolkit.Rect{X: 0, Y: 0, W: 170, H: toolkit.DatePickerFieldH()})
fmt.Println(dp.Text())
}
Output: 2026-07-10
func NewDatePicker ¶ added in v0.11.0
func NewDatePicker(year, month, day int) *DatePicker
NewDatePicker builds a DatePicker initialised to (year, month, day).
func (*DatePicker) A11y ¶ added in v0.40.0
func (d *DatePicker) A11y() A11yInfo
A11y reports the DatePicker as a group carrying its selected date.
func (*DatePicker) Date ¶ added in v0.11.0
func (dp *DatePicker) Date() (y, m, d int)
Date returns the currently-selected (year, month, day).
func (*DatePicker) Draw ¶ added in v0.11.0
func (dp *DatePicker) Draw(p painter.Painter, theme *Theme)
Draw paints the field (border + date text + a grid icon) and, when Open, the Calendar popup positioned by PopoverBounds.
func (*DatePicker) HitRect ¶ added in v0.187.0
func (dp *DatePicker) HitRect() Rect
HitRect is the DatePicker field's tap/toggle target: Bounds clamped up to the touch minimum on each axis and centred. Byte-identical to Bounds at DensityCompact.
func (*DatePicker) OnEvent ¶ added in v0.11.0
func (dp *DatePicker) OnEvent(ev Event)
OnEvent: a click on the field toggles the popup; while open, a click inside the popup is forwarded (translated to Calendar-local coordinates) to the Calendar, whose Day Observable change closes the popup (see NewDatePicker).
func (*DatePicker) Open ¶ added in v0.11.0
func (dp *DatePicker) Open() *mvvm.Observable[bool]
Open is the popover open/closed flag as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way). There is no settable Open field; a field click toggles it and picking a day closes it.
func (*DatePicker) PopoverBounds ¶ added in v0.11.0
func (dp *DatePicker) PopoverBounds() Rect
PopoverBounds is the Rect the Calendar occupies when Open: same X and full calendar width below the field. Six week-rows is the worst case.
func (*DatePicker) SetDate ¶ added in v0.11.0
func (dp *DatePicker) SetDate(year, month, day int)
SetDate moves the selection to (year, month, day) without opening the popup.
func (*DatePicker) Text ¶ added in v0.11.0
func (dp *DatePicker) Text() string
Text is the field's displayed value: ISO 8601 YYYY-MM-DD.
type DateRangePicker ¶ added in v0.35.0
DateRangePicker is a month grid on which the user clicks a start day then an end day; the inclusive range between the two is highlighted. Clicking again once a complete range exists begins a fresh selection.
It composes Calendar for all of the month-grid layout + day hit-testing math (via the embedded Cal, whose Day Observable this widget subscribes to for its own selection logic) and adds a header with prev/next-month arrows that Calendar lacks. The range fill uses the theme's SurfaceAlt tone; the two endpoints use Accent.
The two reactive endpoints are MVVM-only: each selected Date lives in an unexported mvvm.Observable exposed via DateRangePicker.Start and DateRangePicker.End. There are no settable Start/End fields and no OnChange callback -- a host binds Start()/End() (Set / Subscribe / two-way) and a day click Sets them, notifying subscribers on change.
func NewDateRangePicker ¶ added in v0.35.0
func NewDateRangePicker(year, month int) *DateRangePicker
NewDateRangePicker builds a picker displaying (year, month) with no initial selection. The caller positions it with SetBounds; the grid is 7 cells wide.
func (*DateRangePicker) A11y ¶ added in v0.40.0
func (d *DateRangePicker) A11y() A11yInfo
A11y reports the DateRangePicker as a group carrying its "start..end" date range.
func (*DateRangePicker) Draw ¶ added in v0.35.0
func (rp *DateRangePicker) Draw(p painter.Painter, theme *Theme)
Draw paints the header (prev arrow, month/year, next arrow), the weekday row, and the day grid with range highlighting.
func (*DateRangePicker) End ¶ added in v0.35.0
func (d *DateRangePicker) End() *mvvm.Observable[Date]
End is the range's upper endpoint as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) -- there is no settable End field. The zero Date{} means "unset". Completing the range Sets it; subscribers are notified on change.
func (*DateRangePicker) NextArrowHitRect ¶ added in v0.187.0
func (rp *DateRangePicker) NextArrowHitRect() Rect
NextArrowHitRect is the finger target for the next-month arrow; see PrevArrowHitRect.
func (*DateRangePicker) OnEvent ¶ added in v0.35.0
func (rp *DateRangePicker) OnEvent(ev Event)
OnEvent handles clicks (widget-local coordinates): the header arrows page the month; a day-cell click is forwarded to the embedded Calendar, whose OnSelect drives selectDay.
func (*DateRangePicker) PrevArrowHitRect ¶ added in v0.187.0
func (rp *DateRangePicker) PrevArrowHitRect() Rect
PrevArrowHitRect and NextArrowHitRect are the finger targets for the two month-paging arrows: each drawn arrow cell clamped up to the touch minimum on both axes and centred over it, so the 24-logical-pixel cells reach the 44px floor under DensityTouch. They sit at opposite header ends, so the enlarged grabs never overlap. At DensityCompact each equals its drawn cell byte-for-byte.
func (*DateRangePicker) Start ¶ added in v0.35.0
func (d *DateRangePicker) Start() *mvvm.Observable[Date]
Start is the range's lower endpoint as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) -- there is no settable Start field. The zero Date{} means "unset". A day click Sets it; subscribers are notified on change.
type DecoButton ¶ added in v0.72.0
type DecoButton struct {
Rect Rect
Shape DecoButtonShape
Face RGBA
Outline RGBA
Glyph DecoGlyph
GlyphInk RGBA
}
DecoButton is one title-bar button: a face (rectangle or circle) filled with Face, an optional Outline (circle only; A=0 = none) and an optional Glyph stroked in GlyphInk. Rect is frame-local.
type DecoButtonShape ¶ added in v0.72.0
type DecoButtonShape int
DecoButtonShape selects how a title-bar button's face is drawn.
const ( // DecoButtonRect draws a filled rectangular face (the Openbox close/minimize // box), with any Glyph stroked on top in GlyphInk. DecoButtonRect DecoButtonShape = iota // DecoButtonCircle draws a filled circle with an optional 1-unit outline (the // macOS traffic-light dot); a circle button usually carries no glyph. DecoButtonCircle )
type DecoGlyph ¶ added in v0.72.0
type DecoGlyph int
DecoGlyph selects the symbol stroked inside a button face.
const ( // DecoGlyphNone draws no symbol (a bare face / traffic-light dot). DecoGlyphNone DecoGlyph = iota // DecoGlyphClose draws an "×" (two diagonals). DecoGlyphClose // DecoGlyphMinimize draws a low horizontal bar. DecoGlyphMinimize // DecoGlyphMaximize draws a square outline. DecoGlyphMaximize )
type Decoration ¶ added in v0.191.2
type Decoration struct {
// Label is the co-editor's name, shown as a tag beside their caret.
Label string
// Color is the co-editor's colour: caret, name tag, and (tinted) the
// selection band. Alpha is applied to the band automatically.
Color RGBA
// CursorLine, CursorCol place the co-editor's caret (rune coordinates).
CursorLine, CursorCol int
// Selection is the co-editor's highlighted range; an empty Selection
// (Start == End) paints just the caret with no band.
Selection Selection
}
Decoration is a remote co-editor's presence in a shared buffer: their caret and (optional) selection, painted in the co-editor's own colour with a small name tag, so a collaborative editorview shows who is editing where. The host (e.g. a CRDT-backed editor like loom) supplies one Decoration per active co-editor via TextView.Decorations; the same list feeds a "co-editors" combo. It is pure data — the TextView only paints it, never mutates it.
type DensityLevel ¶ added in v0.184.0
type DensityLevel int
Density is the toolkit's touch profile — the "visually adapted for touch" axis, DISTINCT from HiDPI (MetricScale) and composed WITH it. HiDPI answers "how many device pixels per logical pixel" so chrome stays crisp; Density answers "how much breathing room does a pointer need" so the same widgets read comfortably under a fingertip on a phone and compactly under a mouse on a desktop. A phone renders at 3x device scale AND a touch density; the two multiply.
It is a package global, mirroring MetricScale: a process draws one surface at a time on the main thread, so a single setting drives every widget without threading it through every call. The default is DensityCompact, whose factor is exactly 1.0, so a host that never calls SetDensity gets metrics byte-for-byte identical to a toolkit that had no density system at all — the same guarantee MetricScale makes at 1.0.
The level type is DensityLevel so the package-global read accessor can keep the name Density, mirroring the MetricScale accessor pair.
const ( // DensityCompact is the desktop default: no extra spacing and no minimum // hit-target. Its factor is exactly 1.0, so every metric is unchanged from a // density-less toolkit. DensityCompact DensityLevel = iota // DensityComfortable widens spacing a little and enforces a modest minimum // hit-target — a middle ground for hybrids (touchscreen laptops, kiosks). DensityComfortable // DensityTouch is the phone/tablet profile: generous spacing and a 44 // logical-pixel minimum hit-target (the Apple HIG / Material touch floor). DensityTouch )
func Density ¶ added in v0.184.0
func Density() DensityLevel
Density returns the current global touch profile (DensityCompact by default).
type Dialog ¶
Dialog is a modal overlay: a centred Surface card with an optional Title bar, a Content widget filling the body, and an action-button strip at the bottom. The compositor draws a semi-darkened backdrop over the rest of the surface so the user's attention focuses on the dialog.
v0.3 ships the structure; the host app is responsible for routing input events only to the dialog while it's open (existing wasmbox modal-grab behaviour).
func NewDialog ¶
NewDialog builds a Dialog with the given title, content + action buttons. Buttons are laid out right-aligned in the bottom strip.
func NewMessageDialog ¶
NewMessageDialog is a convenience constructor for the most common dialog: a title, a Label as content, and an OK button that calls onOK + closes the dialog via the caller's OnClose hook.
func (*Dialog) A11y ¶ added in v0.40.0
A11y reports the Dialog as a dialog named by its title. This also covers NewMessageDialog, which returns a plain *Dialog rather than a distinct type.
type Diff ¶ added in v0.8.0
Diff renders a coloured, line-by-line unified diff view. Each line carries a kind (context / added / removed); Draw fills the row with a light green tint for added lines, a light red tint for removed lines, and the theme's Surface for context lines. A one-character prefix (' ', '+', '-') anchors the row at the left so the widget stays legible even when its background rows are omitted (as when a caller reuses this on top of a striped background).
The widget is intentionally passive: it exposes no scroll, no selection, and no editing. Host apps that need those wrap Diff in a ScrollView + track selection externally.
func NewDiff ¶ added in v0.8.0
NewDiff builds a Diff view over the supplied lines. A nil slice is normalised to a zero-length slice so Draw never has to nil-guard.
type DiffKind ¶ added in v0.8.0
type DiffKind int
DiffKind enumerates the three per-line change categories a unified diff produces.
const ( // DiffContext marks an unchanged, contextual line — rendered on // Theme.Surface with a leading space. DiffContext DiffKind = iota // DiffAdded marks a line inserted by the change — rendered on a // light green tint with a leading '+'. DiffAdded // DiffRemoved marks a line dropped by the change — rendered on a // light red tint with a leading '-'. DiffRemoved )
type DiffLine ¶ added in v0.8.0
DiffLine is one row in a Diff view: the raw text plus the change kind that colours it.
type DocPos ¶ added in v0.218.0
DocPos is a caret position in a richdoc document: the top-level Block index and a rune Off(set) into that block's editable content (its flattened inline text, or a code block's verbatim text). It is a comparable value so it can live directly on an mvvm.Observable.
type DocSelection ¶ added in v0.218.0
type DocSelection struct {
Start, End DocPos
}
DocSelection is a half-open range of caret positions. Start and End are stored as given (an anchor and a cursor); normalizeSel puts them in document order for painting and range edits. An empty selection (Start == End) means "no selection".
func (DocSelection) IsEmpty ¶ added in v0.218.0
func (s DocSelection) IsEmpty() bool
IsEmpty reports whether the selection covers no cells.
type Dock ¶ added in v0.57.0
type Dock struct {
Base
// contains filtered or unexported fields
}
Dock arranges bars against the edges of its bounds and lets a single body widget fill whatever space is left in the centre — a docked-items model. Bars are docked in insertion order, so a top bar added before a left bar spans the full width above the left bar, and the left bar only gets the height that remains. Any edge may hold several bars, which stack inward in order.
Dock is a Widget: Draw paints the body then the bars; OnEvent routes by Bounds, translating into the matched child's local space. The body may be nil (a bars-only frame).
func NewDock ¶ added in v0.57.0
NewDock builds a Dock around body (nil for a bars-only frame). Add bars with Dock().
func (*Dock) A11y ¶ added in v0.130.0
A11y reports the Dock as a toolbar carrying its docked entries.
func (*Dock) Dock ¶ added in v0.57.0
Dock attaches w to the given edge with size pixels along the dock axis (its cross extent fills the space still available). size is clamped to ≥0.
func (*Dock) Draw ¶ added in v0.57.0
Draw paints the body first, then the bars over any shared edge (they never overlap, so order is cosmetic).
type DockItemState ¶ added in v0.179.0
DockItemState is the per-item information a DockStyle needs to paint an item's face and its running/active indicators. GlyphBox is where the widget will draw the icon, so a style can centre an indicator (e.g. a running dot) under it.
type DockSide ¶ added in v0.57.0
type DockSide int
DockSide names the edge a docked bar attaches to.
type DockStyle ¶ added in v0.179.0
type DockStyle interface {
// DrawGround paints the bar background over r.
DrawGround(p painter.Painter, theme *Theme, r Rect)
// DrawFace paints one item's face and its running/active indicators over r,
// returning the ink the widget should use for that item's icon + label.
DrawFace(p painter.Painter, theme *Theme, r Rect, st DockItemState) (ink RGBA)
}
DockStyle paints an AppDock's decorative surfaces — the ground bar and each item's face plus its running/active indicators — so the same dock model, layout, magnification and hit-testing can wear different looks, exactly the way PostCard / GroupCard give one card model different faces. The widget draws the content (icon, label, attention badge); the style owns the chrome and returns the ink the content should use so a dark face can pick a light ink.
Ship-with styles: ModernDockStyle (macOS — flat rounded faces, a dot under a running icon), BevelDockStyle (Fluxbox — raised/sunken 3D bevels) and WindowsDockStyle (taskbar — flat buttons with an accent underline). A host may implement its own.
type DragSource ¶ added in v0.16.0
DragSource is a widget a drag can originate from. DragData returns the payload string the host should carry for the drag (e.g. a file path or a row id).
type DropDown ¶
type DropDown struct {
Base
// Options is the set-once list of choices (config). OpenUp is a layout
// config flag. The reactive state is MVVM-only: the current selection and
// the open/closed flag live in unexported Observables exposed via
// [DropDown.Selected] and [DropDown.Open].
Options []string
// OpenUp makes the popover appear ABOVE the control instead of below it —
// set it when the control sits near the bottom edge so the list has room.
OpenUp bool
// contains filtered or unexported fields
}
DropDown is a one-of-N selector that shows the current choice in a button-like rectangle. Clicking opens a popover ListBox of all Options just below the widget; selecting one closes the popover and Sets the Selected Observable.
Like Dialog, the popover's rendering surface is owned by the host app; the toolkit exposes Open + Selected — both MVVM-only, as shared mvvm.Observable handles behind accessors — so the host knows what to draw and binds/Subscribes to the state. This keeps DropDown independent of how the compositor handles overlay surfaces (some apps use a separate canvas, some draw the popover directly into the same buffer).
func NewDropDown ¶
NewDropDown builds a DropDown with the given options + an initial selection (clamped to a valid index, or 0 when options is empty).
func (*DropDown) A11y ¶ added in v0.40.0
A11y reports the DropDown as a combobox named by its currently-selected option.
func (*DropDown) Current ¶
Current returns the currently-selected option's string, or "" when Options is empty.
func (*DropDown) Draw ¶
Draw paints the closed widget. The popover, when Open, is the host app's responsibility (host can render a ListBox on top using PopoverBounds).
func (*DropDown) DrawPopover ¶ added in v0.94.0
DrawPopover paints the open options list at PopoverBounds, with the current selection highlighted. A no-op when the DropDown is closed. The host calls it in its overlay pass (after the rest of the scene) so the popover — which extends past the control's Bounds — sits on top; that z-ordering is the one thing the widget can't decide for itself.
func (*DropDown) Focused ¶ added in v0.101.0
func (f *DropDown) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*DropDown) HitRect ¶ added in v0.187.0
HitRect is the DropDown's tap/toggle target: Bounds clamped up to the touch minimum on each axis and centred. Byte-identical to Bounds at DensityCompact.
func (*DropDown) OnEvent ¶
OnEvent toggles Open on click. Selection happens via Select() which the host wires to its popover ListBox's OnActivate. A Disabled dropdown ignores every kind (it cannot be opened).
func (*DropDown) Open ¶
func (d *DropDown) Open() *mvvm.Observable[bool]
Open is the popover open/closed flag as a shared mvvm.Observable: a host binds it to know whether to render the popover — there is no settable Open field. A click toggles it; selecting an option or pressing Escape Sets it false. Subscribers are notified on change.
func (*DropDown) PopoverBounds ¶
PopoverBounds returns the Rect the host should give to its popover ListBox: same X+W as the widget, height proportional to the option count (clamped to PopoverMaxRows rows). Positioned just below the widget, or — when OpenUp is set — just above it so a control near the bottom edge still has room for its list.
func (*DropDown) PopoverClick ¶ added in v0.94.0
PopoverClick routes a click at (x, y) — in the DropDown's own coordinate frame, the same one Bounds/PopoverBounds use — while the popover is open: a click inside it selects that option (Setting Selected and closing), a click anywhere else just closes it. Returns true when the open popover consumed the click, false when the DropDown is closed (so the host falls through to its normal hit-testing, where a click on the control reopens it).
func (*DropDown) Selected ¶
func (d *DropDown) Selected() *mvvm.Observable[int]
Selected is the chosen option index as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Selected field. A click, an Enter commit or an arrow-key preview Sets it; subscribers are notified on change.
func (*DropDown) SetFocused ¶ added in v0.101.0
func (f *DropDown) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
type DropTarget ¶ added in v0.16.0
DropTarget is a widget that can receive a drop. AcceptsDrop reports whether the given payload is droppable here — a host consults it on EventDragStart to decide whether to show an "accepted" cursor and whether to deliver the later EventDrop.
type DropZone ¶ added in v0.9.0
type DropZone struct {
Base
Prompt string
OnDrop func(paths []string)
// contains filtered or unexported fields
}
DropZone is an inline "drag files here" target rendered as a bordered rectangle with a centred prompt string. It is the passive counterpart of FileChooser: FileChooser opens a modal directory browser, DropZone waits in place for the host to hand it dropped file paths (typically via a native HTML5 drag+drop listener the wasmbox compositor wires to the widget).
The Hover flag toggles the dashed-border colour + surface fill so the user sees drag-over feedback before releasing the drop. DropZone is a DropTarget: it drives Hover from the formal drag lifecycle — EventDragStart / EventDragMove raise it, EventDragLeave clears it, and EventDrop delivers the payload (multiple paths newline-separated, recovered with SplitDropPayload) to OnDrop and clears Hover. As a convenience for demos and tests, EventClick also flips Hover in place.
func NewDropZone ¶ added in v0.9.0
NewDropZone constructs a DropZone with the given prompt text. An empty prompt is replaced with the default "Drop files here" so a zero-argument caller still renders a legible target. Bounds default to zero; the caller positions the DropZone via SetBounds.
func (*DropZone) A11y ¶ added in v0.40.0
A11y reports the DropZone as a group named by its drop prompt.
func (*DropZone) AcceptsDrop ¶ added in v0.16.0
AcceptsDrop reports whether a payload is droppable here. A DropZone is a generic file target, so it accepts any non-empty payload.
func (*DropZone) Draw ¶ added in v0.9.0
Draw paints the surface fill, the four dashed edges + the centred prompt text. Fill + border colour swap on Hover so the drag-over state is visible without the caller having to swap in a different widget on drag-enter. Dashes are emitted as short filled rects so the toolkit stays on its two existing raster primitives (fillRect / strokeRect) rather than growing a Painter.DashedLine primitive.
func (*DropZone) Hover ¶ added in v0.9.0
func (d *DropZone) Hover() *mvvm.Observable[bool]
Hover is the drag-over highlight state as a shared mvvm.Observable. Lazily created.
func (*DropZone) OnEvent ¶ added in v0.9.0
OnEvent implements the drag lifecycle: EventDragStart / EventDragMove raise Hover, EventDragLeave clears it, and EventDrop fires OnDrop with the payload's items (split from ev.Code) then clears Hover. EventClick flips Hover in place as a demo/test hook. All other event kinds are ignored so a keyboard event bound for a sibling widget does not accidentally trigger a drop.
type Easing ¶ added in v0.35.0
Easing maps a normalized time t in [0, 1] to an eased progress value, typically also in [0, 1] (some easings may overshoot before settling, though none of the named easings below do). Implementations should treat t outside [0, 1] as clamped to the nearest bound.
type Entry ¶
type Entry struct {
Base
OnSubmit func(text string)
// Placeholder is shown in the muted tone when the text is empty and no IME
// composition is in flight (a hint like "search…" or "client id").
Placeholder string
// Mask, when non-zero, is the rune each character is displayed as (e.g. '•')
// instead of the real text — for secrets/passwords. Text/Value keep the real
// contents; only the display is masked.
Mask rune
// contains filtered or unexported fields
}
Entry is a single-line text input. Receives focus on click, edits its text via EventKeyDown (Backspace, ArrowLeft/Right, Home, End, Enter) + EventChar (printable runes). A 1-pixel vertical cursor renders at the cursor offset when Focused.
The reactive contents live on the Entry.Text Observable: a host binds it (or subscribes) instead of reading a plain field, and every edit publishes through it — so there is no OnChange callback, a Set is the only way in and it is MVVM by construction. The cursor rune index and the in-flight IME preview are internal editing state.
The widget treats the text as a rune index space so multi-byte UTF-8 characters move the cursor by one position even when they take several bytes on the wire.
func (*Entry) A11y ¶ added in v0.19.0
A11y reports the Entry as a textbox carrying its current text.
func (*Entry) Draw ¶
Draw paints the border, fill, text + (when Focused) a 1-px cursor stroke at the cursor's pixel position.
func (*Entry) Focused ¶
func (f *Entry) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*Entry) HitRect ¶ added in v0.187.0
HitRect is the Entry's interactive tap target: its drawn Widget.Bounds with each axis clamped up to the touch minimum and centred, so a single-line field only a glyph-row tall still offers a finger the platform's 44-logical-pixel reach under DensityTouch. At DensityCompact it equals Bounds byte-for-byte (the clamp is a pass-through), leaving desktop hit-testing unchanged.
func (*Entry) SetFocused ¶ added in v0.101.0
func (f *Entry) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
func (*Entry) SetText ¶ added in v0.210.0
SetText replaces the whole contents programmatically and parks the caret at the end (the same place NewEntry leaves it), mirroring how a host that drives the value itself expects the field to read next. It goes through the Text() Observable — so subscribers/bindings fire — rather than touching a field, keeping the widget MVVM-only. For interactive editing the host feeds events to OnEvent instead; this is the programmatic-set seam.
func (*Entry) Text ¶
func (e *Entry) Text() *mvvm.Observable[string]
Text is the entry's committed contents as a shared mvvm.Observable: a host binds it two-way (or subscribes) instead of touching a field, and every edit Sets it — so a Set is the only way to change the text and there is no separate change callback. Lazily created so a bare &Entry{} works.
type Event ¶
type Event struct {
Kind EventKind
X, Y int
Code string
Ctrl, Shift bool
// Alt is the Option (⌥) / Alt modifier; Meta is the Command (⌘) / Super
// (Windows/logo) modifier. Both default false. See the type doc above.
Alt, Meta bool
// Delta is the VERTICAL scroll amount, in ROWS, for an EventScroll:
// positive scrolls down / forward (toward the end of the content),
// negative scrolls up / back. It is zero and ignored on every other event
// kind. Scrollable widgets pass it straight to ScrollBy, which clamps at
// both ends, so an over-large Delta simply pins to the last (or first)
// row instead of running off.
Delta int
// DeltaX is the HORIZONTAL scroll amount, in ROWS, for an EventScroll:
// positive scrolls right (toward the end of the content), negative
// scrolls left. It is zero and ignored on every other event kind, and is
// zero for a purely vertical wheel/notch — a two-finger horizontal swipe
// (the browser's wheel deltaX) is what fills it, so a ScrollView moves its
// horizontal scrollbar in response instead of ignoring the gesture.
DeltaX int
}
Event is one input event delivered to a widget. The parent container translated mouse coordinates into widget-local pixels; Code is the key/char text for keyboard events.
Ctrl and Shift report whether those modifier keys were held when the event was produced. They are meaningful on any event kind but most useful on EventClick, where they drive multi-selection (Ctrl-click toggles a row, Shift-click extends a range from the anchor). Hosts that don't track modifiers simply leave them false, which preserves the original single-selection behaviour everywhere.
Alt and Meta report the two remaining desktop modifiers, so a host can deliver a platform-native accelerator a widget could not otherwise tell apart from a plain Ctrl chord. Alt is the ⌥ Option key on macOS and the Alt key on X11/Wayland/Windows; Meta is the ⌘ Command key on macOS and the Super/Windows/logo key elsewhere. They let a file manager distinguish, for example, ⌘V (paste) from ⌘⌥V (paste-as-move) — a distinction Ctrl/Shift alone cannot express. Like Ctrl/Shift they default false, so a host that does not track them (or a widget that ignores them) behaves exactly as before; only code that opts in by reading them sees any change.
type EventKind ¶
type EventKind int
EventKind enumerates the input event types a widget can receive. The wasmbox compositor routes DOM events through this enum so widgets don't depend on the browser's exact event names.
const ( // EventClick fires on a mousedown+mouseup pair inside the widget. // X/Y carry widget-local coordinates. EventClick EventKind = iota // EventKeyDown fires when a key is pressed while the widget has // focus. Code carries the key name (e.g. "Enter", "ArrowLeft"). EventKeyDown // EventKeyUp is the symmetric release event. EventKeyUp // EventChar fires for printable character input (post-IME). // Code carries the character as a one-rune string. EventChar // EventCompositionStart fires when an IME composition begins // (typically a dead-key press or a CJK IME popup opening). Widgets // that echo text (Entry / TextView) should render Code as the // "in-progress" preview string, underlined or ghosted, WITHOUT // committing it to their buffer. The host is responsible for // resolving the composition via EventCompositionUpdate ticks and // finally an EventChar (post-commit). EventCompositionStart // EventCompositionUpdate refreshes the preview string mid-flow. // Code carries the current, un-committed composed text. EventCompositionUpdate // EventCompositionEnd fires when the composition is either // committed (host follows up with EventChar carrying the same // text) or cancelled (host does NOT send an EventChar and the // widget discards the preview). EventCompositionEnd // EventMouseDrag fires when the mouse moves while a button is // still pressed. X/Y carry the current widget-local position. // The initial button press was already dispatched as EventClick, // so a widget that wants drag semantics remembers "am I being // dragged" from the EventClick and consults it on drag ticks. EventMouseDrag // EventMouseUp fires when the button is released. Widgets that // track drag state clear it here. X/Y carry the release // position (widget-local). EventMouseUp // EventDragStart fires on a DropTarget when a drag first enters it // (drag-enter). The target typically raises a hover cue. Code // carries the drag payload the host is offering, so the target can // decide via AcceptsDrop whether to signal acceptance. EventDragStart // EventDragMove fires as the drag pointer moves while still over the // same DropTarget. X/Y carry the widget-local position (for an // insertion indicator); Code still carries the payload. EventDragMove // EventDragLeave fires when the drag pointer exits a DropTarget // without dropping. The target clears its hover cue. It completes // the enter/move/leave/drop lifecycle so a target never stays stuck // in the hover state. EventDragLeave // EventDrop fires when the drag is released over a DropTarget. Code // carries the payload (multiple items newline-separated — see // SplitDropPayload); the target consumes it and clears its hover cue. EventDrop // EventTouchStart fires when a touch point first lands inside the // widget. X/Y carry the widget-local touch position. Code carries // the touch/pointer id (a host-assigned string, stable for the // lifetime of that contact) so a widget — or a GestureRecognizer — // can distinguish concurrent contacts in a multi-touch stream even // though the toolkit's own GestureRecognizer only tracks one active // pointer at a time. EventTouchStart // EventTouchMove fires as an already-started touch point moves. // X/Y carry the current widget-local position; Code carries the // same touch/pointer id as the EventTouchStart that began it. EventTouchMove // EventTouchEnd fires when a touch point is lifted. X/Y carry the // widget-local release position; Code carries the same touch/pointer // id as the EventTouchStart that began it, so a listener can match // the end to its start even if other contacts are interleaved. EventTouchEnd // EventScroll fires when the user scrolls the wheel (or a trackpad // two-finger swipe) over the widget — the toolkit's native scroll // intent. Delta carries the scroll amount in ROWS: positive scrolls // down / forward (toward the end of the content), negative scrolls // up / back. X/Y carry the widget-local pointer position at the time // of the scroll, so a container can hit-test which child the wheel is // over. Scrollable widgets (ListBox, Table, TreeTable, TreeView, // ScrollView) handle it by calling their own ScrollBy(Delta), which // clamps at both ends; every other widget ignores it. Hosts translate // the browser's wheel event (or a native scroll gesture) into this // kind so no app has to hand-roll wheel routing. EventScroll // EventMouseMove fires when the pointer moves over the widget with NO // button pressed — the plain hover-tracking move (its pressed-button // counterpart is EventMouseDrag). X/Y carry the widget-local pointer // position. Containers forward it to their children (translating // coordinates like every other kind) so a leaf sets its hover face when // the pointer is over it and clears it when the pointer moves off; a // widget that draws no hover state simply ignores it. Purely a // visual-feedback signal — it never activates anything. Appended last // (rather than beside EventMouseDrag) so the pre-existing kinds keep // their integer values for any host that persists them. EventMouseMove // EventSecondaryClick fires on a secondary (right / two-finger / long-press) // button press inside the widget — the gesture a desktop user expects to open // a context menu. It carries the same widget-local X/Y and modifier fields as // EventClick; it is a press, with no paired release event, because opening a // menu needs only the down. A host that has no secondary button (or chooses // not to map one) simply never sends it, and a widget that does not handle it // is unaffected. EventSecondaryClick )
type Execer ¶ added in v0.180.0
type Execer interface {
// Exec runs a statement that returns no rows and reports how many were
// affected.
Exec(sql string) (affected int64, err error)
}
Execer is the OPTIONAL write half of a DataSource. A source that also runs non-result statements (INSERT / UPDATE / DELETE / DDL) implements it; DatabaseEditor detects it with a type assertion so the core DataSource interface stays lean and a read-only source need not implement it.
type Expander ¶
type Expander struct {
Base
// Label is the header caption (config); Content is the child widget shown
// below the header when expanded (config, may be nil for header-only).
Label string
Content Widget
// contains filtered or unexported fields
}
Expander is a header row that toggles a content area's visibility. The header is ExpanderHeaderH px tall, shows a chevron + label; clicking the header flips the expanded state.
When expanded, Content occupies the remaining bounds below the header. When collapsed, only the header is drawn. Label and Content are set-once config; the reactive expanded/collapsed state is MVVM-only, exposed via Expander.Expanded.
func NewExpander ¶
NewExpander builds an Expander with a label + initial content widget (may be nil to render header-only). It starts collapsed; the expanded state is an mvvm.Observable a host binds via Expander.Expanded.
func (*Expander) A11y ¶ added in v0.40.0
A11y reports the Expander as a group named by its label, carrying its expanded/collapsed state.
func (*Expander) Children ¶ added in v0.137.0
Children yields the expander's content, whether or not it is expanded — see Carousel for why hidden content still belongs in the structure.
func (*Expander) Draw ¶
Draw paints the header (chevron + label) + the content widget when Expanded.
func (*Expander) Expanded ¶
func (e *Expander) Expanded() *mvvm.Observable[bool]
Expanded is the open/closed state as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Expanded field. A header click or an Enter/Space key press Sets it; subscribers are notified. It starts collapsed (false) and is lazily allocated so a zero-value Expander still runs.
func (*Expander) Focused ¶ added in v0.102.0
func (f *Expander) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*Expander) OnEvent ¶
OnEvent: click on the header toggles the expanded state; clicks below the header forward to Content (when expanded). While focused, Enter/Space toggles the header (same path as a header click).
func (*Expander) SetFocused ¶ added in v0.102.0
func (f *Expander) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
type Fab ¶ added in v0.187.0
type Fab struct {
Base
// Icon is the short glyph string painted on the disc ("+", "✎", ...),
// centred in the toolkit's bitmap font. It doubles as the accessible name
// when Label is empty.
Icon string
// Label is the Fab's accessible name (what a screen reader announces). When
// empty the Icon glyph is used, mirroring how IconButton names itself.
Label string
// OnTap is the primary action, fired by a tap/click/Enter/Space when the Fab
// has no speed-dial Actions. With Actions present a tap toggles the dial
// instead and OnTap is not used. Nil is a safe no-op.
OnTap func()
// Corner is the docking position within the host rect passed to AnchorIn.
// NewFab defaults it to BottomRight (the Material FAB home); the four true
// corners and the two *Center positions are all honoured.
Corner Corner
// Margin is the logical-pixel inset from the docked edges. Zero (or negative)
// means the FabMargin default; any positive value overrides it and is routed
// through scaled so it tracks density + HiDPI.
Margin int
// Diameter is the logical-pixel disc diameter. Zero (or negative) means the
// FabDiameter default; the resolved value is scaled and then clamped up to
// MinHitTarget so a small custom disc still gives a finger a real target.
Diameter int
// Actions is the optional speed-dial: a tap expands them as a stack of mini
// buttons. Empty leaves the Fab a plain single-action button.
Actions []*FabAction
// contains filtered or unexported fields
}
Fab is a floating action button: a circular, raised, Accent-filled action that floats over content at a screen corner. It is the primary/most-common action on a surface — compose (a mail app), add (a list), post (a feed) — rendered as an [Button.ButtonProminent]-coloured disc with an elevation shadow so it reads as lifted off the content below.
Placement is corner + margin, not layout: a Fab does not sit inside an HBox like a Button — it floats, so a host anchors it with Fab.AnchorIn (which resolves Fab.Corner + Fab.Margin through the shared [anchorCorner] primitive that Toast and Notification also use) and paints it last / as the top Overlay layer. Because it floats above everything, Fab.OnEvent and Fab.HitTest work in SURFACE coordinates (like an Overlay layer), not the parent-local coordinates a flow-laid widget receives.
Speed-dial (optional): give the Fab Fab.Actions and a tap (or long-press, or a secondary click) expands a vertical stack of mini action buttons with a staggered slide-out animation; picking one fires its callback and collapses the dial, and a tap on the scrim collapses it without selecting. Each mini button is sized to at least MinHitTarget via TouchTarget so a fingertip always lands one. With no Actions a tap simply fires Fab.OnTap.
Density: the disc diameter, the mini diameter and the elevation shadow all scale through [scaled], and both diameters are clamped up to the density's minimum hit target — so the same Fab is a compact 56-px disc under a mouse and a generous finger-sized one on a phone, with no per-app code.
Animation is manual-clock, like the rest of the toolkit: the Fab owns no goroutine or timer. A host advances it via Fab.Tick once per frame (which also drives the long-press timer) and consults Fab.Animating to stop repainting when the dial has fully settled. The expand/collapse target state is an mvvm.Observable so a view model can bind to it.
func NewFab ¶ added in v0.187.0
NewFab builds a BottomRight-anchored Fab carrying the given glyph + primary tap handler. onTap may be nil. Add speed-dial entries with AddAction.
func (*Fab) A11y ¶ added in v0.187.0
A11y reports the Fab as a button named by its Label (or Icon), whose Value is "expanded" while the speed-dial is open so a reader hears the disc's state.
func (*Fab) AddAction ¶ added in v0.187.0
AddAction appends a speed-dial entry and returns the Fab for chaining. Adding an action invalidates the cached mini widgets so the next layout rebuilds them.
func (*Fab) AnchorIn ¶ added in v0.187.0
AnchorIn records host as the scrim and places the disc at Corner, inset by Margin — the convenience over a host computing SetBounds by hand, mirroring Notification.AnchorIn / Toast.AnchorIn.
func (*Fab) Animating ¶ added in v0.187.0
Animating reports whether the dial is mid-slide and still needs frames.
func (*Fab) Children ¶ added in v0.187.0
Children yields the mini-button widgets while the dial is open (or animating open/closed), and nothing when collapsed — so a generic accessibility walk announces the speed-dial actions exactly when they are on screen. Positions are refreshed first so each child's Bounds is current.
func (*Fab) Collapse ¶ added in v0.187.0
func (f *Fab) Collapse()
Collapse closes the speed-dial (a no-op when already closed / closing), starting the retract from frame zero.
func (*Fab) Draw ¶ added in v0.187.0
Draw paints (in back-to-front order) the dim scrim, the mini stack, and the raised disc with its elevation shadow, icon and focus ring. A Disabled Fab paints only a muted, shadow-less disc.
func (*Fab) Expand ¶ added in v0.187.0
func (f *Fab) Expand()
Expand opens the speed-dial (a no-op with no Actions, or when already open / opening), starting the staggered slide-out from frame zero.
func (*Fab) Expanded ¶ added in v0.187.0
func (f *Fab) Expanded() *mvvm.Observable[bool]
Expanded returns the observable speed-dial state so a view model can bind to (or observe) whether the dial is open. Get() is true from the moment Expand begins until Collapse begins.
func (*Fab) Focused ¶ added in v0.187.0
func (f *Fab) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*Fab) HitTest ¶ added in v0.187.0
HitTest reports whether a SURFACE point is sensitive. Collapsed, only the disc is (taps elsewhere fall through to content). Open, the scrim captures the whole host so an outside tap collapses the dial; without a scrim (a Fab placed by SetBounds rather than AnchorIn) it falls back to the disc plus the mini rectangles.
func (*Fab) IsExpanded ¶ added in v0.187.0
IsExpanded reports whether the dial is open or opening — the state in which a tap selects a mini or collapses rather than re-activating the disc.
func (*Fab) OnEvent ¶ added in v0.187.0
OnEvent drives the Fab from SURFACE-coordinate events (it is a floating / Overlay-layer widget). A click selects a mini or collapses when the dial is open, else activates the disc; a secondary click or a long-press opens the dial; Enter/Space activate, Escape collapses. A Disabled Fab ignores everything. Touch events feed the gesture recognizer so a touch-only host gets tap + long-press without an EventClick.
func (*Fab) SetFocused ¶ added in v0.187.0
func (f *Fab) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
type FabAction ¶ added in v0.187.0
type FabAction struct {
// Icon is the mini button's glyph.
Icon string
// Label is the mini's accessible name; empty falls back to Icon.
Label string
// OnTap fires exactly once when the mini is selected. Nil is a safe no-op
// (the dial still collapses).
OnTap func()
}
FabAction is one speed-dial entry: a glyph, an optional accessible label, and the callback fired when the mini button is chosen (which also collapses the dial).
type FileChooser ¶
type FileChooser struct {
Base
Root *TreeNode
ListFiles func(dir *TreeNode) []string
OnAccept func(path string)
OnCancel func()
// contains filtered or unexported fields
}
FileChooser is a directory-tree + file-list + path-entry composite. It does NO I/O — the host hands it a virtual root (a TreeNode tree representing directories) + a func that lists files in a given directory. Selection is reported via OnAccept.
FileChooser is the canonical use-case for TreeView + ListBox + Entry composed together. It is what an "Open File…" dialog renders inside a wasmbox app that has no JS file picker access.
func NewFileChooser ¶
func NewFileChooser(root *TreeNode, listFiles func(dir *TreeNode) []string) *FileChooser
NewFileChooser builds a FileChooser rooted at root with the given directory-listing func.
func (*FileChooser) A11y ¶ added in v0.40.0
func (f *FileChooser) A11y() A11yInfo
A11y reports the FileChooser as a group named by its root directory, carrying the currently-selected file path as its Value.
func (*FileChooser) Draw ¶
func (f *FileChooser) Draw(p painter.Painter, theme *Theme)
Draw paints the composite.
func (*FileChooser) OnEvent ¶
func (f *FileChooser) OnEvent(ev Event)
OnEvent dispatches to the child widgets based on which one the event falls inside.
func (*FileChooser) Path ¶
func (f *FileChooser) Path() string
Path returns the entry text — the current effective selection.
func (*FileChooser) SetBounds ¶
func (f *FileChooser) SetBounds(r Rect)
SetBounds positions the child widgets at the chosen split.
type FitLayout ¶ added in v0.59.0
type FitLayout struct{}
FitLayout sizes every item to fill the container (typically one item, e.g. a card body). The fit layout.
type FlowLayout ¶ added in v0.65.0
FlowLayout places items left-to-right and wraps to a new row when the next item would overflow the container width — a wrapping row of pills/tags/buttons. Each item's width is its Item.Size (or, when unset, its widget's current Bounds width); every row is RowHeight tall, with HGap between items on a row and VGap between rows.
func (*FlowLayout) Arrange ¶ added in v0.65.0
func (l *FlowLayout) Arrange(r Rect, items []Item)
Arrange flows the items, wrapping on overflow.
type FocusRing ¶ added in v0.35.0
type FocusRing struct {
// contains filtered or unexported fields
}
FocusRing gives a set of Focusables a single, shared keyboard focus: Next and Prev move it forward/backward through the members, wrapping at both ends; Focus jumps straight to a member (e.g. in response to a click hit- test performed by the caller); HandleKey maps the ring's Tab/Shift+Tab key convention onto Next/Prev for callers that dispatch toolkit.Event directly.
A FocusRing does not implement toolkit.Widget: it neither draws nor receives events on its own, it only supervises which member is focused. The zero value is not usable; construct one with NewFocusRing.
func NewFocusRing ¶ added in v0.35.0
NewFocusRing builds a ring over items, focusing the first one (if any).
func (*FocusRing) Add ¶ added in v0.35.0
Add appends f to the ring. If the ring was empty, f is focused immediately (mirroring NewFocusRing's treatment of the first item).
func (*FocusRing) Clear ¶ added in v0.35.0
func (r *FocusRing) Clear()
Clear removes every item from the ring, first defocusing whichever item currently holds focus, and resets the ring to its empty state.
func (*FocusRing) Current ¶ added in v0.35.0
Current returns the index of the focused item, or -1 when the ring is empty.
func (*FocusRing) Focus ¶ added in v0.35.0
Focus moves focus to item i. Out-of-range i (including any index on an empty ring) is ignored. SetFocused(false) is called on the previously focused item and SetFocused(true) on item i, matching tui.FocusRing.Focus.
func (*FocusRing) Focused ¶ added in v0.35.0
Focused returns the focused item, or nil when the ring is empty.
func (*FocusRing) HandleKey ¶ added in v0.35.0
HandleKey maps a toolkit.EventKeyDown Code — "Tab" or "Shift+Tab" — onto Next/Prev, matching the key convention tui.FocusRing.OnEvent dispatches on. It reports whether the key was consumed so a caller forwards everything else (e.g. Enter, arrow keys) to the focused member itself.
type Focusable ¶ added in v0.35.0
type Focusable interface {
// SetFocused is called when focus enters (true) or leaves (false) this
// item.
SetFocused(focused bool)
// Focused reports whether this item currently holds keyboard focus.
Focused() bool
}
Focusable is a widget that can hold keyboard focus. A widget satisfies it by embedding focusState (SetFocused/Focused come for free) — that embedding is what makes a widget focusable: display-only widgets omit it and are therefore never focused, drawn with a focus ring, nor visited by container Tab traversal.
The surface is deliberately minimal — narrower than the terminal sibling's tui.Focusable, which embeds the whole Widget interface because tui's FocusRing also lays out, draws, and routes events to its members. A toolkit widget instead draws its own focus ring (see focusState.drawFocusRing) and a focus-managing Container/HBox/VBox/Grid/Frame routes keys and clicks to it, so Focusable only has to expose and toggle the focused flag.
type FolderTabs ¶ added in v0.211.0
type FolderTabs struct {
Base
// Labels are the tab captions, laid out left→right in slice order. Set-once
// layout config (the reactive part is the selected index, on the Observable),
// so it stays a plain field under the MVVM gate.
Labels []string
// OnSelect fires with the newly-selected index whenever the selection changes
// via a click or an arrow key — nil-safe, and fired only on an actual change.
// It is a convenience seam beside the Selected Observable; a host may use
// either or both.
OnSelect func(index int)
// contains filtered or unexported fields
}
FolderTabs is a compact, desktop folder-tab strip: a row of small tabs — each sized to its own label, NOT stretched to fill the width — with ROUNDED TOP corners sitting on a thin strip. The active tab is filled in Theme.Surface, carries a Theme.Accent bar along its top edge, and is extended down over the strip's bottom border so it reads as CONNECTED to the pane below it (the classic manila-folder look). Inactive tabs are dimmed and sit one pixel above that border so the border shows beneath them.
It is a THIRD tab style, deliberately distinct from the two the toolkit already ships:
- TabBar is a MOBILE bottom-navigation bar — equal-width, full-height finger targets with icon+label+badge items, laid out for a fingertip.
- ViewSwitcher is a GTK/libadwaita full-width SEGMENTED control — the strip divided evenly into same-width segments, the active one a solid accent block.
FolderTabs is neither: its tabs are label-width (compact, left-packed, not full-bleed) and folder-shaped (rounded-top, connected to the pane), the desktop "notebook tab" idiom for switching a single pane's content — e.g. a Rendered│Log split under an editor. It is a separate widget precisely so the mobile bar and the segmented switcher keep their own looks untouched.
Reactive selection lives on an unexported mvvm.Observable exposed through FolderTabs.Selected; a host binds it (Get / Set / Subscribe) exactly as it binds a ViewSwitcher's Current. The optional FolderTabs.OnSelect func hook fires alongside, for a caller that prefers a plain callback (as TabBar does).
A FolderTabs with no Labels paints only the strip + its bottom border; every input is a no-op. This lets a caller assemble the widget before it knows which panes the app will surface.
func NewFolderTabs ¶ added in v0.211.0
func NewFolderTabs(labels []string, selected int) *FolderTabs
NewFolderTabs constructs a FolderTabs over labels with the initial active tab at selected. selected is clamped into [0, len(labels)-1], or forced to 0 when labels is empty, so the widget never holds an out-of-range selection.
func (*FolderTabs) A11y ¶ added in v0.211.0
func (t *FolderTabs) A11y() A11yInfo
A11y reports the strip as a tablist named by its active tab's label. Each tab is published as an individual RoleTab node via FolderTabs.Children, so a screen reader announces a tablist containing N tabs with one selected.
func (*FolderTabs) Children ¶ added in v0.211.0
func (t *FolderTabs) Children() []Widget
Children exposes each label as a synthetic, non-drawing RoleTab node so the generic accessibility walk (CollectA11y / WalkA11y) descends the strip into a tablist → tab structure, each tab carrying its own name, selected state and surface rectangle. The nodes are rebuilt on demand from the current geometry, so a resize or a selection change is reflected without any retained child state. These nodes are for the a11y/geometry walk only — FolderTabs paints every tab itself in Draw.
func (*FolderTabs) Draw ¶ added in v0.211.0
func (t *FolderTabs) Draw(p painter.Painter, theme *Theme)
Draw paints the strip, its bottom border, then each tab (the active one last so it covers the border and sits on top). A zero-area bounds short-circuits.
func (*FolderTabs) Focused ¶ added in v0.211.0
func (f *FolderTabs) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*FolderTabs) OnEvent ¶ added in v0.211.0
func (t *FolderTabs) OnEvent(ev Event)
OnEvent selects a tab on a click and steps the selection on an arrow key. The event's X/Y are widget-local, so a click is re-anchored to surface space before hit-testing. A click that falls on no tab, a click/key on an empty strip, and every other event kind are no-ops; a Disabled strip ignores everything.
func (*FolderTabs) Selected ¶ added in v0.211.0
func (t *FolderTabs) Selected() *mvvm.Observable[int]
Selected is the active tab index as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Selected field. A click or an arrow key Sets it; subscribers are notified. It is lazily created so the zero-value FolderTabs is usable.
func (*FolderTabs) SetFocused ¶ added in v0.211.0
func (f *FolderTabs) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
func (*FolderTabs) TabRect ¶ added in v0.211.0
func (t *FolderTabs) TabRect(i int) Rect
TabRect returns the device rectangle of tab i in SURFACE coordinates: sized to its label plus padding, laid left-to-right from a small left inset. The height runs from the tab top to the strip bottom (the active tab is extended over the border in Draw; an inactive one stops one pixel short of it). An out-of-range i (or an unmeasurable strip) returns the zero Rect, so a caller can range over len(Labels) without a bounds check.
type Font ¶ added in v0.20.0
type Font interface {
Advance() int
Height() int
Measure(text string) int
Draw(p painter.Painter, x, y int, text string, ink RGBA)
}
Font is the toolkit's text metrics + rendering abstraction. Widgets lay themselves out against the ACTIVE font's metrics (via GlyphHeight / GlyphAdvance) and paint text through DrawText, so swapping the active font with SetFont rescales the whole UI's typography without touching any widget.
- Advance is the horizontal step from one glyph origin to the next.
- Height is the glyph box height.
- Measure is the total width text occupies when drawn (proportional fonts sum per-glyph advances; a monospace font returns len*Advance).
- Draw paints text left-to-right at (x, y) in the given ink.
The built-in bitmap font is monospace (Measure == len*Advance), which keeps grid-aligned layout math trivial. A proportional font (see NewTrueTypeFont) still lays out correctly because widgets size text through Measure/TextWidth rather than assuming a fixed advance.
func CurrentFont ¶ added in v0.20.0
func CurrentFont() Font
CurrentFont returns the font widgets lay out and draw with: the one a host set, or the built-in bitmap at the current metric scale.
The built-in scales because a host that turned the one documented HiDPI knob should not get chrome at twice the size around type that stayed put -- which is a worse interface than the one it had before it asked. A host that chose a font chose its size too, so that one is left alone: the same rule Menu and Browser follow for their own Scale fields.
func DefaultOpenTypeFont ¶ added in v0.77.0
DefaultOpenTypeFont returns the toolkit's bundled default face — Atkinson Hyperlegible, designed by the Braille Institute for maximum character distinction — as an anti-aliased, shaped Font at sizePx pixels. A parse failure (which the bundled face never triggers) is returned wrapped.
Use it to install AA text yourself, or to build a multi-script fallback chain before installing:
base, _ := toolkit.DefaultOpenTypeFont(16) cjk, _ := toolkit.NewTrueTypeFont(notosanssc.TTF, 16) f, _ := toolkit.NewFallbackFont(base, cjk) toolkit.SetFont(f)
func NewBitmapFont ¶ added in v0.20.0
NewBitmapFont returns the built-in 5x7 font scaled by the given integer factor (clamped to at least 1). SetFont(NewBitmapFont(2)) doubles all text.
func NewFallbackFont ¶ added in v0.54.0
NewFallbackFont chains fonts so glyphs missing from earlier fonts are rendered by later ones. The first font is the primary (its metrics and baseline drive layout). Every argument must be a font from NewTrueTypeFont; at least one is required. This is how the app renders scripts outside the primary UI face — pass a CJK face after the Latin one to display Chinese/Japanese text.
func NewSyntheticBoldFont ¶ added in v0.128.0
NewSyntheticBoldFont returns a Font that draws f at a faux-bold weight. Use it only when the family has no true bold instance to load; a designed bold is always better. It errors on a nil font.
The wrapper composes over any Font, including a fallback chain, so the whole chain is emboldened rather than only its primary face.
func NewSyntheticItalicFont ¶ added in v0.218.0
NewSyntheticItalicFont returns a Font that draws f at a faux-italic slant. It errors on a nil font. Use it only when the family has no true italic instance to load; a designed italic is always better.
func NewTrueTypeFont ¶ added in v0.31.0
NewTrueTypeFont parses ttf (a TrueType byte blob) and returns a Font that renders it anti-aliased at sizePx pixels. Parse failures are wrapped and returned; on success the face and its metrics are cached for the font's life.
Typical use pairs it with an embedded face, e.g.:
f, err := NewTrueTypeFont(myFontTTF, 16)
if err != nil { /* handle */ }
SetFont(f)
type FontChooser ¶ added in v0.21.0
type FontChooser struct {
Base
Options []FontOption
OnChoose func(idx int, f Font)
// contains filtered or unexported fields
}
FontChooser is a vertical picker of fonts: each option's name is drawn in that very font, so the list doubles as a live size/style preview. Clicking a row selects it, applies it as the active font via SetFont, and fires OnChoose. It is the picker the Font interface (v0.20) unblocked — the long-deferred sibling of ColorChooser / FileChooser.
With no options supplied it defaults to three scales of the built-in bitmap font (Regular / Large / Extra Large), so an app gets a working font size picker for free.
func NewFontChooser ¶ added in v0.21.0
func NewFontChooser(options []FontOption) *FontChooser
NewFontChooser builds a FontChooser over the given options (defaulting to the built-in scale ladder when none are supplied).
func (*FontChooser) A11y ¶ added in v0.40.0
func (f *FontChooser) A11y() A11yInfo
A11y reports the FontChooser as a combobox named by its currently-selected font option.
func (*FontChooser) Draw ¶ added in v0.21.0
func (fc *FontChooser) Draw(p painter.Painter, theme *Theme)
Draw paints the panel and each option's name rendered in its own font, the Selected row on an Accent band.
func (*FontChooser) OnEvent ¶ added in v0.21.0
func (fc *FontChooser) OnEvent(ev Event)
OnEvent: a click on a row selects it, applies it as the active font (SetFont), and fires OnChoose.
func (*FontChooser) Selected ¶ added in v0.21.0
func (fc *FontChooser) Selected() *mvvm.Observable[int]
Selected is reactive state as a shared mvvm.Observable; edits Set it. Lazily created.
type FontOption ¶ added in v0.21.0
FontOption is one named font in a FontChooser.
type FormField ¶ added in v0.9.0
type FormField struct {
Base
Label string
Help string // optional dim caption below the child
Child Widget // the actual input; may be nil
Rules []Rule // optional validation rules run by Validate
// contains filtered or unexported fields
}
FormField is a labelled input row: a Label above (in theme.OnBack- ground), an optional Child input widget below, and an optional caption row underneath the Child that shows either an Error (in fixed red) or Help text (in theme.Border for a muted look). Error takes precedence over Help when both are set.
FormField sits directly on theme.Background (it is a form container, not a card) and does not fill its own body — the label glyphs and the composed Child provide their own inks. Callers wanting a filled body can wrap the FormField in a Card.
Child composition: SetBounds on the Child is called during Draw so callers only have to position the FormField itself. OnEvent forwards clicks (and other event kinds' point events) to the Child when (X, Y) falls inside the Child rect, translating coordinates into Child-local space. Non-point events (keyboard) are forwarded unconditionally so the Child can react to focus-driven input.
func NewFormField ¶ added in v0.9.0
NewFormField constructs a FormField wrapping child with a label above. Help + Error remain empty; the caller assigns them as the field's state changes.
func (*FormField) A11y ¶ added in v0.40.0
A11y reports the FormField as a group named by its label, carrying its error text (if any) as Value.
func (*FormField) Draw ¶ added in v0.9.0
Draw paints the label row, positions + draws the Child (when non- nil), and paints the caption row (Error > Help > nothing).
func (*FormField) Error ¶ added in v0.9.0
func (f *FormField) Error() *mvvm.Observable[string]
Error is reactive state as a shared mvvm.Observable; edits Set it. Lazily created.
func (*FormField) OnEvent ¶ added in v0.9.0
OnEvent forwards the event to Child when Child is non-nil. Point events (EventClick) are gated on the Child rect so a click outside the input body is dropped; non-point events (keyboard/composition) are forwarded unconditionally so a focused Child sees them. Nil Child is a no-op.
func (*FormField) Validate ¶ added in v0.42.0
Validate runs Rules against the field's current Value, in order, stopping at the first failure -- the same short-circuit semantics as the package-level Validate. On failure, Error is set to the failing rule's message and Validate returns false. On success (or when Rules is empty), Error is cleared and Validate returns true.
Validate only ever touches Error; it does not repaint -- callers invoke it (typically from a submit handler or an OnChange callback on Child) and then trigger their own redraw so the caption row picks up the new Error.
type Frame ¶
type Frame struct {
Base
// Padding is the inset (in pixels) between Frame's border + its
// child. Defaults to 4 when left at zero; negative values are
// clamped to zero at layout time.
Padding int
// Title, when non-empty, draws a title bar across the top of the frame
// (inside the border), turning the plain group-box into a titled panel.
// The zero value "" keeps the original border-only box.
Title string
// Collapsible shows a ▼/▶ disclosure chevron in the title bar; a click
// on the bar toggles the collapsed state. It forces a title bar even when
// Title is "".
Collapsible bool
// contains filtered or unexported fields
}
Frame draws a 1-pixel border around a single child widget + inset the child by Padding pixels inside that border. Useful as a group- box / panel separator when an app wants to visually fence off a region of widgets.
Frame is a Widget: Draw paints the border + delegates to the child; OnEvent forwards to the child with translated coordinates.
func NewFrame ¶
NewFrame wraps child in a Frame. child may be nil (the Frame then just draws its border + accepts no events).
func (*Frame) A11y ¶ added in v0.40.0
A11y reports the Frame as a plain grouping container. Frame carries no title text of its own (see the type doc), so Name is always empty -- unlike the other "group" widgets above that surface a label.
func (*Frame) Collapsed ¶ added in v0.78.0
func (f *Frame) Collapsed() *mvvm.Observable[bool]
Collapsed is the frame's collapse state as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Collapsed field. A title-bar click (when Collapsible) flips it, hiding the child and drawing only the title bar; subscribers are notified on change.
func (*Frame) Draw ¶
Draw paints the 1-pixel border, the title bar (if any) then the child. A collapsed frame draws only the title bar + a border around it.
func (*Frame) OnEvent ¶
OnEvent toggles Collapsed on a title-bar click (when Collapsible), else forwards to the child if the event lands inside its Bounds. Keyboard events go through the focus system first (Tab/Shift+Tab traversal + routing to the focused descendant); a click inside the child also moves focus to the focusable it hits.
type GalleryItem ¶ added in v0.142.0
GalleryItem is one gallery entry: a thumbnail/icon and a label. Raster marks Image as a real raster thumbnail (a photo, a rendered preview) so a light chip is painted behind it; leave it false for a flat vector/symbol icon that needs no backing. A nil Image with Raster false draws a document glyph. Key is an opaque caller identity the widget never interprets.
type GalleryView ¶ added in v0.142.0
type GalleryView struct {
Base
// Items is the ordered gallery content. Mutate it directly and call
// SetItems (or SetBounds) to re-normalize the selection and strip scroll.
Items []GalleryItem
// Empty is the message centred when there are no items; a blank Empty falls
// back to a generic default.
Empty string
// OnActivate fires when the already-selected item is clicked again, or
// Enter/Return/Space is pressed, with its index. Nil-guarded.
OnActivate func(index int)
// contains filtered or unexported fields
}
GalleryView is a preview-plus-filmstrip browser: a large preview of the current item filling the top region, and a horizontally-scrolling row of small thumbnails along the bottom. It generalizes a file-manager "gallery view" (macOS Finder's Gallery) — where an icon grid shows every item at the same small size, a GalleryView commits most of its area to ONE big preview and relegates the rest to a filmstrip, so a caller browsing photos or documents reads the current item large while still seeing its neighbours.
Layout: the body is filled with Theme.Surface; the top region (70% of the height) is the preview — the current item's raster thumbnail fit and centred inside a subtle rounded frame (a dark raster sits on a light backing chip so it stays visible on a dark theme), or a document glyph for a non-image item, with the item's label in a caption band beneath it. The bottom region (30%) is the filmstrip: a Theme.SurfaceAlt band under a hairline Theme.Border, laid out left to right at a uniform thumbnail size, scrolled horizontally and clipped to the widget bounds. The selected thumbnail is centred in the band and drawn with a soft accent field and an accent ring.
Selection + navigation: a click selects the thumbnail under the pointer (firing OnSelect); a second click on the already-selected thumbnail activates it (firing OnActivate). Left/Right (or ArrowLeft/ArrowRight) move the selection and auto-scroll the strip to keep it centred; Home/End jump to the ends; Enter/Return/Space activate the current item. Selected / SetSelected read and drive the selection programmatically. Because a gallery always shows a current item, a fresh GalleryView with at least one item selects index 0.
Example ¶
ExampleGalleryView builds a small gallery, moves the selection with a key and reports the current item.
g := NewGalleryView(
GalleryItem{Label: "Sunset.jpg", Key: "sunset", Raster: true},
GalleryItem{Label: "Notes.txt", Key: "notes"},
)
g.SetBounds(Rect{X: 0, Y: 0, W: 320, H: 240})
g.Draw(newP(makeSurface(320, 240), 320), DefaultLight())
g.OnEvent(Event{Kind: EventKeyDown, Code: "ArrowRight"})
fmt.Printf("selected item %d\n", g.Selected().Get())
Output: selected item 1
func NewGalleryView ¶ added in v0.142.0
func NewGalleryView(items ...GalleryItem) *GalleryView
NewGalleryView builds a GalleryView over items. With at least one item the first is selected (a gallery always shows a current item); with none nothing is selected (Selected returns -1). Call SetBounds to lay it out before drawing.
func (*GalleryView) A11y ¶ added in v0.142.0
func (g *GalleryView) A11y() A11yInfo
A11y reports the GalleryView as a grid. Value is the current item's label, or empty when nothing is selected.
func (*GalleryView) Draw ¶ added in v0.142.0
func (g *GalleryView) Draw(p painter.Painter, theme *Theme)
Draw paints the preview and filmstrip (or the empty-state message), clipped to the widget bounds.
func (*GalleryView) OnEvent ¶ added in v0.142.0
func (g *GalleryView) OnEvent(ev Event)
OnEvent moves the selection on Left/Right/Home/End, activates on Enter/Return/Space, selects on a click, activates on a second click of the selected thumbnail, and is inert while Disabled or empty.
func (*GalleryView) PreviewRect ¶ added in v0.142.0
func (g *GalleryView) PreviewRect() Rect
PreviewRect is the top region that shows the large preview (the body minus the filmstrip band).
func (*GalleryView) Selected ¶ added in v0.142.0
func (g *GalleryView) Selected() *mvvm.Observable[int]
Selected is the current item index (-1 = none) as a shared mvvm.Observable: a host binds it (or subscribes for the old OnSelect notification) instead of reading a field, and a click/key move Sets it. Lazily created so a bare &GalleryView{} works (its zero selection is -1, "nothing selected").
func (*GalleryView) SetBounds ¶ added in v0.142.0
func (g *GalleryView) SetBounds(r Rect)
SetBounds records the widget bounds and re-anchors the strip scroll so the current selection stays centred at the new size.
func (*GalleryView) SetItems ¶ added in v0.142.0
func (g *GalleryView) SetItems(items []GalleryItem)
SetItems replaces the gallery content and re-normalizes the selection (a now-out-of-range or unset selection snaps to the first item, or clears when there are no items) and the strip scroll.
func (*GalleryView) SetSelected ¶ added in v0.142.0
func (g *GalleryView) SetSelected(index int)
SetSelected selects item index and auto-scrolls the strip to keep it centred; an out-of-range index clears the selection to -1 (the preview goes blank).
func (*GalleryView) StripRect ¶ added in v0.142.0
func (g *GalleryView) StripRect() Rect
StripRect is the bottom filmstrip band.
func (*GalleryView) ThumbAt ¶ added in v0.142.0
func (g *GalleryView) ThumbAt(x, y int) int
ThumbAt maps a widget-local point to a thumbnail index, or -1 for the gap between thumbnails, past the last thumbnail, or outside the strip band.
type Gantt ¶ added in v0.82.0
type Gantt struct {
Base
Tasks []GanttTask
Units int
// OnTaskChange fires when a drag edits a task's span, with the task index
// and its new [start, end) columns. Nil is safe -- Tasks is still mutated
// in place, so the chart reflects the edit whether or not a host listens.
OnTaskChange func(i, start, end int)
// contains filtered or unexported fields
}
Gantt is a horizontal project-schedule chart: a left gutter of task Labels, a tick header naming the time-unit columns, and one row per task carrying a bar that spans its [Start, End) columns across the shared axis. Units is the total number of columns on that axis; when it is <= 0 it is derived from the largest task End so a caller can leave it unset. Progress paints a darker overlay on each bar, and the selected task (when it indexes a task) tints that row.
Tasks and Units are config. The reactive selection is MVVM-only: the selected task index lives in an unexported Observable exposed via Gantt.Selected — a click Sets it and a host binds it, there is no settable Selected field.
Gantt renders through painter.Painter, so the same schedule draws as pixels (WUI/GUI) or promoted cells (TUI). An empty task slice draws just the gutter separator, header band and axis ticks.
func NewGantt ¶ added in v0.82.0
NewGantt builds a Gantt over the given tasks with no selection (the Selected Observable starts at -1) and an auto-derived axis (Units = 0). A nil slice is normalised to a non-nil empty slice so range loops and len() checks never special-case nil.
func (*Gantt) A11y ¶ added in v0.130.0
A11y reports the Gantt chart as an img carrying its task count, matching the other charts.
func (*Gantt) Draw ¶ added in v0.82.0
Draw paints the surface, the label gutter + its separator, the tick header band with one rule per axis column, and one row per task: a selection tint (Selected only), the task Label in the gutter, and a bar spanning [Start, End) with its Progress overlay. The plotting area (everything right of the gutter) and the gutter itself are clipped so a long label or an over-long bar never bleeds across the boundary.
func (*Gantt) OnEvent ¶ added in v0.82.0
OnEvent drives selection and bar editing. On EventClick it selects the task row (Setting the Selected Observable) and, from where in the bar the press landed, arms a drag: near the left/right edge resizes Start/End, inside the bar moves the whole span, and elsewhere in the row is a plain select. EventMouseDrag applies the edit live; EventMouseUp commits it and fires OnTaskChange (nil-safe).
func (*Gantt) ScrollBy ¶ added in v0.108.0
ScrollBy shifts scroll by delta rows (negative scrolls up), clamped to [0, maxScroll()] and written back immediately.
func (*Gantt) Selected ¶ added in v0.82.0
func (g *Gantt) Selected() *mvvm.Observable[int]
Selected is the selected task index as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Selected field. A bar click Sets it; subscribers are notified. A value that does not index a task (the -1 constructor default, or an out-of-range index) simply tints no row. Accessed on a bare Gantt (no constructor) it lazily initialises to 0, the field's former zero value.
func (*Gantt) TaskAt ¶ added in v0.84.0
TaskAt returns the index of the task row under widget-local (x, y), or -1 for the header band or empty space past the last task. The scroll offset is folded in so a hit-test after scrolling resolves to the task actually shown in that viewport slot. Exposed so a host can hit-test a right-click and build a context menu for that task.
type GanttTask ¶ added in v0.82.0
GanttTask is one horizontal bar in a Gantt chart. Label names the task and is drawn in the left gutter; Start and End are integer time-unit columns on the shared axis (End must be greater than Start) so the bar spans the half-open range [Start, End). Fill is the bar colour — its zero value falls back to the theme's Accent so a task added without an explicit colour still paints in the app's palette. Progress in [0, 1] draws a darker overlay across that leading fraction of the bar, the usual "% complete" cue.
type Gauge ¶ added in v0.75.0
type Gauge struct {
Base
Min, Max float64
Bands []GaugeBand
Caption string
Thickness int
// contains filtered or unexported fields
}
Gauge is a radial arc gauge: a 270° track from the lower-left to the lower-right representing the range Min..Max, filled up to Value, with optional coloured threshold Bands and a centred Caption. Unlike ProgressCircle (a full ring that "fills up") it carries a value scale and colour zones, the display-only counterpart to a dashboard dial.
It rasterises the arc per-pixel over painter.Painter's putPixel (the same approach as PieChart — no arc primitive is added): the background track paints in theme.SurfaceAlt across the whole sweep, and the value arc paints from the start up to frac() in theme.Accent (or, when Bands is set, the colour of the band matching Value). The Caption is drawn centred in Base.Font. Value is clamped to [Min, Max] via frac.
func NewGauge ¶ added in v0.75.0
NewGauge constructs a Gauge over the range min..max at the given value.
func (*Gauge) A11y ¶ added in v0.105.0
A11y reports the Gauge as a meter carrying its current value both as a Value string and as the numeric Min/Max/Now range triple.
func (*Gauge) Draw ¶ added in v0.75.0
Draw paints the background track arc, the value arc up to frac(), and the centred Caption. It is a no-op for an empty or sub-pixel bounds so a hidden or collapsed gauge draws nothing and never panics.
func (*Gauge) Value ¶ added in v0.75.0
func (g *Gauge) Value() *mvvm.Observable[float64]
Value is the gauge's reactive needle position as a shared mvvm.Observable; edits Set it and Draw reads it live, clamped to [Min, Max]. Lazily created, defaulting to 0.
type GaugeBand ¶ added in v0.75.0
GaugeBand is a coloured segment of a Gauge's track up to the value Upto: the band whose Upto first reaches (>=) the current Value gives the value arc its colour. Bands are consulted in slice order, so callers list them by ascending Upto (e.g. a green "ok" band, then a yellow "warn" band, then a red "critical" band).
type GestureRecognizer ¶ added in v0.39.0
type GestureRecognizer struct {
// TapSlop is the largest movement (in pixels, on whichever axis moved
// most) still considered "held still" for tap and long-press purposes.
TapSlop int
// LongPressTicks is the number of Tick() calls a touch must be held
// (without moving past TapSlop) before OnLongPress fires.
LongPressTicks int
// SwipeMinDist is the minimum net displacement (in pixels, on the
// dominant axis) for a release to be recognized as a swipe.
SwipeMinDist int
// OnTap fires when a touch starts and ends within TapSlop pixels and
// wasn't already resolved as a long press. x, y are the release
// position (widget-local).
OnTap func(x, y int)
// OnLongPress fires once a held touch reaches LongPressTicks without
// moving past TapSlop. x, y are the current (held) position.
OnLongPress func(x, y int)
// OnSwipe fires on release when the net displacement reaches
// SwipeMinDist on its dominant axis.
OnSwipe func(dir SwipeDir)
// contains filtered or unexported fields
}
GestureRecognizer turns a stream of EventTouchStart / EventTouchMove / EventTouchEnd events (see widget.go) into higher-level tap, long-press and swipe callbacks. It is pure logic — it does not draw or hold a Widget reference — so any widget (or a host, ahead of dispatch) can embed one.
State machine:
EventTouchStart always (re)arms the recognizer: it becomes "active", remembers the touch/pointer id (Event.Code) and the start position, and resets the hold-tick counter and the long-press-fired flag. Only one touch is tracked at a time — a recognizer is meant to sit behind a single widget's single active contact; multi-touch gestures (pinch, ...) are out of scope and left as future work.
EventTouchMove updates the current position, but only when it carries the id of the active touch; anything else (no active touch, or a different id — e.g. a second finger) is ignored.
Event has no timestamp, so long-press timing is driven by the caller calling Tick() on its own clock (e.g. once per animation frame or per timer tick) instead of wall-clock time. While a touch is active and has not yet moved past TapSlop, each Tick() increments a hold counter; once it reaches LongPressTicks, OnLongPress fires exactly once for that touch and a flag suppresses the Tap that would otherwise fire when the touch is released. Once movement exceeds TapSlop, Tick() stops counting (a long press requires holding still).
EventTouchEnd resolves the gesture from the net displacement between the start and end positions (ignored if the id doesn't match the active touch):
- if the displacement's largest-axis magnitude is >= SwipeMinDist, a swipe fired along whichever axis moved further, in the direction of travel;
- otherwise, if the magnitude is <= TapSlop and no long-press already fired for this touch, OnTap fires;
- anything in between (moved more than a tap, but not far enough for a swipe) resolves to nothing.
func NewGestureRecognizer ¶ added in v0.39.0
func NewGestureRecognizer() *GestureRecognizer
NewGestureRecognizer returns a GestureRecognizer with sensible default thresholds (TapSlop=8px, LongPressTicks=30, SwipeMinDist=24px). Callers wanting different behaviour can override any field, or build a GestureRecognizer{} literal directly with their own thresholds — the callbacks and Feed/Tick logic don't depend on how the struct was built.
func (*GestureRecognizer) Feed ¶ added in v0.39.0
func (g *GestureRecognizer) Feed(ev Event)
Feed consumes one input event. Only EventTouchStart, EventTouchMove and EventTouchEnd are meaningful to a GestureRecognizer; every other kind is ignored so a host can feed it its full event stream unfiltered.
func (*GestureRecognizer) Tick ¶ added in v0.39.0
func (g *GestureRecognizer) Tick()
Tick advances the long-press timer by one caller-driven step. It is a no-op unless a touch is currently active and held within TapSlop of its start position; once the hold reaches LongPressTicks, OnLongPress fires exactly once for that touch (subsequent ticks, and the eventual EventTouchEnd's Tap, are then suppressed for it).
type GradientDir ¶ added in v0.183.0
type GradientDir int
GradientDir is the direction of a Backdrop's linear gradient fill.
const ( // GradientVertical runs the gradient top (Fill) to bottom (GradientTo). GradientVertical GradientDir = iota // GradientHorizontal runs it left (Fill) to right (GradientTo). GradientHorizontal // GradientDiagonal runs it top-left (Fill) to bottom-right (GradientTo). GradientDiagonal // GradientCrossDiagonal runs it top-right (Fill) to bottom-left (GradientTo). GradientCrossDiagonal )
type Grid ¶
type Grid struct {
Base
// Spacing is the inter-cell gutter in pixels applied on both axes (negatives
// clamped to 0 at layout time). Default 0 keeps the historical flush grid.
Spacing int
// ColWidths/RowHeights pin individual tracks to a fixed pixel size; a 0 entry
// (or a missing index) is a flexible track sharing the remaining space equally.
// Absent/empty = all-flexible (the historical equal-cell layout).
ColWidths []int
RowHeights []int
// contains filtered or unexported fields
}
Grid lays children out in a fixed cols x rows table. Children are placed via Attach(child, col, row); a cell with no attached child stays empty.
By default every cell is the same size (container W/cols, H/rows) with no gutter — the historical, zero-config behaviour. Two additive fields refine that:
- Spacing adds an inter-cell gutter (in pixels) on BOTH axes. The gutters are subtracted from the extent before the cells are sized. Default 0 = flush.
- ColWidths/RowHeights pin individual tracks to a fixed pixel size. An entry of 0 (or a track index past the slice) is a FLEXIBLE track: after the fixed tracks and gutters are removed, the remaining space is split equally among the flexible tracks. An absent/empty slice makes every track flexible, i.e. the all-equal historical layout.
Grid is a Widget: Draw fans out to every attached child + OnEvent hit-tests then forwards.
func NewGrid ¶
NewGrid constructs an empty cols x rows grid. cols + rows must be positive; the constructor clamps non-positive inputs to 1 to keep the divide-by-zero out of SetBounds.
func (*Grid) A11y ¶ added in v0.130.0
A11y reports the Grid as presentational. A data table is RoleGrid; this is a layout grid, which is a different thing wearing a similar name.
func (*Grid) Attach ¶
Attach places w at (col, row). Out-of-range coordinates are clamped into the grid so a typo doesn't silently vanish + the child still ends up somewhere visible. Re-runs layout immediately.
func (*Grid) OnEvent ¶
OnEvent hit-tests attached children + forwards with translated coordinates. EventMouseMove is forwarded to every attached child instead, so hover-enter and hover-leave both propagate (see HBox.OnEvent). Keyboard events go through the focus system and a click also moves focus to the focusable it hits (see HBox.OnEvent).
type GroupCard ¶ added in v0.164.0
type GroupCard struct {
Base
// Pill is the coloured source tag (e.g. "Usenet"). Empty draws no pill.
Pill string
// PillColor / PillInk colour the source pill; the zero value (A==0) falls back
// to Theme.Accent / a readable ink (see PostCard.pillInk).
PillColor, PillInk RGBA
// Status is the optional status pill beside the source pill (e.g. "complete").
// Empty hides it.
Status string
// StatusColor / StatusInk colour the status pill; the zero value falls back to
// Theme.Accent / a readable ink.
StatusColor, StatusInk RGBA
// Title is the group's headline (e.g. the release base name), one elided line.
Title string
// Meta is the muted summary line (e.g. "12 parts · 3 files · 40 MB").
Meta string
// Members are the expanded part lines, one preformatted string per row.
Members []string
// Actionable enables the header affordance: a download checkbox and the Action
// pill. When false neither is drawn and CheckRect / ActionRect are empty.
Actionable bool
// Action is the pill label shown when Actionable (e.g. "Reconstruct"). Empty
// draws no pill but still reserves the checkbox when Actionable.
Action string
// Per-element fonts, each optional (nil falls back to EffectiveFont). TitleFont
// sizes the headline, MetaFont the meta + member lines, PillFont the badges and
// the action pill.
TitleFont, MetaFont, PillFont Font
// contains filtered or unexported fields
}
GroupCard is a collapsible summary card for a set of related items — a multi-part post, a thread, a release split across files. Its header is always shown: a disclosure chevron, a coloured source pill, an optional status pill (e.g. "complete" / "incomplete"), a title, and a muted meta line. When the post is Actionable it also carries, right-aligned in the header, a download checkbox and an action pill (e.g. "Reconstruct"). Expanding the card lists its Members — one preformatted line per part — beneath the header, divider-separated.
Layout (inside the CardPadX/Y inset):
┌──────────────────────────────────────────────┐ │ ▸ [Pill] [Status] [x] (Action) │ ← header: chevron, badges, affordance │ Title over one elided line │ │ meta · line · here │ │ ── member line 1 ────────────── │ ← Members, only when Expanded │ ── member line 2 ────────────── │ └──────────────────────────────────────────────┘
Like PostCard it is passive content: it lays out and paints itself and reports its exact height through Measure(width) (taller when Expanded); a feed list (CardList / VirtualList) puts selection / hover affordances on top, and reads the chevron / checkbox / action hit rectangles (ChevronRect / CheckRect / ActionRect) to route clicks. The title, meta and member lines are real Labels exposed through Children, so CollectRuns lifts them out as selectable text runs.
func NewGroupCard ¶ added in v0.164.0
NewGroupCard builds a collapsed GroupCard from its header text fields.
func (*GroupCard) ActionRect ¶ added in v0.164.0
ActionRect is the Action pill's rectangle, right-aligned and vertically centred on the header. Empty when the card is not Actionable or Action is unset.
func (*GroupCard) CheckRect ¶ added in v0.164.0
CheckRect is the download checkbox's rectangle, left of the Action pill (or right-aligned when there is no Action pill). Empty when not Actionable.
func (*GroupCard) Checked ¶ added in v0.164.0
func (c *GroupCard) Checked() *mvvm.Observable[bool]
Checked is the reactive download-checkbox state as a shared mvvm.Observable. Lazily created, defaulting to unchecked.
func (*GroupCard) ChevronRect ¶ added in v0.164.0
ChevronRect is the square hit target for the disclosure chevron, vertically centred on the header content at the card's left.
func (*GroupCard) Children ¶ added in v0.164.0
Children yields the card's selectable Labels in visual order — the title, the meta line, then each expanded member line — so CollectRuns lifts them out as text runs. Chevron, badges, checkbox and action pill are decoration and are not returned. Calling Children re-assembles the tree at the card's current bounds.
func (*GroupCard) Draw ¶ added in v0.164.0
Draw paints the card frame, the header (chevron, source + status pills, download checkbox + action pill), the title and meta, and — when expanded — the divider-separated member rows. Muted inks are theme-derived here, at paint time.
func (*GroupCard) Expanded ¶ added in v0.164.0
func (c *GroupCard) Expanded() *mvvm.Observable[bool]
Expanded is the reactive disclosure state as a shared mvvm.Observable: true shows the member list below the header. Lazily created, defaulting to collapsed.
func (*GroupCard) Measure ¶ added in v0.164.0
Measure reports the card's exact height at outer width width: the CardPadY inset top and bottom, the header content, and — when expanded — the member rows.
func (*GroupCard) MemberRect ¶ added in v0.164.0
MemberRect is the i-th member row's rectangle within the expanded body.
type HBox ¶
type HBox struct {
Base
// Spacing is the gap in pixels between adjacent children. NewHBox seeds it to
// DefaultBoxSpacing (4); it is then honoured literally, so setting it to 0
// yields a flush box and negative values are clamped to zero at layout time.
Spacing int
// Align positions each child on the cross (vertical) axis; the zero value
// BoxStretch fills the height (the historical behaviour). Pack distributes
// leftover width when the children do not fill the box (no flex child).
Align BoxAlign
Pack BoxPack
// contains filtered or unexported fields
}
HBox is a horizontal flow container. Children are laid out left-to-right; each takes a flex share of the width or a fixed width (see boxChild), with Spacing gaps between them. Children's Y + height fill the box's vertical extent.
HBox is a Widget itself: Draw fans out to every child + OnEvent hit-tests by child Bounds, translating coordinates into the matched child's local space.
func NewHBox ¶
func NewHBox() *HBox
NewHBox constructs an empty HBox with Spacing seeded to DefaultBoxSpacing. Add children via Append/AddFlex/AddFixed.
func (*HBox) A11y ¶ added in v0.130.0
A11y reports the HBox as presentational: it arranges its children and carries no meaning of its own.
func (*HBox) AddFixed ¶ added in v0.50.0
AddFixed adds w with a fixed width in pixels (clamped to ≥0).
func (*HBox) AddFlex ¶ added in v0.50.0
AddFlex adds w with an explicit flex weight (clamped to ≥1).
func (*HBox) Children ¶ added in v0.123.0
Children yields the box's child widgets in insertion order, so generic tree walkers (e.g. CollectRuns) can descend without knowing the box type.
func (*HBox) OnEvent ¶
OnEvent forwards to the first child whose Bounds contains the event point, translated into that child's local space. EventMouseMove is forwarded to EVERY child (translated) instead, so the child under the pointer raises its hover face while the ones it left clear theirs. Keyboard events go through the focus system (routeFocusKey): Tab/Shift+Tab move focus, other keys route to the focused descendant; a click also moves focus to the focusable it hits.
type HeaderBar ¶ added in v0.7.0
type HeaderBar struct {
Base
Title string
Subtitle string
Start []Widget // rendered left-to-right along the left edge
End []Widget // rendered right-to-left along the right edge
}
HeaderBar is the GTK "client-side decorations" bar: an optional row of Start widgets (usually navigation — back, menu), a centred Title (+ optional Subtitle) and an optional row of End widgets (usually actions — search, close). Composes cleanly above a Notebook + Statusbar so an app can assemble a stock GNOME window out of just three toolkit widgets.
Start widgets paint left-to-right from the bar's left edge; End widgets paint right-to-left from the bar's right edge. The title (and subtitle, when non-empty) are centred horizontally in whatever space remains between the two child regions.
HeaderBar positions its Start/End children in SetBounds (so their Bounds are correct before the first paint) and forwards pointer events to them in OnEvent, hit-testing each child and translating the event into its local frame -- the same dispatch HBox does. A child whose Bounds contains the event handles it; a click elsewhere on the bar is ignored.
func NewHeaderBar ¶ added in v0.7.0
NewHeaderBar constructs a HeaderBar carrying title. Subtitle, Start and End remain zero-valued; the caller populates them before the first Draw.
func (*HeaderBar) A11y ¶ added in v0.40.0
A11y reports the HeaderBar as a banner named by its title.
func (*HeaderBar) Children ¶ added in v0.137.0
Children yields the leading widgets then the trailing ones, which is how the bar reads left to right.
func (*HeaderBar) Draw ¶ added in v0.7.0
Draw paints the bar body, (re)positions + draws every Start / End child, then paints Title (+ Subtitle when non-empty) centred in whatever horizontal space is left between the two child regions.
func (*HeaderBar) OnEvent ¶ added in v0.104.0
OnEvent forwards a pointer event to the first Start / End child whose Bounds contains it, translated into that child's local frame -- mirroring HBox's dispatch. EventMouseMove goes to every child (so each raises/clears its hover face); every other kind lands only on the child under the pointer. Event coordinates are widget-local. layout() runs first so a child added after the last SetBounds still has current Bounds to hit-test against.
type Highlighter ¶ added in v0.180.0
type Highlighter interface {
Highlight(language string, lines []string, theme *Theme) [][]TextSpan
}
Highlighter turns a source buffer into per-line coloured spans. It is the pluggable seam a CodeEditor uses for syntax highlighting, so the toolkit core carries no lexer of its own: a consumer supplies a rouge-backed implementation (github.com/go-widgets/toolkit/rougelex) — or any other — and importing the core toolkit never pulls a highlighting engine in.
Highlight receives the WHOLE buffer (as lines) rather than one line at a time, so a multi-line construct — a block comment, a heredoc, a triple quoted string — is coloured correctly across the lines it spans. The returned slice is indexed by line: element i holds the spans covering lines[i] in that line's rune coordinates ([Start, End), the same half-open convention as TextSpan). An implementation returns one entry per input line (len(result) == len(lines)); CodeEditor tolerates a short or long result by treating a missing row as "no spans".
type IconButton ¶ added in v0.9.0
type IconButton struct {
Base
Icon string
OnClick func()
// contains filtered or unexported fields
}
IconButton is a compact toolbar button whose entire face is one short glyph string ("+", "OK", "v", ...). Distinct from Button (which carries a text label with hover/press states) and ToggleButton (which carries toggle state) — IconButton is a passive Surface-faced tile meant for dense toolbars where the glyph itself is the semantic content.
The face is theme.Surface with a 1-px theme.Border stroke; the glyph renders in theme.OnSurface. No accent fill by default — this keeps the button reading as a subtle toolbar affordance rather than a primary action.
Auto-sizing: if Bounds().W is zero the first Draw() resizes the button to IconButtonSize x IconButtonSize (H preserved when non-zero). A pre-sized Bounds is honoured verbatim so a fixed toolbar column doesn't shift when the widget is dropped in.
func NewIconButton ¶ added in v0.9.0
func NewIconButton(icon string, onClick func()) *IconButton
NewIconButton constructs an IconButton carrying the given glyph + click handler. onClick may be nil (a no-op button is still rendered). Bounds default to zero so the first Draw() auto-sizes the widget to IconButtonSize x IconButtonSize.
func (*IconButton) A11y ¶ added in v0.40.0
func (b *IconButton) A11y() A11yInfo
A11y reports the IconButton as a button named by its icon identifier (it has no separate text label).
func (*IconButton) Draw ¶ added in v0.9.0
func (i *IconButton) Draw(p painter.Painter, theme *Theme)
Draw paints the surface + border and centres Icon inside using the toolkit's 5x7 bitmap font. If Bounds().W is zero the widget resizes itself to IconButtonSize x IconButtonSize (H preserved when already non-zero) before painting.
func (*IconButton) Focused ¶ added in v0.101.0
func (f *IconButton) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*IconButton) HitRect ¶ added in v0.187.0
func (i *IconButton) HitRect() Rect
HitRect is the icon button's interactive rectangle: its drawn Bounds clamped up to the density hit-target and centred over them (see [touchHitRect]). Byte-identical to Bounds under DensityCompact; a compact 28px toolbar button exposes a >=44px finger target under DensityTouch without changing its glyph.
func (*IconButton) HitTest ¶ added in v0.187.0
func (i *IconButton) HitTest(px, py int) bool
HitTest reports whether a surface point falls on the icon button's (touch-clamped) hit rect — the default Bounds().Contains at compact, the finger-sized area at touch.
func (*IconButton) OnEvent ¶ added in v0.9.0
func (i *IconButton) OnEvent(ev Event)
OnEvent drives the button from pointer events: EventClick presses it (showing the pressed face) and fires OnClick, EventMouseUp releases it, EventMouseMove tracks the hover face. A Disabled button ignores every kind. OnClick is nil-safe.
func (*IconButton) SetFocused ¶ added in v0.101.0
func (f *IconButton) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
type IconCell ¶ added in v0.136.0
IconCell is one cell of an IconGrid: a thumbnail/icon and a label. Raster marks the Image as a real raster thumbnail (a photo, a rendered preview) so the cell paints a light chip behind it; leave it false for a flat vector/symbol icon that needs no backing. Key is an opaque caller identity carried by DragData.
type IconFunc ¶ added in v0.80.0
IconFunc paints a vector/stock icon into rect r using ink as its stroke colour — the same shape the toolkit's own DrawIcon*** helpers (DrawIconNew, DrawIconSettings, ...) and Table's RowIcon already use. A StatusIcon takes one so a caller can hang any of the stock icons, or a hand-drawn one, in a tray slot without shipping a bitmap.
type IconGrid ¶ added in v0.136.0
type IconGrid struct {
Base
// Cells is the ordered grid content. Mutating it is reflected on the next
// Draw; the scroll offset is clamped on the fly, so shrinking Cells never
// scrolls past the end.
Cells []IconCell
// IconSize is the icon square side in pixels; it drives the whole cell
// footprint. Set it via SetIconSize (which enforces a sane minimum).
IconSize int
// Empty is the message centred in an empty grid; a blank Empty falls back to
// a generic default.
Empty string
// OnActivate fires when the already-selected cell is clicked again, with its
// index. Nil-guarded.
OnActivate func(index int)
// contains filtered or unexported fields
}
IconGrid is a selectable, size-driven grid of icon/thumbnail cells that reflows to the widget width. It generalizes a file-manager "icon view": unlike a bare reflowing grid it owns per-cell chrome — the icon is centred and fit in a subtle rounded frame, a real raster thumbnail sits on a light backing chip so a dark image stays visible on a dark theme, and the label is centred and elided to the cell width — plus selection and hit-testing. The cell footprint is driven by IconSize, so a host slider can resize every cell live.
Layout: a body filled with Theme.Surface (or a centred empty-state message when there are no cells), then a reflowing grid of uniform cells centred within any left-over horizontal slack, scrolled vertically and clipped to the widget bounds. Each cell reserves padding above the icon, the icon square, a gap, and a label band; the selected cell paints a soft rounded field behind its icon and a rounded accent highlight behind its label.
Selection + navigation: a click selects the cell under the pointer (firing OnSelect); a second click on the already-selected cell activates it (firing OnActivate). The wheel scrolls the grid. Selected / SetSelected read and drive the selection programmatically, and DragData makes a selected cell a DragSource carrying its Key.
Example ¶
ExampleIconGrid builds a small thumbnail grid, selects a cell and reports it.
g := NewIconGrid(
IconCell{Label: "Report.pdf", Key: "report"},
IconCell{Label: "Photo.jpg", Key: "photo", Raster: true},
)
g.SetBounds(Rect{X: 0, Y: 0, W: 200, H: 160})
g.Draw(newP(makeSurface(200, 160), 200), DefaultLight())
g.OnEvent(Event{Kind: EventClick, X: 40, Y: 40})
fmt.Printf("selected cell %d\n", g.Selected().Get())
Output: selected cell 0
func NewIconGrid ¶ added in v0.136.0
NewIconGrid builds an IconGrid over cells with a default icon size. Nothing is selected initially.
func (*IconGrid) A11y ¶ added in v0.136.0
A11y reports the IconGrid as a grid. Value is the selected cell's label, or empty when nothing is selected.
func (*IconGrid) DragData ¶ added in v0.136.0
DragData reports the selected cell's Key, or "" when nothing is selected. It makes the IconGrid a DragSource.
func (*IconGrid) Draw ¶ added in v0.136.0
Draw paints the visible cells (or the empty-state message), clipped to the widget bounds.
func (*IconGrid) IndexAt ¶ added in v0.136.0
IndexAt maps a widget-local point to a cell index, or -1 for empty space.
func (*IconGrid) OnEvent ¶ added in v0.136.0
OnEvent scrolls on the wheel, selects on a click, activates on a second click of the selected cell, and is inert while Disabled.
func (*IconGrid) Selected ¶ added in v0.136.0
func (v *IconGrid) Selected() *mvvm.Observable[int]
Selected is the selected cell index (-1 = none) as a shared mvvm.Observable: a host binds it (or subscribes for the old OnSelect notification) instead of reading a field, and a click Sets it. Lazily created so a bare &IconGrid{} works (its zero selection is -1, "nothing selected").
func (*IconGrid) SetIconSize ¶ added in v0.136.0
SetIconSize sets the icon square side, clamped to a readable minimum, and resets the scroll so the reflow stays anchored at the top.
func (*IconGrid) SetSelected ¶ added in v0.136.0
SetSelected selects cell index; an out-of-range index clears the selection.
type Image ¶
type Image struct {
Base
Pixels []byte // RGBA bytes, W*H*4 in length
W, H int // source dimensions
Scale ScaleMode // how the source maps onto the bounds (default ScaleStretch)
// Alt is the image's accessible name — the short description a reader
// announces in place of the picture. Set it to whatever the source calls the
// image (a photo's caption, a chart's summary, a post's alt text); leave it
// empty ONLY for decoration that carries no information, which is the same
// rule as an empty HTML alt attribute.
Alt string
}
Image paints a caller-supplied RGBA byte buffer into its bounds. Scaling is nearest-neighbour; Scale selects stretch-to-fill (default) or aspect-preserving fit-and-centre.
func NewImage ¶
NewImage wraps pixels (length must equal w*h*4) + the source dimensions in a stretch-to-fill image. Caller owns the pixels; the toolkit just reads them.
func NewImageFit ¶ added in v0.45.0
NewImageFit is NewImage with ScaleFit: the image preserves its aspect ratio and is centred within its bounds.
type InlineStyles ¶ added in v0.227.0
InlineStyles reports which inline styles are in force at the caret (or across the whole selection). A style is Active when a collapsed caret sits in a run carrying it — or, with the toggle armed, when the next typed rune would carry it — and, for a non-empty selection, only when EVERY selected cell carries it (the same "all set" test [RichEditor.toggleInlineStyle] uses to decide whether a toggle adds or removes the style). It is the query a formatting toolbar reads to light its Bold / Italic / Strikethrough / Code buttons.
type IsoAnimatedIcon ¶ added in v0.219.0
type IsoAnimatedIcon interface {
IsoIcon
// RenderAt returns the drawing for cell (x, y) shaded from base at animation
// phase, a cycle position normalised into [0, 1). The implementation must not
// retain x, y or base.
RenderAt(x, y int, base stdcolor.RGBA, phase float64) IsoIconDrawing
}
IsoAnimatedIcon is an IsoIcon whose drawing varies with a time PHASE — a procedural animation authored from the very same isometric primitives as the still icons, never from external art. It renders one frame for a given phase; the diagram drives that phase from the host's clock (see IsoDiagram.AnimationStep).
The phase is a wrap-around cycle position: it is normalised into [0, 1) before a frame is composed, so phase p, p+1 and p-1 all render identically. A still (never-driven) diagram holds phase 0, and the interface's own IsoIcon.Render MUST equal IsoAnimatedIcon.RenderAt at phase 0 — the rest frame — so an animated icon that is never stepped draws exactly like an authored still.
type IsoArrow ¶ added in v0.201.0
type IsoArrow int
IsoArrow selects the end decorations of an IsoConnector.
const ( // IsoArrowNone draws no head — the zero value and the legacy default, so a // connector that never sets an arrow renders as the bare line it always was. IsoArrowNone IsoArrow = iota // IsoArrowSingle draws a filled head at the target (To) end. IsoArrowSingle // IsoArrowDouble draws a head at both the source (From) and target (To) ends. IsoArrowDouble )
type IsoCRDTDocument ¶ added in v0.203.0
type IsoCRDTDocument struct {
// contains filtered or unexported fields
}
IsoCRDTDocument is a collaborative IsoDocument backed by the shared structured-document CRDT core (structured.Document): every entity of all five families — nodes, connectors, zones, text annotations and layers — lives in one crdt.Composite, so any number of replicas may edit the diagram at once, offline and in any order, and every replica converges to the same diagram. It is a drop-in for the in-memory IsoDoc: an IsoDiagram built on it renders and edits through the exact same interface, with no widget change.
It adds NO merge logic. The convergence, commutativity, idempotence and associativity are the structured/crdt package's, inherited whole. This type is only the mapping between the widget's value entities and the CRDT's records:
- An entity's stable string ID (IsoNode.ID and the rest) is the CRDT record key directly — there is no local id table. Two replicas that place "server-a" both name the same record and converge.
- Each entity field is one independent LWW register under the caller's own field name, so one replica moving a node while another recolours it keeps both edits. Only a field whose value differs from the entity's default is stored, so a snapshot stays compact and re-reads to the same value.
- Integer fields (positions, sizes, order, width) travel through structured.EncodeInt and boolean fields through structured.EncodeBool, so a document compiled to js/wasm stores byte-for-byte what a 64-bit server does. Colours and enums reuse this package's own JSON codec words, so there is no third encoding of a shape or a colour.
Fan-out through MVVM ¶
State crossing into a view crosses through go-widgets/mvvm: a local edit and an applied remote batch both tick an mvvm.Observable revision, and IsoCRDTDocument.Subscribe — the one method IsoDiagram binds to — registers a change observer over it, so every bound view repaints on a merge exactly as it does on a local edit.
Transport ¶
IsoCRDTDocument.OpsSince and IsoCRDTDocument.Apply exchange operations with peers; IsoCRDTDocument.Snapshot and LoadIsoCRDTDocument carry a whole document to a late joiner or to disk; IsoCRDTDocument.Version drives a delta sync. All of it is the structured.Document's own transport, re-exposed.
An IsoCRDTDocument is not safe for concurrent use; drive it from one goroutine and exchange operations, not the value, between replicas.
func LoadIsoCRDTDocument ¶ added in v0.203.0
func LoadIsoCRDTDocument(site crdt.SiteID, snapshot []byte) (*IsoCRDTDocument, error)
LoadIsoCRDTDocument rebuilds a collaborative document from a IsoCRDTDocument.Snapshot, to be edited as site. A malformed snapshot is returned as an error, never a panic.
func NewIsoCRDTDocument ¶ added in v0.203.0
func NewIsoCRDTDocument(site crdt.SiteID) *IsoCRDTDocument
NewIsoCRDTDocument returns an empty collaborative document that issues operations as site. Every replica editing one diagram concurrently must pass a distinct crdt.SiteID.
func (*IsoCRDTDocument) Apply ¶ added in v0.203.0
func (d *IsoCRDTDocument) Apply(batches ...crdt.PartOps) error
Apply integrates batches of operations from peers, tolerating duplicates and reordering, and ticks the revision so every bound view repaints. An invalid batch is reported as an error and changes nothing.
func (*IsoCRDTDocument) Connectors ¶ added in v0.203.0
func (d *IsoCRDTDocument) Connectors() []IsoConnector
Connectors returns every connector present, ascending by ID.
func (*IsoCRDTDocument) Layer ¶ added in v0.203.0
func (d *IsoCRDTDocument) Layer(id string) (IsoLayer, bool)
Layer returns the layer with the given ID and whether it exists.
func (*IsoCRDTDocument) Layers ¶ added in v0.203.0
func (d *IsoCRDTDocument) Layers() []IsoLayer
Layers returns every layer present, ascending by ID.
func (*IsoCRDTDocument) Node ¶ added in v0.203.0
func (d *IsoCRDTDocument) Node(id string) (IsoNode, bool)
Node returns the node with the given ID and whether it exists.
func (*IsoCRDTDocument) Nodes ¶ added in v0.203.0
func (d *IsoCRDTDocument) Nodes() []IsoNode
Nodes returns every node present, ascending by ID.
func (*IsoCRDTDocument) OpsSince ¶ added in v0.203.0
func (d *IsoCRDTDocument) OpsSince(v crdt.CompositeVersion) []crdt.PartOps
OpsSince returns the operations this replica holds that v does not, across all families, ready to send to the peer that produced v. Pass a nil version for everything.
func (*IsoCRDTDocument) Pending ¶ added in v0.203.0
func (d *IsoCRDTDocument) Pending() int
Pending reports how many received operations are still waiting for the operations they depend on. It is zero once a replica has caught up.
func (*IsoCRDTDocument) PutConnector ¶ added in v0.203.0
func (d *IsoCRDTDocument) PutConnector(c IsoConnector)
PutConnector inserts or replaces a connector by ID.
func (*IsoCRDTDocument) PutLayer ¶ added in v0.203.0
func (d *IsoCRDTDocument) PutLayer(l IsoLayer)
PutLayer inserts or replaces a layer by ID.
func (*IsoCRDTDocument) PutNode ¶ added in v0.203.0
func (d *IsoCRDTDocument) PutNode(n IsoNode)
PutNode inserts a node or, when one already has the same ID, replaces its fields (an upsert with last-writer-wins fields).
func (*IsoCRDTDocument) PutText ¶ added in v0.203.0
func (d *IsoCRDTDocument) PutText(t IsoText)
PutText inserts or replaces a text annotation by ID.
func (*IsoCRDTDocument) PutZone ¶ added in v0.203.0
func (d *IsoCRDTDocument) PutZone(z IsoZone)
PutZone inserts or replaces a zone by ID.
func (*IsoCRDTDocument) RemoveConnector ¶ added in v0.203.0
func (d *IsoCRDTDocument) RemoveConnector(id string)
RemoveConnector deletes a connector by ID; a missing ID is a no-op.
func (*IsoCRDTDocument) RemoveLayer ¶ added in v0.203.0
func (d *IsoCRDTDocument) RemoveLayer(id string)
RemoveLayer deletes a layer by ID; a missing ID is a no-op. Entities still naming a removed layer fall back to the implicit default layer at draw time, exactly as with the in-memory document.
func (*IsoCRDTDocument) RemoveNode ¶ added in v0.203.0
func (d *IsoCRDTDocument) RemoveNode(id string)
RemoveNode deletes the node with id and every connector attached to it. A missing ID is a complete no-op — no connector is cascaded — exactly as the in-memory document behaves.
func (*IsoCRDTDocument) RemoveText ¶ added in v0.203.0
func (d *IsoCRDTDocument) RemoveText(id string)
RemoveText deletes a text annotation by ID; a missing ID is a no-op.
func (*IsoCRDTDocument) RemoveZone ¶ added in v0.203.0
func (d *IsoCRDTDocument) RemoveZone(id string)
RemoveZone deletes a zone by ID; a missing ID is a no-op.
func (*IsoCRDTDocument) Rev ¶ added in v0.203.0
func (d *IsoCRDTDocument) Rev() uint64
Rev returns the current revision counter, which ticks on every change.
func (*IsoCRDTDocument) Site ¶ added in v0.203.0
func (d *IsoCRDTDocument) Site() crdt.SiteID
Site returns the replica identity this document issues operations as.
func (*IsoCRDTDocument) Snapshot ¶ added in v0.203.0
func (d *IsoCRDTDocument) Snapshot() []byte
Snapshot encodes the whole document — every family — for a joining peer or for persistence. Two replicas holding the same operations produce identical bytes.
func (*IsoCRDTDocument) Subscribe ¶ added in v0.203.0
func (d *IsoCRDTDocument) Subscribe(fn func()) (unsubscribe func())
Subscribe registers fn to run after every change — a local edit or an applied remote batch — and returns a function that unsubscribes it. It is the seam the widget binds its repaint to.
func (*IsoCRDTDocument) Text ¶ added in v0.203.0
func (d *IsoCRDTDocument) Text(id string) (IsoText, bool)
Text returns the text annotation with the given ID and whether it exists.
func (*IsoCRDTDocument) Texts ¶ added in v0.203.0
func (d *IsoCRDTDocument) Texts() []IsoText
Texts returns every text annotation present, ascending by ID.
func (*IsoCRDTDocument) Version ¶ added in v0.203.0
func (d *IsoCRDTDocument) Version() crdt.CompositeVersion
Version returns what this replica holds, to hand a peer that sends back what it is missing; see IsoCRDTDocument.OpsSince.
func (*IsoCRDTDocument) Zone ¶ added in v0.203.0
func (d *IsoCRDTDocument) Zone(id string) (IsoZone, bool)
Zone returns the zone with the given ID and whether it exists.
func (*IsoCRDTDocument) Zones ¶ added in v0.203.0
func (d *IsoCRDTDocument) Zones() []IsoZone
Zones returns every zone present, ascending by ID.
type IsoConnector ¶ added in v0.200.0
type IsoConnector struct {
// ID is the connector's stable identity (the OR-map key).
ID string
// From and To are the IDs of the source and destination nodes.
From, To string
// Label is an optional caption drawn along the routed path (at its midpoint,
// offset off the line so it does not sit on the stroke).
Label string
// Style selects the stroke pattern. The zero value [IsoSolid] is the legacy
// continuous line.
Style IsoConnectorStyle
// Arrow selects the end heads. The zero value [IsoArrowNone] draws none.
Arrow IsoArrow
// Color is the stroke colour; a zero value (A==0) inherits the theme's link
// colour (OnSurface) at draw time, so a connector left uncoloured is still
// visible and follows the light/dark theme like the rest of the widget.
Color RGBA
// Width is the stroke thickness in logical pixels; zero uses the default and
// any positive value is routed through the toolkit's HiDPI/density scale, so
// connectors thicken in lockstep with the rest of the chrome.
Width int
// Routed, when true, routes the connector along the isometric grid — a chain
// of grid-orthogonal segments anchored on the source and target node faces
// (the face nearest the neighbour) — instead of the single straight
// anchor-to-anchor segment. The zero value (false) is the legacy straight
// line. The route is COMPUTED from the endpoints at draw time, never stored.
Routed bool
// Layer names the [IsoLayer] this connector belongs to; the zero value (the
// empty string) is the implicit default layer, so an unlayered connector
// behaves exactly as before. See [IsoLayer].
Layer string
}
IsoConnector is a directed link between two [IsoNode]s. From and To are node IDs; a connector whose endpoints are not both present in the document is skipped when drawing.
Every field beyond ID/From/To is an enrichment whose zero value reproduces the original bare connector: an unset connector routes as a single straight segment between the two node top anchors, solid, headless, in the theme's link colour, at the default width — byte-for-byte the pre-enrichment rendering.
type IsoConnectorStyle ¶ added in v0.201.0
type IsoConnectorStyle int
IsoConnectorStyle selects the stroke pattern of an IsoConnector.
const ( // IsoSolid is a continuous line — the zero value and the legacy default, so a // connector that never sets a style draws exactly as it did before styles // existed. IsoSolid IsoConnectorStyle = iota // IsoDashed is a line of evenly spaced dashes. IsoDashed // IsoDotted is a line of short round dots. IsoDotted )
type IsoDiagram ¶ added in v0.200.0
type IsoDiagram struct {
Base
// Cols and Rows are the ground grid's extent in cells.
Cols, Rows int
// DefaultShape is the solid a newly placed node takes.
DefaultShape IsoShape
// Icons, when non-nil, is the per-widget icon registry an [IsoNode]'s Icon id
// resolves through; nil uses the package-global [IsoDefaultIcons]. A host sets
// this to give one diagram its own component library without touching the
// shared default.
Icons *IsoIconRegistry
// Mode selects the left-drag behaviour (move vs connect).
Mode IsoMode
// AnimationPeriod is the wall-clock length of one full animation cycle, in the
// same unit AnimationStep's dt is given in (seconds by convention). The global
// animation phase advances by dt/AnimationPeriod each step and wraps at 1. A
// non-positive value is treated as the default (isoDefaultAnimPeriod), so the
// zero value animates at the default rate. It is plain view state (like pan or
// zoom) and never enters the document.
AnimationPeriod float64
// OnSelect fires when the selected node changes; id is "" when the selection
// is cleared.
OnSelect func(id string)
// OnSelectConnector fires when the selected connector changes; id is "" when
// the connector selection is cleared. It mirrors OnSelect for the connector
// half of the selection, so a host can drive a "connector properties" panel.
OnSelectConnector func(id string)
// OnSelectZone fires when the selected zone changes; id is "" when the zone
// selection is cleared. It mirrors OnSelect for the zone third of the
// selection, so a host can drive a "zone properties" panel.
OnSelectZone func(id string)
// OnSelectText fires when the selected text annotation changes; id is "" when
// the text selection is cleared. It mirrors OnSelect for the text quarter of
// the selection, so a host can drive a "text properties" panel.
OnSelectText func(id string)
// OnInvalidate, when set, is called whenever the widget's appearance changed
// and it should be redrawn. A document edit (including one from another
// collaborator, via the store's Subscribe) also triggers it.
OnInvalidate func()
// contains filtered or unexported fields
}
IsoDiagram is an editable isometric diagram widget — a FossFLOW-style node / connector editor. It owns an IsoDocument (nodes + connectors) and an isometric iso.Projection; it renders the ground grid, each node as a shaded isometric solid and each connector as a depth-sorted line by compositing an iso.Scene onto a pixel buffer, then blitting that buffer through the Painter. All projection and primitive drawing is delegated to github.com/go-gfx/gfx/iso — the widget adds only the document model, the interactions (place / drag / connect / select / delete / pan / zoom / context-menu / undo) and the accessibility tree.
func NewIsoDiagram ¶ added in v0.200.0
func NewIsoDiagram(doc IsoDocument) *IsoDiagram
NewIsoDiagram returns an isometric diagram editing doc. A nil doc gets a fresh empty IsoDoc. The widget subscribes to the document so any edit — local or from a collaborating store — repaints via OnInvalidate.
func NewIsoDiagramView ¶ added in v0.202.0
func NewIsoDiagramView(doc IsoDocument) *IsoDiagram
NewIsoDiagramView returns an additional IsoDiagram bound to an EXISTING document, for showing one model in several synchronised views. Because every edit flows through the document's observable lists, a mutation made through any view is seen by all of them: each view subscribes to the shared document and repaints on change.
Only the MODEL is shared. Everything a view owns is local to that widget: its isometric iso.Projection (pan / zoom), its selection set, its in-flight gesture and its undo/redo stacks all live on the widget, not in the document. So two views can be panned, zoomed and selected independently while editing the same diagram — panning one never moves the other, and selecting in one never changes the other's highlight, yet placing or moving a node in either appears in both. It is a thin, intention-revealing alias for NewIsoDiagram: pass the same IsoDocument to as many views as you need.
func (*IsoDiagram) A11y ¶ added in v0.200.0
func (d *IsoDiagram) A11y() A11yInfo
A11y describes the widget itself as a group holding the diagram's elements.
func (*IsoDiagram) AcceptsDrop ¶ added in v0.206.0
func (d *IsoDiagram) AcceptsDrop(payload string) bool
AcceptsDrop makes the diagram a DropTarget for icon drops from an IsoIconPalette: it accepts exactly the payloads DecodeIsoIconPayload recognises (an "iso-icon:<id>" item, possibly among several newline-separated ones), so a host shows the accept cursor over the canvas only for a real icon drag and never for an unrelated payload.
func (*IsoDiagram) AddLayer ¶ added in v0.202.0
func (d *IsoDiagram) AddLayer(name string) string
AddLayer creates a new visible, unlocked layer named name, stacked one order above the current top layer, and returns its id — as one undoable command.
func (*IsoDiagram) AnimationPhase ¶ added in v0.219.0
func (d *IsoDiagram) AnimationPhase() float64
AnimationPhase is the diagram's current global animation phase in [0, 1). A host may read it to drive a synchronised affordance, or to snapshot/restore an animation for a deterministic test.
func (*IsoDiagram) AnimationStep ¶ added in v0.219.0
func (d *IsoDiagram) AnimationStep(dt float64)
AnimationStep advances the diagram's global animation phase by dt (elapsed wall-clock time, in AnimationPeriod's unit — seconds by convention) and, when the document holds at least one node carrying an animated icon, requests a repaint via OnInvalidate. The host owns the clock and calls this each frame; the widget keeps no timer, so the rendering stays a deterministic function of the accumulated dt — two diagrams stepped by the same dt sequence render identically.
The phase wraps into [0, 1) after each step, so a long dt (or many steps) folds through the cycle correctly. A diagram whose nodes carry only still icons (or no icon) still advances its phase but never invalidates — nothing on screen moves — and a diagram that is never stepped renders the rest frame, byte-identical to the pre-animation widget.
func (*IsoDiagram) AssignLayer ¶ added in v0.202.0
func (d *IsoDiagram) AssignLayer(ref IsoEntityRef, layerID string) bool
AssignLayer moves one entity to the layer with layerID (undoable). Returns false when the entity is absent or already on that layer.
func (*IsoDiagram) AssignSelectionToLayer ¶ added in v0.202.0
func (d *IsoDiagram) AssignSelectionToLayer(layerID string) bool
AssignSelectionToLayer moves every selected entity to the layer with layerID as ONE undoable command. Returns false when nothing is selected or no entity's layer actually changes.
func (*IsoDiagram) CanRedo ¶ added in v0.200.0
func (d *IsoDiagram) CanRedo() bool
CanRedo reports whether there is an undone edit to redo.
func (*IsoDiagram) CanUndo ¶ added in v0.200.0
func (d *IsoDiagram) CanUndo() bool
CanUndo reports whether there is an edit to undo.
func (*IsoDiagram) Children ¶ added in v0.200.0
func (d *IsoDiagram) Children() []Widget
Children exposes each node and connector as an accessibility proxy so a screen reader can enumerate the diagram's contents, with the node's label and its selected state. The proxies are synthetic — they are not laid out or drawn; only the a11y walk (and text-run collection, which finds nothing on them) consults them.
func (*IsoDiagram) Close ¶ added in v0.200.0
func (d *IsoDiagram) Close()
Close unsubscribes the widget from its document. It is optional: a diagram that outlives its use leaks only one closure, but a host churning through documents should call it.
func (*IsoDiagram) ContextMenu ¶ added in v0.200.0
func (d *IsoDiagram) ContextMenu() *ContextMenu
ContextMenu returns the widget's right-click menu overlay, so a host can style it or add items.
func (*IsoDiagram) Copy ¶ added in v0.202.0
func (d *IsoDiagram) Copy()
Copy captures the current multi-selection into the widget's clipboard: every selected node, zone and text, plus every connector INTERNAL to the selected nodes (both endpoints selected) so a pasted subgraph keeps its wiring. A selected connector with an unselected endpoint is not captured — it could not be reconnected on paste. Copy does not mutate the document and, with nothing selected, leaves the clipboard untouched.
func (*IsoDiagram) Cut ¶ added in v0.202.0
func (d *IsoDiagram) Cut()
Cut copies the current selection and then deletes it — the copy and the delete being independent, so a following Paste re-inserts what was cut. The delete is one undoable command (see IsoDiagram.DeleteSelection).
func (*IsoDiagram) DeleteLayer ¶ added in v0.202.0
func (d *IsoDiagram) DeleteLayer(id string) bool
DeleteLayer removes the layer with id as one undoable command, first reassigning every entity on it to the default layer so nothing is orphaned. It returns false for the empty (default) id or an unknown layer.
func (*IsoDiagram) DeleteSelection ¶ added in v0.202.0
func (d *IsoDiagram) DeleteSelection()
DeleteSelection removes every selected entity as ONE undoable command and clears the selection. With nothing selected it is a no-op (no undo entry).
func (*IsoDiagram) Doc ¶ added in v0.200.0
func (d *IsoDiagram) Doc() IsoDocument
Doc returns the document the widget edits.
func (*IsoDiagram) Draw ¶ added in v0.200.0
func (d *IsoDiagram) Draw(p painter.Painter, theme *Theme)
Draw renders the diagram: the isometric scene is composited into an opaque buffer the size of the widget, blitted into place, then labels, the rubber-band connector preview, the selection outline and the context menu are drawn over it in painter space.
func (*IsoDiagram) Duplicate ¶ added in v0.202.0
func (d *IsoDiagram) Duplicate()
Duplicate copies the current selection and immediately pastes it (Ctrl-D): a one-gesture clone offset by [isoPasteOffset] cells, leaving the copy selected. With nothing selected it is a no-op and does not disturb the clipboard.
func (*IsoDiagram) ExportJSON ¶ added in v0.202.0
func (d *IsoDiagram) ExportJSON() ([]byte, error)
ExportJSON serialises the widget's document to the native JSON schema (see MarshalIsoDocument) and additionally records a viewport hint — the widget's current grid extent — so a subsequent IsoDiagram.ImportJSON can restore the same canvas size.
func (*IsoDiagram) ImportJSON ¶ added in v0.202.0
func (d *IsoDiagram) ImportJSON(data []byte) error
ImportJSON replaces the widget's document contents with the entities decoded from data, as ONE undoable command: a following IsoDiagram.Undo restores the document exactly as it was. The swap flows through the document's observable lists, so every bound view repaints. A viewport hint in the file, when present, also updates the widget's grid extent. On a decode error the document is left untouched (no undo entry, no repaint) and the error is returned.
func (*IsoDiagram) IsSelected ¶ added in v0.202.0
func (d *IsoDiagram) IsSelected(ref IsoEntityRef) bool
IsSelected reports whether ref is in the current multi-selection.
func (*IsoDiagram) Layers ¶ added in v0.202.0
func (d *IsoDiagram) Layers() []IsoLayer
Layers returns a snapshot of the document's layer set, in document order.
func (*IsoDiagram) OnEvent ¶ added in v0.200.0
func (d *IsoDiagram) OnEvent(ev Event)
OnEvent drives every interaction. While the context menu is open it consumes all input; otherwise a press starts a gesture (move / connect / pan / place), drags update it, release commits it, the wheel zooms and Delete/undo/redo keys act on the selection.
func (*IsoDiagram) Pan ¶ added in v0.200.0
func (d *IsoDiagram) Pan(dx, dy int)
Pan shifts the view by (dx, dy) pixels.
func (*IsoDiagram) Paste ¶ added in v0.202.0
func (d *IsoDiagram) Paste()
Paste inserts the clipboard's entities as ONE undoable command, each shifted by [isoPasteOffset] cells and given a fresh document-unique id, and selects the newly pasted entities. Internal connectors are rewired onto the pasted nodes' new ids (the copied node set guarantees both endpoints were remapped). An empty clipboard is a no-op.
func (*IsoDiagram) PlacementIcon ¶ added in v0.206.0
func (d *IsoDiagram) PlacementIcon() string
PlacementIcon returns the armed click-to-place icon id, or "" when placement is disarmed.
func (*IsoDiagram) PlacementIconObservable ¶ added in v0.206.0
func (d *IsoDiagram) PlacementIconObservable() *mvvm.Observable[string]
PlacementIconObservable exposes the armed click-to-place icon id so a host can bind an IsoIconPalette's selected-icon observable straight into it: once bound, picking an icon in the palette arms the diagram, and the next tap on empty ground drops a node carrying that icon. Binding is the MVVM alternative to the drag-and-drop path for hosts whose back-end has no inter-widget drag.
func (*IsoDiagram) Projection ¶ added in v0.200.0
func (d *IsoDiagram) Projection() *iso.Projection
Projection returns the live isometric projection (tile size + origin). Panning and zooming mutate it in place.
func (*IsoDiagram) Redo ¶ added in v0.200.0
func (d *IsoDiagram) Redo()
Redo re-applies the last undone edit.
func (*IsoDiagram) RenameLayer ¶ added in v0.202.0
func (d *IsoDiagram) RenameLayer(id, name string) bool
RenameLayer sets a layer's display name (undoable). Returns false when the layer is absent or already carries that name.
func (*IsoDiagram) RotateCCW ¶ added in v0.217.0
func (d *IsoDiagram) RotateCCW()
RotateCCW turns the view one quarter-turn counter-clockwise (the previous of the four orientations).
func (*IsoDiagram) RotateCW ¶ added in v0.217.0
func (d *IsoDiagram) RotateCW()
RotateCW turns the view one quarter-turn clockwise (the next of the four orientations).
func (*IsoDiagram) SelectAll ¶ added in v0.202.0
func (d *IsoDiagram) SelectAll()
SelectAll selects every pickable entity (every node, connector, zone and text whose layer is visible and unlocked). Entities on a hidden or locked layer are left out, exactly as they are unclickable.
func (*IsoDiagram) SelectConnector ¶ added in v0.201.0
func (d *IsoDiagram) SelectConnector(id string)
SelectConnector selects the connector with the given id (or clears the connector selection when id is ""). Selecting a connector clears any node, zone or text selection so only one entity ever highlights at once.
func (*IsoDiagram) SelectText ¶ added in v0.202.0
func (d *IsoDiagram) SelectText(id string)
SelectText selects the text annotation with the given id (or clears the text selection when id is ""). Selecting a text clears any node, connector or zone selection so only one entity ever highlights at once.
func (*IsoDiagram) SelectZone ¶ added in v0.202.0
func (d *IsoDiagram) SelectZone(id string)
SelectZone selects the zone with the given id (or clears the zone selection when id is ""). Selecting a zone clears any node, connector or text selection so only one entity ever highlights at once.
func (*IsoDiagram) Selected ¶ added in v0.200.0
func (d *IsoDiagram) Selected() string
Selected returns the selected node's ID, or "" when nothing is selected.
func (*IsoDiagram) SelectedConnector ¶ added in v0.201.0
func (d *IsoDiagram) SelectedConnector() string
SelectedConnector returns the selected connector's ID, or "" when no connector is selected.
func (*IsoDiagram) SelectedConnectorObservable ¶ added in v0.201.0
func (d *IsoDiagram) SelectedConnectorObservable() *mvvm.Observable[string]
SelectedConnectorObservable exposes the connector-selection property so a host can bind it into an MVVM view model (e.g. a connector-style inspector) that stays in sync with clicks in the diagram, rather than polling IsoDiagram.SelectedConnector every frame.
func (*IsoDiagram) SelectedText ¶ added in v0.202.0
func (d *IsoDiagram) SelectedText() string
SelectedText returns the selected text annotation's ID, or "" when none is selected.
func (*IsoDiagram) SelectedTextObservable ¶ added in v0.202.0
func (d *IsoDiagram) SelectedTextObservable() *mvvm.Observable[string]
SelectedTextObservable exposes the text-selection property so a host can bind it into an MVVM view model (e.g. a text-properties inspector) that stays in sync with clicks in the diagram, rather than polling IsoDiagram.SelectedText every frame.
func (*IsoDiagram) SelectedZone ¶ added in v0.202.0
func (d *IsoDiagram) SelectedZone() string
SelectedZone returns the selected zone's ID, or "" when no zone is selected.
func (*IsoDiagram) SelectedZoneObservable ¶ added in v0.202.0
func (d *IsoDiagram) SelectedZoneObservable() *mvvm.Observable[string]
SelectedZoneObservable exposes the zone-selection property so a host can bind it into an MVVM view model (e.g. a zone-properties inspector) that stays in sync with clicks in the diagram, rather than polling IsoDiagram.SelectedZone every frame.
func (*IsoDiagram) Selection ¶ added in v0.202.0
func (d *IsoDiagram) Selection() []IsoEntityRef
Selection returns a snapshot of the current multi-selection, in selection order (the order entities were added).
func (*IsoDiagram) SelectionList ¶ added in v0.202.0
func (d *IsoDiagram) SelectionList() *mvvm.ObservableList[IsoEntityRef]
SelectionList exposes the authoritative multi-selection as an observable list so a host can bind it into an MVVM view model (e.g. a "3 items selected" status or a multi-object inspector) that stays in sync with clicks, the marquee and select-all, rather than polling IsoDiagram.Selection every frame.
func (*IsoDiagram) SetBounds ¶ added in v0.200.0
func (d *IsoDiagram) SetBounds(r Rect)
SetBounds positions the widget and, until the user first pans or zooms, re-centres the grid within the new bounds so the diagram is visible without any host setup.
func (*IsoDiagram) SetConnectorArrow ¶ added in v0.201.0
func (d *IsoDiagram) SetConnectorArrow(id string, a IsoArrow) bool
SetConnectorArrow sets the end heads of connector id (undoable). It returns false when the connector is absent or already has that arrow.
func (*IsoDiagram) SetConnectorColor ¶ added in v0.201.0
func (d *IsoDiagram) SetConnectorColor(id string, col RGBA) bool
SetConnectorColor sets the stroke colour of connector id (undoable); pass a zero colour (A==0) to fall back to the theme link colour. It returns false when the connector is absent or already has that colour.
func (*IsoDiagram) SetConnectorRouted ¶ added in v0.201.0
func (d *IsoDiagram) SetConnectorRouted(id string, routed bool) bool
SetConnectorRouted toggles grid-aware routing on connector id (undoable). It returns false when the connector is absent or already in that mode.
func (*IsoDiagram) SetConnectorStyle ¶ added in v0.201.0
func (d *IsoDiagram) SetConnectorStyle(id string, s IsoConnectorStyle) bool
SetConnectorStyle sets the stroke style of connector id (undoable). It returns false when the connector is absent or already has that style.
func (*IsoDiagram) SetLayerLocked ¶ added in v0.202.0
func (d *IsoDiagram) SetLayerLocked(id string, locked bool) bool
SetLayerLocked toggles a layer's lock (undoable). A locked layer still draws but its entities cannot be picked or edited. Returns false when the layer is absent or already in that state.
func (*IsoDiagram) SetLayerOrder ¶ added in v0.202.0
func (d *IsoDiagram) SetLayerOrder(id string, order int) bool
SetLayerOrder sets a layer's back-to-front rank (undoable). Returns false when the layer is absent or already at that order.
func (*IsoDiagram) SetLayerVisible ¶ added in v0.202.0
func (d *IsoDiagram) SetLayerVisible(id string, visible bool) bool
SetLayerVisible toggles a layer's visibility (undoable). Hiding a layer stops its entities from drawing and from being picked. Returns false when the layer is absent or already in that state.
func (*IsoDiagram) SetPlacementIcon ¶ added in v0.206.0
func (d *IsoDiagram) SetPlacementIcon(id string)
SetPlacementIcon arms (or, with "", disarms) the click-to-place icon — the setter counterpart of IsoDiagram.PlacementIconObservable.
func (*IsoDiagram) SetSelectedConnectorArrow ¶ added in v0.201.0
func (d *IsoDiagram) SetSelectedConnectorArrow(a IsoArrow) bool
SetSelectedConnectorArrow applies IsoDiagram.SetConnectorArrow to the currently selected connector.
func (*IsoDiagram) SetSelectedConnectorColor ¶ added in v0.201.0
func (d *IsoDiagram) SetSelectedConnectorColor(col RGBA) bool
SetSelectedConnectorColor applies IsoDiagram.SetConnectorColor to the currently selected connector.
func (*IsoDiagram) SetSelectedConnectorRouted ¶ added in v0.201.0
func (d *IsoDiagram) SetSelectedConnectorRouted(routed bool) bool
SetSelectedConnectorRouted applies IsoDiagram.SetConnectorRouted to the currently selected connector.
func (*IsoDiagram) SetSelectedConnectorStyle ¶ added in v0.201.0
func (d *IsoDiagram) SetSelectedConnectorStyle(s IsoConnectorStyle) bool
SetSelectedConnectorStyle applies IsoDiagram.SetConnectorStyle to the currently selected connector (a no-op returning false when none is selected).
func (*IsoDiagram) SetSelectedTextColor ¶ added in v0.202.0
func (d *IsoDiagram) SetSelectedTextColor(col RGBA) bool
SetSelectedTextColor applies IsoDiagram.SetTextColor to the selected annotation.
func (*IsoDiagram) SetSelectedTextContent ¶ added in v0.202.0
func (d *IsoDiagram) SetSelectedTextContent(text string) bool
SetSelectedTextContent applies IsoDiagram.SetTextContent to the selected annotation (a no-op returning false when none is selected).
func (*IsoDiagram) SetSelectedTextSize ¶ added in v0.202.0
func (d *IsoDiagram) SetSelectedTextSize(size int) bool
SetSelectedTextSize applies IsoDiagram.SetTextSize to the selected annotation.
func (*IsoDiagram) SetSelectedZoneColor ¶ added in v0.202.0
func (d *IsoDiagram) SetSelectedZoneColor(col RGBA) bool
SetSelectedZoneColor applies IsoDiagram.SetZoneColor to the selected zone (a no-op returning false when none is selected).
func (*IsoDiagram) SetSelectedZoneLabel ¶ added in v0.202.0
func (d *IsoDiagram) SetSelectedZoneLabel(label string) bool
SetSelectedZoneLabel applies IsoDiagram.SetZoneLabel to the selected zone.
func (*IsoDiagram) SetSelectionColor ¶ added in v0.202.0
func (d *IsoDiagram) SetSelectionColor(col RGBA) bool
SetSelectionColor recolours every selected entity — nodes, connectors, zones and texts alike — to col as ONE undoable command. A zero colour (A==0) falls each entity back to its theme default. It returns false (no undo entry) when nothing is selected or no entity's colour actually changed.
func (*IsoDiagram) SetTextColor ¶ added in v0.202.0
func (d *IsoDiagram) SetTextColor(id string, col RGBA) bool
SetTextColor sets an annotation's ink (undoable); pass a zero colour (A==0) to fall back to the theme OnSurface. Returns false when the annotation is absent or already has that colour.
func (*IsoDiagram) SetTextContent ¶ added in v0.202.0
func (d *IsoDiagram) SetTextContent(id, text string) bool
SetTextContent sets an annotation's caption (undoable). Returns false when the annotation is absent or already carries that text.
func (*IsoDiagram) SetTextPos ¶ added in v0.202.0
func (d *IsoDiagram) SetTextPos(id string, x, y int) bool
SetTextPos sets an annotation's anchor cell (undoable). Returns false when the annotation is absent or already at that cell.
func (*IsoDiagram) SetTextSize ¶ added in v0.202.0
func (d *IsoDiagram) SetTextSize(id string, size int) bool
SetTextSize sets an annotation's type scale (undoable); zero uses the widget's effective font. Returns false when the annotation is absent or already that size.
func (*IsoDiagram) SetViewRotation ¶ added in v0.217.0
func (d *IsoDiagram) SetViewRotation(q int)
SetViewRotation turns the view to quarter-turn q, taken modulo 4 (so any integer, negative included, names one of the four orientations). It re-orients the whole rendered plane — grid, nodes, connectors, zones and texts — and recomputes the depth-sort for the new orientation; it records no undo entry and changes nothing in the document. A redundant set (same normalised quarter) neither notifies nor repaints.
func (*IsoDiagram) SetZoneColor ¶ added in v0.202.0
func (d *IsoDiagram) SetZoneColor(id string, col RGBA) bool
SetZoneColor sets a zone's fill colour (undoable); pass a zero colour (A==0) to fall back to the translucent theme accent. Returns false when the zone is absent or already has that colour.
func (*IsoDiagram) SetZoneLabel ¶ added in v0.202.0
func (d *IsoDiagram) SetZoneLabel(id, label string) bool
SetZoneLabel sets a zone's caption (undoable). Returns false when the zone is absent or already carries that label.
func (*IsoDiagram) SetZoneRect ¶ added in v0.202.0
func (d *IsoDiagram) SetZoneRect(id string, x, y, w, h int) bool
SetZoneRect sets a zone's rectangle (undoable), clamping the size to at least 1x1. Returns false when the zone is absent or already occupies that rectangle.
func (*IsoDiagram) ViewRotation ¶ added in v0.217.0
func (d *IsoDiagram) ViewRotation() int
ViewRotation returns the current view rotation in quarter-turns (0..3). 0 is the unrotated view; 1, 2 and 3 are successive 90° clockwise turns of the plane.
func (*IsoDiagram) ViewRotationObservable ¶ added in v0.217.0
func (d *IsoDiagram) ViewRotationObservable() *mvvm.Observable[int]
ViewRotationObservable exposes the view-rotation property so a host can bind it into an MVVM view model (e.g. a "rotate view" toolbar) that stays in sync with the diagram, rather than polling IsoDiagram.ViewRotation every frame. Setting it is equivalent to IsoDiagram.SetViewRotation; the stored value is always normalised to 0..3.
func (*IsoDiagram) ZoomAt ¶ added in v0.200.0
func (d *IsoDiagram) ZoomAt(factor float64, cx, cy int)
ZoomAt multiplies the tile size by factor, keeping the world point currently under widget-local (cx, cy) fixed on screen, and clamps the tile width to [IsoMinTile, IsoMaxTile].
type IsoDoc ¶ added in v0.200.0
type IsoDoc struct {
// contains filtered or unexported fields
}
IsoDoc is the default in-memory IsoDocument, backed by two go-widgets/mvvm ObservableLists (one for nodes, one for connectors). Because every edit flows through an Observable, the widget binds to the document once and repaints on change instead of copying node fields every frame — the MVVM discipline the toolkit follows — and a host can bind the same lists into its own view models. Swapping this for a CRDT-backed store means implementing IsoDocument elsewhere; nothing else changes.
func UnmarshalIsoDocument ¶ added in v0.202.0
UnmarshalIsoDocument parses a native JSON document (see MarshalIsoDocument) into a fresh IsoDoc. It validates the schema version, every id and every connector endpoint (see [decodeIsoSnapshot]); on any error it returns nil and the error, leaving no half-built document. The result is structurally equal to the document that produced the JSON, so Marshal → Unmarshal → Marshal is byte-identical.
func (*IsoDoc) ConnectorList ¶ added in v0.200.0
func (d *IsoDoc) ConnectorList() *mvvm.ObservableList[IsoConnector]
ConnectorList exposes the underlying observable connector collection (see IsoDoc.NodeList).
func (*IsoDoc) Connectors ¶ added in v0.200.0
func (d *IsoDoc) Connectors() []IsoConnector
Connectors returns a snapshot copy of every connector.
func (*IsoDoc) Layer ¶ added in v0.202.0
Layer returns the layer with the given ID and whether it exists.
func (*IsoDoc) LayerList ¶ added in v0.202.0
func (d *IsoDoc) LayerList() *mvvm.ObservableList[IsoLayer]
LayerList exposes the underlying observable layer collection (see IsoDoc.NodeList).
func (*IsoDoc) Node ¶ added in v0.200.0
Node returns the node with the given ID and whether it exists.
func (*IsoDoc) NodeList ¶ added in v0.200.0
func (d *IsoDoc) NodeList() *mvvm.ObservableList[IsoNode]
NodeList exposes the underlying observable node collection so a host can bind it into an MVVM view (e.g. a palette or an outline list) that stays in sync with the diagram. Mutating it directly is equivalent to the Put/Remove methods.
func (*IsoDoc) PutConnector ¶ added in v0.200.0
func (d *IsoDoc) PutConnector(c IsoConnector)
PutConnector upserts c by its ID.
func (*IsoDoc) RemoveConnector ¶ added in v0.200.0
RemoveConnector deletes the connector with id.
func (*IsoDoc) RemoveLayer ¶ added in v0.202.0
RemoveLayer deletes the layer with id. Entities still naming it are left untouched and fall back to the implicit default layer at draw time.
func (*IsoDoc) RemoveNode ¶ added in v0.200.0
RemoveNode deletes the node with id and every connector attached to it.
func (*IsoDoc) RemoveText ¶ added in v0.202.0
RemoveText deletes the text annotation with id.
func (*IsoDoc) RemoveZone ¶ added in v0.202.0
RemoveZone deletes the zone with id.
func (*IsoDoc) Subscribe ¶ added in v0.200.0
func (d *IsoDoc) Subscribe(fn func()) (unsubscribe func())
Subscribe runs fn after any node, connector, zone or text edit and returns an unsubscribe function. It fans out to every observable list so a single subscription covers the whole document.
func (*IsoDoc) Text ¶ added in v0.202.0
Text returns the text annotation with the given ID and whether it exists.
func (*IsoDoc) TextList ¶ added in v0.202.0
func (d *IsoDoc) TextList() *mvvm.ObservableList[IsoText]
TextList exposes the underlying observable text-annotation collection (see IsoDoc.NodeList).
func (*IsoDoc) Zone ¶ added in v0.202.0
Zone returns the zone with the given ID and whether it exists.
func (*IsoDoc) ZoneList ¶ added in v0.202.0
func (d *IsoDoc) ZoneList() *mvvm.ObservableList[IsoZone]
ZoneList exposes the underlying observable zone collection (see IsoDoc.NodeList).
type IsoDocument ¶ added in v0.200.0
type IsoDocument interface {
// Nodes returns a snapshot copy of every node, in insertion order.
Nodes() []IsoNode
// Connectors returns a snapshot copy of every connector, in insertion order.
Connectors() []IsoConnector
// Zones returns a snapshot copy of every zone, in insertion order.
Zones() []IsoZone
// Texts returns a snapshot copy of every text annotation, in insertion order.
Texts() []IsoText
// Node returns the node with the given ID and whether it was found.
Node(id string) (IsoNode, bool)
// Zone returns the zone with the given ID and whether it was found.
Zone(id string) (IsoZone, bool)
// Text returns the text annotation with the given ID and whether it was found.
Text(id string) (IsoText, bool)
// PutNode inserts a node or, when one already has the same ID, replaces it
// (an OR-map upsert with last-writer-wins fields).
PutNode(n IsoNode)
// RemoveNode deletes the node with the given ID and every connector that
// touches it. A missing ID is a no-op.
RemoveNode(id string)
// PutConnector inserts or replaces a connector by ID.
PutConnector(c IsoConnector)
// RemoveConnector deletes a connector by ID; a missing ID is a no-op.
RemoveConnector(id string)
// PutZone inserts or replaces a zone by ID.
PutZone(z IsoZone)
// RemoveZone deletes a zone by ID; a missing ID is a no-op.
RemoveZone(id string)
// PutText inserts or replaces a text annotation by ID.
PutText(t IsoText)
// RemoveText deletes a text annotation by ID; a missing ID is a no-op.
RemoveText(id string)
// Layers returns a snapshot copy of every layer, in insertion order.
Layers() []IsoLayer
// Layer returns the layer with the given ID and whether it was found.
Layer(id string) (IsoLayer, bool)
// PutLayer inserts or replaces a layer by ID.
PutLayer(l IsoLayer)
// RemoveLayer deletes the layer with the given ID; a missing ID is a no-op.
// Entities still naming a removed layer fall back to the implicit default.
RemoveLayer(id string)
// Subscribe registers fn to run after every mutation and returns a function
// that unsubscribes it.
Subscribe(fn func()) (unsubscribe func())
}
IsoDocument is the backing store an IsoDiagram edits. It is deliberately a small interface over "sets of nodes, connectors, zones and text annotations, each keyed by ID, with change notification" so the widget never reaches into a concrete store. The bundled IsoDoc backs it with go-widgets/mvvm observable lists; a structured-collab CRDT (each entity set an OR-map with LWW fields) that implements this same interface is a drop-in replacement — the widget compiles unchanged against either.
type IsoEntityKind ¶ added in v0.202.0
type IsoEntityKind int
IsoEntityKind names which family an IsoEntityRef points at. It replaces the four separate mono-selection channels' implicit "kind" with one explicit tag so a single selection set can hold nodes, connectors, zones and texts mixed.
const ( // IsoEntityNode references an [IsoNode]. IsoEntityNode IsoEntityKind = iota // IsoEntityConnector references an [IsoConnector]. IsoEntityConnector // IsoEntityZone references an [IsoZone]. IsoEntityZone // IsoEntityText references an [IsoText] annotation. IsoEntityText )
type IsoEntityRef ¶ added in v0.202.0
type IsoEntityRef struct {
// Kind is the entity family the id belongs to.
Kind IsoEntityKind
// ID is the entity's stable id within its family.
ID string
}
IsoEntityRef identifies one selected entity: its family and its id. It is a comparable value, so it is a natural element of an mvvm.ObservableList and a map key.
type IsoIcon ¶ added in v0.200.0
type IsoIcon interface {
// Render returns the drawing for a node whose 1x1 footprint's near corner is
// grid cell (x, y), coloured from the node's resolved base colour base (the
// same colour the plain-shape fallback would shade its faces from). The
// implementation must not retain x, y or base.
Render(x, y int, base stdcolor.RGBA) IsoIconDrawing
}
IsoIcon renders one placeable component of an isometric diagram at a grid cell. It is the extension point of the icon library: a host registers custom icons (whole component packs) so an IsoNode can name one by id. An icon is a value evaluated at draw time, so it must be safe to call IsoIcon.Render concurrently for read.
var IsoFallbackIcon IsoIcon = IsoPrimitiveIcon{Build: isoBoxShapes}
IsoFallbackIcon is the icon an IsoIconRegistry resolves an unknown (or empty) id to: a plain unit cube shaded from the node's base colour — identical to the widget's original bare-shape node. It is exported so a host can reuse it or substitute its own default.
type IsoIconDrawing ¶ added in v0.200.0
type IsoIconDrawing struct {
// Shapes are the isometric solids (from github.com/go-gfx/gfx/iso) that draw
// this icon; they are added to the widget's [iso.Scene] and depth-sorted with
// every other node and connector so overlap is correct.
Shapes []iso.Shape
// Sprite, when non-nil, is a straight-alpha raster image blitted (billboarded,
// nearest-neighbour scaled) into the node's cell rectangle after the scene is
// rendered. This is how an external icon pack — arbitrary isometric PNG art —
// is placed without any Go drawing code.
Sprite *raster.Image
}
IsoIconDrawing is what an IsoIcon contributes for one node placement: a set of depth-sortable isometric primitives to composite into the diagram's scene and/or a billboarded sprite image to blit at the node's cell. A primitive-composed icon fills Shapes; a sprite/image icon fills Sprite; a hybrid may fill both (the shapes draw in the depth-sorted scene, the sprite blits over the rendered scene at the cell).
type IsoIconPack ¶ added in v0.200.0
type IsoIconPack struct {
// Name is the pack's namespace prefix; empty registers the icons bare.
Name string
// Icons maps a within-pack key to the icon registered for it.
Icons map[string]IsoIcon
}
IsoIconPack is a named set of icons registered together — a component library (e.g. an "aws" or "network" pack). Registering the pack adds each icon under the id "<Name>/<key>" when Name is non-empty (so packs from different sources never collide), or under the bare key when Name is empty.
type IsoIconPalette ¶ added in v0.206.0
type IsoIconPalette struct {
Base
// Title is the header caption. A blank Title falls back to a generic default.
Title string
// DefaultGroupName is the heading for bare (un-namespaced) icon ids. A blank
// value falls back to a generic default.
DefaultGroupName string
// OnPickIcon fires when the selected (armed) icon changes — with the new
// registry id, or "" when the selection is cleared. Nil-guarded. It is the
// click-to-place hook a host wires to [IsoDiagram.SetPlacementIcon] when it
// does not route the drag-and-drop payload itself.
OnPickIcon func(id string)
// OnInvalidate, when set, is called whenever the panel's appearance changed
// and it should be redrawn.
OnInvalidate func()
// contains filtered or unexported fields
}
IsoIconPalette is a draggable, collapsible panel that lists an IsoIconRegistry's icons grouped by pack, each row a small isometric thumbnail (rendered through IsoIcon.Render) beside its key. It is the component drawer for an IsoDiagram: a user picks an icon here and drops it on the canvas (the palette is a DragSource; the diagram a DropTarget), or — where a back-end has no inter-widget drag — clicks an icon to arm it and taps the canvas to place it (bind IsoIconPalette.SelectedIcon to IsoDiagram.PlacementIconObservable, or wire OnPickIcon).
The panel repositions itself when its header is dragged and collapses to just the header on a click of the header's toggle; both the position (IsoIconPalette.Origin) and the collapsed flag (IsoIconPalette.Collapsed) are mvvm.Observables so that cross-boundary state is observed, not polled. The icon list scrolls (reusing the toolkit's shared scrollbar machinery) when it overflows the body.
func NewIsoIconPalette ¶ added in v0.206.0
func NewIsoIconPalette(reg *IsoIconRegistry) *IsoIconPalette
NewIsoIconPalette builds a palette over reg (nil uses the package-global IsoDefaultIcons). Nothing is selected, the panel is expanded, and its origin tracks whatever IsoIconPalette.SetBounds the host gives it.
func (*IsoIconPalette) A11y ¶ added in v0.206.0
func (p *IsoIconPalette) A11y() A11yInfo
A11y reports the palette as a list, with the armed icon id as its value so a screen reader announces the current pick.
func (*IsoIconPalette) Collapsed ¶ added in v0.206.0
func (p *IsoIconPalette) Collapsed() *mvvm.Observable[bool]
Collapsed exposes the collapsed flag so a host can observe or drive the fold/unfold. true hides the icon list, leaving only the header.
func (*IsoIconPalette) DragData ¶ added in v0.206.0
func (p *IsoIconPalette) DragData() string
DragData makes the palette a DragSource: it returns the armed icon's drag payload, or "" when nothing is selected (so a drag that began on no icon carries nothing).
func (*IsoIconPalette) Draw ¶ added in v0.206.0
func (p *IsoIconPalette) Draw(pt painter.Painter, theme *Theme)
Draw paints the panel: a rounded surface with a header band (title + fold chevron) and, when expanded, the grouped icon list with its scrollbar. Each icon row renders its isometric thumbnail into a small buffer blitted at the row, with the selected row tinted.
func (*IsoIconPalette) Entries ¶ added in v0.206.0
func (p *IsoIconPalette) Entries() []IsoPaletteEntry
Entries returns every listed entry, flattened in the same order the groups lay out.
func (*IsoIconPalette) Groups ¶ added in v0.206.0
func (p *IsoIconPalette) Groups() []IsoPaletteGroup
Groups returns the registry's icons grouped by pack, deterministically ordered: the bare-id group first, then packs alphabetically, and within each group the entries by key. It is what the panel draws and what a test asserts against — the palette lists exactly the registry's ids, by pack.
func (*IsoIconPalette) OnEvent ¶ added in v0.206.0
func (p *IsoIconPalette) OnEvent(ev Event)
OnEvent drives the panel: a header press begins a move or toggles the fold, a body press selects an icon or grabs the scrollbar, drags advance the move or the thumb, the wheel scrolls the list. A disabled panel ignores everything.
func (*IsoIconPalette) Origin ¶ added in v0.206.0
func (p *IsoIconPalette) Origin() *mvvm.Observable[IsoPalettePos]
Origin exposes the panel's top-left position so a host can observe (persist) or drive it; the panel Sets it when its header is dragged.
func (*IsoIconPalette) Registry ¶ added in v0.206.0
func (p *IsoIconPalette) Registry() *IsoIconRegistry
Registry returns the icon registry the palette lists.
func (*IsoIconPalette) SelectIcon ¶ added in v0.206.0
func (p *IsoIconPalette) SelectIcon(id string)
SelectIcon selects (arms) the icon with id, or clears the selection when id is "". It is the programmatic counterpart of clicking a row.
func (*IsoIconPalette) SelectedIcon ¶ added in v0.206.0
func (p *IsoIconPalette) SelectedIcon() *mvvm.Observable[string]
SelectedIcon exposes the armed icon id so a host binds it into an IsoDiagram.PlacementIconObservable (or its own view model) instead of polling. "" means nothing is selected.
func (*IsoIconPalette) SetBounds ¶ added in v0.206.0
func (p *IsoIconPalette) SetBounds(r Rect)
SetBounds positions and sizes the panel and mirrors the position into the Origin observable (a no-op emit when unchanged), so binding Origin and calling SetBounds stay consistent.
func (*IsoIconPalette) SetCollapsed ¶ added in v0.206.0
func (p *IsoIconPalette) SetCollapsed(v bool)
SetCollapsed folds (true) or unfolds (false) the panel.
func (*IsoIconPalette) Toggle ¶ added in v0.206.0
func (p *IsoIconPalette) Toggle()
Toggle flips the collapsed state.
type IsoIconRegistry ¶ added in v0.200.0
type IsoIconRegistry struct {
// contains filtered or unexported fields
}
IsoIconRegistry maps a string icon-id to an IsoIcon. The package keeps one default registry (IsoDefaultIcons, seeded with the built-in architecture icons); an IsoDiagram uses that unless its own Icons field overrides it. The registry is safe for concurrent Register/Resolve.
func IsoDefaultIcons ¶ added in v0.200.0
func IsoDefaultIcons() *IsoIconRegistry
IsoDefaultIcons returns the package-global icon registry. RegisterIcon and RegisterIconPack mutate it, and an IsoDiagram whose own Icons field is nil draws through it.
func NewIsoIconRegistry ¶ added in v0.200.0
func NewIsoIconRegistry() *IsoIconRegistry
NewIsoIconRegistry returns an empty registry. Unknown ids resolve to IsoFallbackIcon until something is registered.
func (*IsoIconRegistry) IDs ¶ added in v0.200.0
func (r *IsoIconRegistry) IDs() []string
IDs returns the registered ids in no particular order — for building a palette or listing an installed library.
func (*IsoIconRegistry) Register ¶ added in v0.200.0
func (r *IsoIconRegistry) Register(id string, icon IsoIcon)
Register adds (or replaces) the icon stored under id.
func (*IsoIconRegistry) RegisterPack ¶ added in v0.200.0
func (r *IsoIconRegistry) RegisterPack(p IsoIconPack)
RegisterPack registers every icon in p (see IsoIconPack for the id scheme).
func (*IsoIconRegistry) Resolve ¶ added in v0.200.0
func (r *IsoIconRegistry) Resolve(id string) (IsoIcon, bool)
Resolve returns the icon registered under id and true, or IsoFallbackIcon and false when id is unknown (including the empty id). The always-usable icon it returns lets a caller draw without branching on the boolean.
type IsoLayer ¶ added in v0.202.0
type IsoLayer struct {
// ID is the layer's stable identity — the id an entity's Layer field
// references and the OR-map key. A document must not hold two layers with
// the same ID; [IsoDoc.PutLayer] upserts on it. The empty ID is reserved
// for the implicit default layer.
ID string
// Name is a human-readable caption for a layers panel; it never affects
// rendering.
Name string
// Visible reports whether the layer's entities draw. A non-visible layer is
// skipped entirely at draw time.
Visible bool
// Locked reports whether the layer's entities are frozen: a locked layer
// still draws but its entities cannot be picked, moved, recoloured or
// deleted through the widget.
Locked bool
// Order is the layer's back-to-front rank: lower draws further back, higher
// draws in front. Ties (two layers sharing an Order) keep document order.
Order int
}
IsoLayer is a named drawing plane that groups entities for z-ordering, visibility and locking — the "layers" of a drawing editor. Every IsoNode, IsoConnector, IsoZone and IsoText carries a Layer id naming the layer it lives on; the empty id is the implicit default layer (order 0, visible, unlocked), so a document that never creates a layer renders and edits byte-for-byte as it did before layers existed.
Layers compose back-to-front by Order: a lower Order draws first (further back), a higher Order draws later (in front), and WITHIN one layer the existing isometric depth-sort is untouched — so an entity on a higher-Order layer always draws over one on a lower-Order layer, whatever their isometric depth. A layer whose Visible is false is not rendered at all; a layer whose Locked is true is rendered but cannot be selected or edited. Like the other entities it is a plain value, so the layer set snapshots cheaply for undo and mirrors into a CRDT OR-map keyed by ID.
type IsoMode ¶ added in v0.200.0
type IsoMode int
IsoMode selects what a left-drag on a node does.
const ( // IsoModeSelect is the default: click selects a node / connector / zone / text, // drag moves the pressed node, zone or text (or resizes the selected zone by a // corner handle), and drag on empty ground pans the view. IsoModeSelect IsoMode = iota // IsoModeConnect turns a node drag into a connector gesture: press one node, // release on another, and a connector is created between them. IsoModeConnect // IsoModeZone turns a ground drag into a zone-creation gesture: press one // ground cell, drag to another and release to create a rectangular zone // spanning the two cells. IsoModeZone // IsoModeText turns a ground tap into a text-annotation gesture: tap a ground // cell to drop a new (empty-captioned) text annotation there, selected and // ready for [IsoDiagram.SetSelectedTextContent]. IsoModeText )
type IsoNode ¶ added in v0.200.0
type IsoNode struct {
// ID is the node's stable identity — the key an OR-map / LWW store uses. A
// document must not hold two nodes with the same ID; [IsoDoc.PutNode]
// upserts on it.
ID string
// X, Y is the node's grid cell (integer world coordinates); the node's
// footprint is the unit square [X,X+1] x [Y,Y+1].
X, Y int
// Shape selects the rendered solid (cube / box / pyramid). It is the fallback
// geometry: it draws only when Icon is empty.
Shape IsoShape
// Icon, when non-empty, names an [IsoIcon] in the diagram's registry
// ([IsoDiagram.Icons], else [IsoDefaultIcons]) that renders this node instead
// of the bare Shape. An unknown id falls back to a plain cube. The field is a
// plain string on the value node, so it snapshots for undo and flows through
// the observable document like every other field — no per-frame copy.
Icon string
// Label is the caption drawn above the node and announced to a screen
// reader.
Label string
// Color is the node's base colour; a zero value (A==0) inherits the theme
// accent at draw time so a node placed without a colour is still visible.
Color RGBA
// Layer names the [IsoLayer] this node belongs to (see the type doc on
// IsoLayer for the ordering / visibility / lock semantics). The zero value
// (the empty string) is the implicit default layer — order 0, visible,
// unlocked — so a node placed without ever touching layers renders and
// selects exactly as it did before layers existed. The field is a plain
// string on the value node, so it snapshots for undo and mirrors into a
// CRDT like every other field.
Layer string
}
IsoNode is one placed element of an isometric diagram: an identity, a grid cell it occupies, the solid it draws as, a label and a base colour the isometric renderer shades per face. It is a plain value so the whole document is cheap to snapshot (undo) and to mirror into a CRDT OR-map keyed by ID.
type IsoPaletteEntry ¶ added in v0.206.0
type IsoPaletteEntry struct {
// ID is the registry id ("aws/ec2", or a bare "server").
ID string
// Pack is the id's namespace (the part before "/"), or "" for a bare id.
Pack string
// Key is the within-pack key (the part after "/", or the whole bare id) — the
// entry's label.
Key string
// Icon is the resolved icon the entry's thumbnail draws.
Icon IsoIcon
}
IsoPaletteEntry is one listed icon of an IsoIconPalette: its registry id, the pack it belongs to (the id's prefix before "/", or "" for a bare id), the within-pack key that labels it, and the resolved IsoIcon the thumbnail renders.
type IsoPaletteGroup ¶ added in v0.206.0
type IsoPaletteGroup struct {
Name string
Entries []IsoPaletteEntry
}
IsoPaletteGroup is one pack's worth of IsoPaletteEntry under a heading, as an IsoIconPalette lays its list out. Name is the pack's display heading (a bare id's group takes the palette's DefaultGroupName).
type IsoPalettePos ¶ added in v0.206.0
type IsoPalettePos struct{ X, Y int }
IsoPalettePos is an IsoIconPalette's top-left position on its surface. It is a comparable value so it rides in an mvvm.Observable (IsoIconPalette.Origin) the host can bind, observe and persist — the MVVM way the panel's position crosses the widget/host boundary while the panel repositions itself on a header drag.
type IsoPrimitiveIcon ¶ added in v0.200.0
type IsoPrimitiveIcon struct {
// Build composes the icon's solids at grid cell (x, y) from base colour base.
Build func(x, y int, base stdcolor.RGBA) []iso.Shape
}
IsoPrimitiveIcon is an IsoIcon composed from isometric primitives — the pure-Go, code-authored kind. Build returns the [iso.Shape]s (cubes, bricks, pyramids, slopes, lines, …) that draw the icon at cell (x, y) shaded from base. Authoring an icon is therefore just writing that function; the bundled architecture icons (IsoDefaultIcons) are exactly this.
func (IsoPrimitiveIcon) Render ¶ added in v0.200.0
func (i IsoPrimitiveIcon) Render(x, y int, base stdcolor.RGBA) IsoIconDrawing
Render satisfies IsoIcon by returning the built shapes as the drawing.
type IsoProceduralAnimIcon ¶ added in v0.219.0
type IsoProceduralAnimIcon struct {
// Frame composes the icon at grid cell (x, y) from base colour base for the
// wrapped phase in [0, 1).
Frame func(x, y int, base stdcolor.RGBA, phase float64) IsoIconDrawing
}
IsoProceduralAnimIcon is the pure-Go, code-authored IsoAnimatedIcon: a single Frame closure composes the icon's [iso.Shape]s (and/or sprite) at a phase. It is to IsoAnimatedIcon what IsoPrimitiveIcon is to IsoIcon. The phase handed to Frame is always in [0, 1) (RenderAt wraps it first), so a frame author never has to fold the cycle themselves, and Render is Frame at phase 0.
func (IsoProceduralAnimIcon) Render ¶ added in v0.219.0
func (i IsoProceduralAnimIcon) Render(x, y int, base stdcolor.RGBA) IsoIconDrawing
Render satisfies IsoIcon by rendering the rest frame (phase 0), so a procedural animated icon that is never driven draws exactly like a still.
func (IsoProceduralAnimIcon) RenderAt ¶ added in v0.219.0
func (i IsoProceduralAnimIcon) RenderAt(x, y int, base stdcolor.RGBA, phase float64) IsoIconDrawing
RenderAt satisfies IsoAnimatedIcon: it folds phase into [0, 1) and calls Frame. RenderAt(...,phase) and RenderAt(...,phase+k) for any integer k return the same drawing.
type IsoShape ¶ added in v0.200.0
type IsoShape int
IsoShape names the solid an IsoNode renders as on the isometric grid.
const ( // IsoCube is a unit cube one grid cell on every side — the default node. IsoCube IsoShape = iota // IsoBox is a 1x1 footprint raised twice as tall as a cube, for a node that // should read as a stack / server / tower rather than a single block. IsoBox // IsoPyramid is a 1x1 footprint tapering to an apex one unit up. IsoPyramid )
type IsoSpriteIcon ¶ added in v0.200.0
type IsoSpriteIcon struct {
// Img is the sprite blitted at the cell. A nil Img draws nothing.
Img *raster.Image
}
IsoSpriteIcon is an IsoIcon that blits a fixed raster image — an isometric sprite from an external icon pack — billboarded at the node's cell. Img is the straight-alpha art (transparent pixels show the grid through). The node's base colour does not tint the sprite; the art carries its own colours.
func (IsoSpriteIcon) Render ¶ added in v0.200.0
func (i IsoSpriteIcon) Render(x, y int, base stdcolor.RGBA) IsoIconDrawing
Render satisfies IsoIcon by returning the sprite as the drawing.
type IsoText ¶ added in v0.202.0
type IsoText struct {
// ID is the annotation's stable identity (the OR-map key).
ID string
// X, Y is the annotation's anchor tile (integer world coordinates); the text
// is centred on that cell's projected ground centre.
X, Y int
// Text is the string drawn. An empty string still selects and moves as an
// (invisible) annotation, so an editor can place one and fill it in later.
Text string
// Color is the ink; a zero value (A==0) inherits the theme's OnSurface colour
// at draw time so an uncoloured annotation is still legible and theme-aware.
Color RGBA
// Size is the integer type scale routed through the toolkit HiDPI/density
// scale (1 is the base bitmap font, 2 double-height, ...). Zero uses the
// widget's effective font, so an annotation placed without a size matches the
// rest of the chrome.
Size int
// Layer names the [IsoLayer] this annotation belongs to; the zero value (the
// empty string) is the implicit default layer, so an unlayered annotation
// behaves exactly as before. See [IsoLayer].
Layer string
}
IsoText is a standalone text annotation anchored at a tile position on the ground plane. It is not attached to any node; it floats on the topmost layer, above every node, connector and zone. Like the other entities it is a plain value for cheap snapshotting and CRDT mirroring.
type IsoZone ¶ added in v0.202.0
type IsoZone struct {
// ID is the zone's stable identity — the OR-map key. A document must not hold
// two zones with the same ID; [IsoDoc.PutZone] upserts on it.
ID string
// X, Y is the zone's minimum corner in grid cells (integer world
// coordinates).
X, Y int
// W, H is the zone's size in grid cells. The widget's editing commands keep
// both at least 1, so a zone always covers a non-empty rectangle.
W, H int
// Color is the zone's fill colour; its alpha is the fill opacity, so a
// semi-transparent value tints the ground without hiding the grid. A zero
// value (A==0) inherits a translucent theme accent at draw time, so a zone
// created without a colour is still visible and follows the theme.
Color RGBA
// Label is an optional caption drawn in a corner of the zone and announced to
// a screen reader.
Label string
// Layer names the [IsoLayer] this zone belongs to; the zero value (the empty
// string) is the implicit default layer, so an unlayered zone behaves exactly
// as before. See [IsoLayer].
Layer string
}
IsoZone is a rectangular coloured region on the ground plane (z=0) that groups nodes visually. It occupies the tile rectangle [X, X+W] x [Y, Y+H]: (X, Y) is its minimum corner in grid cells and (W, H) is its size in cells. Like every other entity it is a plain value so the whole document snapshots cheaply for undo and mirrors into a CRDT OR-map keyed by ID.
type Item ¶ added in v0.59.0
Item wraps a widget with its per-layout configuration — the analog of an Ext child component's layout config. Box layouts read Flex/Size (a positive Flex is a proportional weight; else a positive Size is a fixed main-axis extent; both zero means an equal flex share, like HBox.Append). Border layouts read Region (with Size the edge band's thickness). Fit/Card ignore the config.
type Kanban ¶ added in v0.82.0
type Kanban struct {
Base
// Columns are the board's lists, left to right.
Columns []KanbanColumn
// OnCardClick fires when a card is clicked, with the 0-indexed column
// and card. Nil is safe (the click still updates the selection).
OnCardClick func(col, card int)
// OnCardMove fires when a drag drops a card at a new position, with the
// source (fromCol, fromCard) and the destination (toCol, toIdx) the card
// now occupies. Nil is safe -- the board still updates Columns in place.
OnCardMove func(fromCol, fromCard, toCol, toIdx int)
// contains filtered or unexported fields
}
Kanban renders a Trello-style board: a row of equal-width columns (lists) laid side by side across the widget's Bounds, each a titled header band above a vertical stack of rounded cards. It is the missing "workflow board" primitive next to Table (a data grid) and ListBox (a single column of items) -- Kanban is many columns, each a stack of two-line cards, the shape a to-do / progress board needs.
Visual (per column):
+-----------------------+ | Title (n) | <- KanbanHeaderH, SurfaceAlt, count Badge +-----------------------+ | | Card title | <- KanbanCardH, Surface, accent stripe | | muted subtitle | +-----------------------+ | | Card title | | | muted subtitle | +-----------------------+
Columns share the horizontal budget equally (like a fixed HBox), separated by KanbanColGap. Each card carries a left accent stripe -- KanbanCard.Accent, or Theme.Accent when that is the zero value -- so a board can colour-code cards by category without the host hand-drawing anything. The selected card (SelectedCol/SelectedCard) paints on an accent-tinted fill with an accent border.
A column whose cards overflow its height clips to its body (via painter.Clipper, the same graceful degradation Table relies on) AND scrolls independently: the wheel over a column shifts that column's card stack (colScroll) so cards past the fold are reachable, each column clipped to its own window. Clicking a card selects it and fires OnCardClick; clicks on headers, gaps or dead space are no-ops.
func NewKanban ¶ added in v0.82.0
func NewKanban(cols []KanbanColumn) *Kanban
NewKanban builds a Kanban from cols with no card selected (SelectedCol / SelectedCard both -1), mirroring how NewTable seeds Selected to -1.
func (*Kanban) A11y ¶ added in v0.105.0
A11y reports the Kanban board as a group carrying the selected card's title, or "" when nothing is selected (the -1/-1 sentinel or an out-of-range pair), mirroring how Draw collapses a stale selection.
func (*Kanban) CardAt ¶ added in v0.84.0
CardAt maps widget-local (x, y) to the (column, card) it lands on, or (-1, -1) for a header, gap or dead space. Exposed so a host can hit-test a right-click and build a context menu for the card under the cursor.
func (*Kanban) Draw ¶ added in v0.82.0
Draw paints every column: a SurfaceAlt panel + header band with the title and a count Badge, a Border divider, then the column's cards clipped to the column body. The selected card (if any) paints on an accent-tinted fill with an accent border; every other card on Surface with a Border stroke and its left accent stripe.
func (*Kanban) MoveCard ¶ added in v0.84.0
MoveCard removes the card at (fromCol, fromCard) and re-inserts it at toIdx in toCol, updating Selected* to its landing spot (out-of-range sources are ignored; toIdx is clamped). Exposed so a host can drive the same move a drag performs from a menu action.
func (*Kanban) OnEvent ¶ added in v0.82.0
OnEvent drives selection and card drag-and-drop. On EventClick it grabs the card under the pointer (selecting it and firing OnCardClick); on EventMouseDrag it tracks the pointer and marks the gesture a drag; on EventMouseUp it drops the grabbed card at the target column/slot, mutating Columns and firing OnCardMove. A press-release with no intervening drag leaves the board a plain click. Both callbacks are nil-safe.
func (*Kanban) SelectedCard ¶ added in v0.82.0
func (k *Kanban) SelectedCard() *mvvm.Observable[int]
SelectedCard is the highlighted card's index within its column as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable SelectedCard field. A card click Sets it (paired with SelectedCol); subscribers are notified. A bare &Kanban{} lazily seeds 0 on first access, matching the old zero-value field.
func (*Kanban) SelectedCol ¶ added in v0.82.0
func (k *Kanban) SelectedCol() *mvvm.Observable[int]
SelectedCol is the highlighted card's column index as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable SelectedCol field. A card click Sets it (paired with SelectedCard); subscribers are notified. A bare &Kanban{} lazily seeds 0 on first access, matching the old zero-value field.
type KanbanCard ¶ added in v0.82.0
KanbanCard is one card in a column: a bold Title over a muted Subtitle, with a left accent stripe drawn in Accent -- or Theme.Accent when Accent is the zero RGBA (A==0), the same "unset falls back to the theme" convention Badge.Fill uses. Either text may be "" to omit that line.
type KanbanColumn ¶ added in v0.82.0
type KanbanColumn struct {
Title string
Cards []KanbanCard
}
KanbanColumn is one list: a header Title above its stack of Cards.
type Kbd ¶ added in v0.7.0
Kbd renders a keyboard-shortcut hint like the "⌘K" chip beside a menu item: a small bordered box with the key text centred inside. Uses Theme.Surface for the face + Theme.Border for the stroke so the chip reads as a raised inlay against the parent panel.
Kbd is passive (no OnEvent handling); the caller sets Bounds to position it — a Kbd typically lives to the right of a menu label, vertically centred with the menu row. Nothing about the widget depends on the input actually being pressed; it's a purely visual mnemonic.
func NewKbd ¶ added in v0.7.0
NewKbd constructs a Kbd carrying the given key text. Callers set Bounds via SetBounds before Draw; a natural fit is {W: TextWidth(keys) + 2*KbdPadX, H: GlyphHeight() + 2*KbdPadY}.
type Keymap ¶ added in v0.151.0
type Keymap struct {
// OnChange, when set, fires after any mutation to the bindings
// (Bind/Rebind/Unbind), so a menu or palette can refresh the shortcut
// hints it shows for its actions.
OnChange func()
// contains filtered or unexported fields
}
Keymap maps chords to action ids across scopes, resolves keystrokes to actions one stroke at a time (so multi-stroke chords work), detects binding conflicts, and supports live rebinding. It holds no reference to an ActionRegistry: it stores action ids, and the caller runs the resolved id against a registry. This keeps the binding layer independent of the actions themselves — the same map can be inspected, serialised or rebound without the actions existing yet.
A Keymap is not safe for concurrent use; drive it from the UI goroutine.
func (*Keymap) Bind ¶ added in v0.151.0
Bind binds chord to action in scope. Binding the same chord to the same action in the same scope is idempotent; binding it to a DIFFERENT action in the same scope returns ErrConflict (the existing binding is left intact). Binding the same chord in a different scope is always allowed — that is how a widget-scope binding shadows a global one.
func (*Keymap) Bindings ¶ added in v0.151.0
Bindings returns an independent snapshot of every binding in registration order (chords deep-copied), for inspection or serialisation.
func (*Keymap) Conflict ¶ added in v0.151.0
Conflict reports the action already bound to chord in scope, if any — the query a rebinding dialog runs to warn "this key is already used by X" before committing. conflict is false when the chord is free in that scope.
func (*Keymap) Feed ¶ added in v0.151.0
func (k *Keymap) Feed(ev Event, active ScopeMask) (action string, state MatchState)
Feed resolves one input event against the active scopes, tracking multi-stroke chords across calls:
- Complete: the event finished a binding; the returned id is the action to run and the pending chord is cleared.
- Partial: the event is a valid prefix of a longer chord; "" is returned and the pending chord is retained for the next Feed.
- NoMatch: the event matched nothing; the pending chord is cleared. If a chord was in progress it is abandoned and the event is retried as a fresh first stroke, so a stray key mid-chord can itself begin a new binding.
Non-keyboard events return NoMatch without disturbing the pending chord. ScopeGlobal is always considered active; the mask adds window/widget scopes.
func (*Keymap) Pending ¶ added in v0.151.0
Pending returns the chord typed so far but not yet resolved — the prefix a status line shows as "waiting for the next key". It is empty except between the strokes of a multi-stroke chord.
func (*Keymap) Rebind ¶ added in v0.151.0
Rebind changes the chord bound to action in scope, live: it removes every existing binding for that action in that scope and installs the new chord. If the new chord is already bound to a DIFFERENT action in the same scope it returns ErrConflict and makes no change. This is the hot-rebinding entry point a "customise shortcuts" UI calls.
func (*Keymap) Reset ¶ added in v0.151.0
func (k *Keymap) Reset()
Reset clears any pending chord, abandoning a half-typed multi-stroke binding (e.g. on focus loss or Escape).
func (*Keymap) ShortcutFor ¶ added in v0.151.0
ShortcutFor returns the chord bound to action at its most specific scope (widget over window over global), for a menu or palette to display as the action's current shortcut. It reflects live rebinds. ok is false when the action has no binding.
func (*Keymap) Unbind ¶ added in v0.151.0
Unbind removes the binding for chord in scope, returning whether one was found and removed.
func (*Keymap) UnbindAction ¶ added in v0.151.0
UnbindAction removes every binding for action across all scopes, returning how many were removed.
type Label ¶
type Label struct {
Base
Align Align
// VAlign is the vertical alignment of the text within the Label's bounds
// height. The zero value VAuto keeps the original layout (centred when taller than the
// text, else top); VTop/VMiddle/VBottom force a specific edge.
VAlign VAlign
// Ellipsis truncates the text with a trailing "…" when it is wider than the
// Label's bounds width. The zero value false renders the full text, which
// may overflow the bounds (the original behaviour).
Ellipsis bool
// Ink overrides the text colour. The zero value (A==0) means "inherit the
// theme's OnSurface colour"; set a colour with a non-zero alpha to paint the
// label in it (e.g. a muted or accent tone).
Ink RGBA
// FontSize, when positive, renders this label at that pixel size regardless
// of the global font size — e.g. a large clock face over the app's default
// body text. It re-renders the widget's base face (its Font override, else
// the active font) at FontSize px via NewTrueTypeFont; all metrics + bounds
// (width, glyph height, ellipsis) honour the resized face. The zero value
// (or any non-positive value) keeps the base face at its own size, so an
// unset FontSize is byte-identical to a pre-FontSize Label.
//
// FontSize only applies when the base face is a scalable TrueType/OpenType
// font (one exposing its sfnt bytes); over the built-in bitmap font — which
// has no outline to re-scale — it is ignored and the base face is used.
FontSize int
// contains filtered or unexported fields
}
Label is a passive widget that displays Text in the theme's OnSurface colour, drawn with the toolkit's 5x7 bitmap font. It is horizontally aligned per Align (left by default) and vertically aligned per VAlign (VAuto keeps the original centre-when-taller layout by default). When Ellipsis is set, over-wide text is truncated with a trailing "…" to fit the bounds width.
Label is non-interactive: HitTest returns false so clicks pass through to the widget beneath. Apps that want a clickable label should compose a Button with the text instead.
func (*Label) Draw ¶
Draw paints the Label's text with the toolkit's bitmap font. The text is positioned vertically per VAlign (VAuto keeps the original centre-when-taller) and horizontally per Align. When Ellipsis is set and the text is wider than the bounds it is truncated with a trailing "…" so it fits the width.
func (*Label) HitTest ¶
HitTest returns false unconditionally: a Label is decorative, not interactive. Override (or compose with a Button) to make a label receive events.
func (*Label) SetFontSize ¶ added in v0.86.0
SetFontSize sets the per-label pixel font size (see FontSize) and returns the Label for fluent chaining. A non-positive size clears the override, restoring the base face's own size.
func (*Label) Text ¶
func (l *Label) Text() *mvvm.Observable[string]
Text is the label's string as a shared mvvm.Observable: a host binds it (or subscribes) instead of touching a field, and updates go through Set — so there is no settable Text field. Lazily created so a bare &Label{} works.
func (*Label) TextRuns ¶ added in v0.122.0
TextRuns exposes the label's text to the selection subsystem as a single run at its current bounds, measured with the face it paints with. It implements SelectableText, so a label is selectable/copyable when its container is fed into a TextSelection (e.g. via CollectRuns). An empty label contributes nothing.
type Layout ¶ added in v0.59.0
Layout positions a container's items within its content rectangle. Swapping the Layout re-arranges the same items — the heart of the Ext operating model.
type LevelBar ¶
type LevelBar struct {
Base
Max int
Orientation Orientation
// Label, when non-empty, is centred over the bar (horizontal only) in
// Theme.OnSurface ink. The zero value draws no caption (the original look).
Label string
// Thresholds recolour the filled cells by value band. Empty (the default)
// keeps the Accent fill, so an unset LevelBar is byte-identical to before.
Thresholds []LevelThreshold
// contains filtered or unexported fields
}
LevelBar is the discrete cousin of ProgressBar: Max equal cells, the first Value cells filled + the rest in SurfaceAlt. Useful for battery / signal-strength / VU-meter style indicators. Orientation Horizontal (default) fills left→right; Vertical fills bottom→top.
Two optional refinements layer on without changing the default look (no Label, no Thresholds renders exactly as before, filling in Accent):
- Label: a caption centred over the bar (horizontal orientation only, where it fits), in Theme.OnSurface ink — e.g. "72%".
- Thresholds: value bands that recolour the filled cells (e.g. red when low, amber mid, green high). The band whose Min is the greatest value not exceeding Value wins; with no matching band (or none configured) the fill stays Theme.Accent.
func NewLevelBar ¶
NewLevelBar builds a LevelBar with the given Max (Value defaults to 0).
func (*LevelBar) A11y ¶ added in v0.40.0
A11y reports the LevelBar as a meter carrying its "value/max" reading, plus the numeric Min/Max/Now range triple (Min is 0, the empty meter).
func (*LevelBar) Draw ¶
Draw paints Max cells with a 1-px gap; the first Value cells use the threshold fill (Theme.Accent by default), the rest Theme.SurfaceAlt. For the horizontal orientation an optional Label is centred over the bar.
func (*LevelBar) Value ¶
func (l *LevelBar) Value() *mvvm.Observable[int]
Value is the number of lit cells as a shared mvvm.Observable; edits Set it and Draw reads it live. Lazily created, defaulting to 0 (empty).
type LevelThreshold ¶ added in v0.86.0
LevelThreshold recolours a LevelBar's fill once Value reaches Min. Several thresholds partition the range into coloured bands (e.g. {0,red}, {4,amber}, {8,green}); the band with the greatest Min not exceeding Value is applied.
type LineChart ¶ added in v0.12.0
type LineChart struct {
Base
Series []float64
Min, Max float64 // Y bounds; when equal, taken from the data
// contains filtered or unexported fields
}
LineChart plots one series of Y values as a polyline over a left+bottom axis frame -- the full-size sibling of the inline Sparkline. Values are spread evenly across the plot width and scaled vertically between Min and Max (auto- derived from the data when Min == Max). Display-only.
It renders through painter.Painter, so the same chart draws as anti-aliased pixels (WUI/GUI) or promoted cells (TUI). A single point renders as a dot; an empty series draws just the axes.
Example ¶
ExampleLineChart plots a series as a polyline over an axis frame.
package main
import (
"github.com/go-widgets/painter"
"github.com/go-widgets/toolkit"
)
// newSurface returns a PixelPainter over a fresh w×h RGBA buffer — the render
// target the examples draw into. A CellPainter would render the same widgets to
// a terminal grid instead.
func newSurface(w, h int) *painter.PixelPainter {
return painter.NewPixelPainter(make([]byte, 4*w*h), w, h)
}
func main() {
chart := toolkit.NewLineChart([]float64{3, 7, 2, 8, 5, 9, 4})
chart.SetBounds(toolkit.Rect{X: 0, Y: 0, W: 220, H: 80})
chart.Draw(newSurface(220, 80), toolkit.DefaultLight())
}
Output:
func NewLineChart ¶ added in v0.12.0
NewLineChart builds a LineChart over the given series with auto Y bounds.
func (*LineChart) A11y ¶ added in v0.40.0
A11y reports the LineChart as an img carrying its data-point count.
func (*LineChart) Draw ¶ added in v0.12.0
Draw paints the axis frame then the polyline (or a dot for a lone point).
func (*LineChart) Hover ¶ added in v0.89.0
func (c *LineChart) Hover() *mvvm.Observable[bool]
Hover is the reactive hover-highlight toggle as a shared mvvm.Observable; false draws no hover affordance. Lazily created, defaulting to off.
func (*LineChart) HoverIndex ¶ added in v0.89.0
func (c *LineChart) HoverIndex() *mvvm.Observable[int]
HoverIndex is the reactive hovered index as a shared mvvm.Observable. Lazily created, defaulting to 0.
func (*LineChart) OnEvent ¶ added in v0.100.0
OnEvent tracks the hover crosshair from the pointer: a move over the chart sets Hover/HoverIndex to the nearest data point, a move off the chart (a container forwards moves to every child) clears Hover.
type LinkCard ¶ added in v0.155.0
type LinkCard struct {
Base
// Favicon is the site glyph shown at the left; nil drops it and the title
// column takes the full width. It is scaled into a square the height of one
// text line.
Favicon *image.RGBA
// Title is the link headline, wrapped to the title column over as many lines
// as it needs. Empty draws no title.
Title string
// Domain is the source host ("example.com"), drawn dim under the title as a
// single elided line. Empty draws no domain.
Domain string
// Meta is the optional byline strip drawn under the title column; nil (or an
// all-hidden strip) draws nothing and reserves no space.
Meta *CardMeta
}
LinkCard is a content card for an external link: a small square favicon on the left, a wrapped title beside it and the source domain under the title in the dim tone, with an optional CardMeta strip closing the card. It is the unfurled-link tile a bookmarks list or a link-sharing feed is built from.
Layout (inside the CardPadX/Y inset):
┌────────────────────────────┐ │ ▣ Wrapped link title over │ ← Favicon (left), Title wrapped in the │ as many lines as needed │ column to its right │ example.com │ ← Domain, dim, under the title │ author · 3h · ▲12 · 💬4 │ ← Meta (optional), full content width └────────────────────────────┘
The favicon is a one-line-tall square; a nil Favicon drops it and the title column spans the full content width. The domain is a single elided line. LinkCard is passive content — no hover, no selection.
func NewLinkCard ¶ added in v0.155.0
NewLinkCard builds a LinkCard with an optional favicon (nil for none), a title, a domain and an optional meta strip (nil for none).
func (*LinkCard) A11y ¶ added in v0.155.0
A11y reports the link card as a group named by its title, with the source domain as its value.
func (*LinkCard) Children ¶ added in v0.155.0
Children yields the meta strip when present so a generic walk (accessibility, text selection) reaches it. The favicon, title and domain are drawn directly and are not sub-widgets.
func (*LinkCard) Draw ¶ added in v0.155.0
Draw paints the frame, the favicon, the wrapped title, the dim domain line and the meta strip. Content fills exactly Measure(Bounds().W): the same layout drives both.
type ListBox ¶
type ListBox struct {
Base
Items []string
RowHeight int // pixels per row; default 18 via NewListBox
OnActivate func(idx int)
MultiSelect bool // enable Ctrl/Shift multi-row selection
// ItemRenderer, when non-nil, draws each row's CONTENT instead of the
// default single line of text. It is handed the row's content rectangle
// rc (full row height, minus the scrollbar gutter), the row index, the
// item string, whether the row is selected, and the resolved text ink
// (theme.OnSurface, or theme.Background when selected). The ListBox still
// paints the row background (selection highlight) and owns scrolling,
// selection and drag-reorder -- the renderer only fills in the content, so
// a host can draw an icon + multi-line text, badges, a progress bar, etc.
// This is the DataView seam. The zero value (nil) keeps the original
// one-line text render, byte-identical to before this field existed.
ItemRenderer func(p painter.Painter, theme *Theme, rc Rect, index int, item string, selected bool, ink RGBA)
// Reorderable enables drag-to-reorder (see the type doc). Default
// false leaves the ListBox exactly as it behaved before this feature
// existed.
Reorderable bool
// OnReorder fires after a successful drag-reorder with the row's
// original index (from) and its final index after the move (to).
// Nil-guarded; never called while Reorderable is false.
OnReorder func(from, to int)
// contains filtered or unexported fields
}
ListBox is a vertical list of selectable string rows. Click on a row selects it + fires OnActivate.
Visual: each row is RowHeight pixels tall. The selected row uses Theme.Accent as background + Theme.Background as ink; unselected rows use Theme.Surface + Theme.OnSurface. Rows are rendered via font.DrawText with a 4 px left margin.
Multi-selection: setting MultiSelect enables Ctrl/Shift-modified clicks that build a set of selected rows (see IsSelected / SelectedIndices / SetSelection / ClearSelection / ToggleSelect / SelectRange). Selected remains the anchor/cursor row -- the point a Shift-click range is measured from, and the row most recently clicked (plain or Ctrl). When MultiSelect is false (the default) none of this is reachable: Ctrl/Shift are ignored and only Selected is ever highlighted, exactly as before this feature existed.
Virtual scrolling: ListBox is self-contained -- it never relies on an outer ScrollView. ScrollRow is the index of the top visible row; Draw paints only the rows that fit in Bounds().H (windowed rendering, so a list with thousands of rows costs the same per frame as one with a handful), and OnEvent maps click coordinates back through ScrollRow so hit-testing stays correct while scrolled. See ScrollTo / ScrollBy. When every row already fits in the viewport (len(Items) <= the number of visible rows) rendering is byte-identical to a ListBox with no scrolling at all -- no scrollbar is drawn and the windowing has no visible effect.
Drag-to-reorder: setting Reorderable turns the ListBox into both a DragSource and a DropTarget for its own private "listrow:" payload scheme (see ListRowDragPrefix / DragData / AcceptsDrop) -- a host wires its native drag gestures to the widget exactly as it would for any other DragSource/DropTarget pair (see dnd.go), and the ListBox handles tracking the pressed row, painting an insertion-line indicator on EventDragMove, and reordering Items in place on EventDrop, firing OnReorder. When Reorderable is false (the default) none of this is reachable: DragData always returns "", AcceptsDrop always returns false, EventDragMove/EventDragLeave/EventDrop are no-ops, and Draw never paints an indicator -- rendering + behavior are byte-identical to a ListBox with no drag-to-reorder support at all.
func NewListBox ¶
NewListBox builds a ListBox containing items. Selected starts at -1 (no row selected) and RowHeight defaults to 18 (a comfortable 7-px font + 11 px vertical padding).
func (*ListBox) A11y ¶ added in v0.40.0
A11y reports the ListBox as a listbox. Value is the selected item's text in single-select mode, or a "N selected" count while MultiSelect is on.
func (*ListBox) AcceptsDrop ¶ added in v0.37.0
AcceptsDrop reports whether payload is a reorder drag carrying ListBox's own "listrow:" scheme. It is always false when Reorderable is false, and false for any payload that doesn't carry the scheme (e.g. a different DragSource's payload).
func (*ListBox) ClearSelection ¶ added in v0.37.0
func (l *ListBox) ClearSelection()
ClearSelection empties the selection set. Selected (the anchor/cursor row) is left untouched.
func (*ListBox) DragData ¶ added in v0.37.0
DragData returns the drag-to-reorder payload for the row hit by the most recent EventClick (see pressedRow), or "" when Reorderable is false or no row has been pressed yet.
func (*ListBox) Draw ¶
Draw paints only the rows currently within the scroll window -- [ScrollRow, ScrollRow+visibleRows) -- positioning row i at top + (i-ScrollRow)*RowHeight. When every row already fits (len(Items) <= visibleRows() and ScrollRow clamps to 0), that window covers the whole list and rendering is byte-identical to a non-scrolling ListBox: no scrollbar, no clipping, full-width rows.
When the list overflows the viewport, rows are clipped to the content area (via painter.Clipper, if the backend supports it) so a partially-visible trailing row never bleeds past Bounds().H, and a thin scrollbar track+thumb is painted on the right edge.
func (*ListBox) Focused ¶ added in v0.101.0
func (f *ListBox) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*ListBox) IndexAt ¶ added in v0.85.0
onClick handles a click at (X, Y): it selects the row idx = ScrollRow + Y/RowHeight -- Y/RowHeight locates the row within the visible window, ScrollRow maps that back to an absolute Items index (clamped to the list length); OnActivate fires with that idx.
When MultiSelect is false, a click simply moves Selected to idx -- unchanged from the widget's original single-selection behaviour, and Ctrl/Shift are ignored entirely.
When MultiSelect is true:
- a plain click selects ONLY idx (clearing any other selected rows) and moves the anchor (Selected) to idx;
- a Ctrl-click toggles idx's membership in the selection set and moves the anchor to idx;
- a Shift-click selects the inclusive range between the current anchor (Selected) and idx, replacing the selection set, and leaves the anchor itself unchanged so successive Shift-clicks keep extending/shrinking from the same origin.
Every valid click also records idx as pressedRow (see DragData) -- unconditionally, regardless of Reorderable, since it costs nothing and DragData itself already gates on Reorderable. IndexAt returns the Items index under widget-local (x, y), accounting for the scroll offset, or -1 for empty space past the last row (or a zero RowHeight). Exposed so a host can hit-test a right-click and build a context menu for the item under the cursor (x is accepted for signature symmetry; the row is determined by y).
func (*ListBox) IsSelected ¶ added in v0.37.0
IsSelected reports whether row i is a member of the multi-selection set. It is independent of MultiSelect + Selected, so it can be queried (and pre-seeded via SetSelection/ToggleSelect/SelectRange) even before multi-selection is switched on.
func (*ListBox) OnEvent ¶
OnEvent dispatches: EventClick to onClick (selection, unchanged from before this feature); EventDragMove/EventDragLeave/EventDrop to the drag-to-reorder handlers below, which are all no-ops while Reorderable is false, so behavior is byte-identical to a ListBox with no drag-to-reorder support when the feature isn't opted into. EventScroll (wheel) scrolls the visible window; the Arrow/Page/Home/End keys move the selection cursor and Enter/Space activate it (see handleKey). Every other event kind is ignored.
func (*ListBox) ScrollBy ¶ added in v0.37.0
ScrollBy shifts ScrollRow by delta rows (negative scrolls up), clamped exactly like ScrollTo.
func (*ListBox) ScrollRow ¶ added in v0.37.0
func (l *ListBox) ScrollRow() *mvvm.Observable[int]
ScrollRow is the index of the top visible row as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable ScrollRow field. A wheel scroll, an arrow-key scroll-into-view, or a scrollbar drag Sets it (always clamped, see ScrollTo/ScrollBy); Draw reads it back. A bare &ListBox{} lazy-inits the observable to 0; NewListBox seeds it to 0 too.
func (*ListBox) ScrollTo ¶ added in v0.37.0
ScrollTo moves the top visible row to row, clamped to [0, maxScrollRow()], and writes the clamped value back to ScrollRow.
func (*ListBox) SelectRange ¶ added in v0.37.0
SelectRange selects the inclusive range of rows between a and b (either order accepted), replacing the current selection set. The range is clamped to [0, len(Items)); if the list is empty, or the clamped range is inverted, the resulting selection is empty.
func (*ListBox) Selected ¶
func (l *ListBox) Selected() *mvvm.Observable[int]
Selected is the anchor/cursor row as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Selected field. A click or an arrow-key move Sets it; subscribers are notified. A bare &ListBox{} lazy-inits the observable to 0; NewListBox seeds it to -1 (no selection).
func (*ListBox) SelectedIndices ¶ added in v0.37.0
SelectedIndices returns the selected rows in ascending order. The returned slice is a fresh copy the caller may mutate freely.
func (*ListBox) SetFocused ¶ added in v0.101.0
func (f *ListBox) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
func (*ListBox) SetSelection ¶ added in v0.37.0
SetSelection replaces the selection set with exactly the given indices. Indices outside [0, len(Items)) are silently dropped.
func (*ListBox) ToggleSelect ¶ added in v0.37.0
ToggleSelect flips row i's membership in the selection set. Out-of-range indices are a no-op.
type LoadMask ¶ added in v0.80.0
type LoadMask struct {
Base
// Message is an optional caption shown under the spinner (e.g. "Loading…").
Message string
// Scrim is the dimming colour painted over the bounds. The zero value
// uses a translucent black (src-over blended by the pixel back-end), so a
// LoadMask dropped in with no configuration reads as a subtle dim.
Scrim painter.RGBA
// contains filtered or unexported fields
}
LoadMask is a "busy" overlay: while Active it dims its whole bounds with a translucent scrim, paints a centred indeterminate Spinner and an optional Message, and swallows pointer events so the content beneath cannot be interacted with mid-load. Inactive, it draws nothing and lets events pass through (HitTest false), so it is safe to leave permanently mounted as the topmost Overlay layer and just toggle Active.
Drive the spinner animation by calling Tick(dt) from the host's frame loop (no goroutine/timer), the same cadence contract as Spinner and ProgressCircle.
func NewLoadMask ¶ added in v0.80.0
NewLoadMask builds an inactive LoadMask with the given message (may be "").
func (*LoadMask) A11y ¶ added in v0.130.0
A11y reports the LoadMask as a status region: it exists precisely to say that work is in progress, which is something a reader must be able to announce. The value distinguishes a mask that is actually up from one merely composed into the tree — an inactive mask draws nothing and blocks nothing.
func (*LoadMask) Active ¶ added in v0.80.0
func (m *LoadMask) Active() *mvvm.Observable[bool]
Active gates the whole widget as reactive mvvm.Observable state: false draws nothing and is event-transparent; true dims + shows the spinner/message + swallows events. Lazily created, defaulting to inactive.
func (*LoadMask) Animating ¶ added in v0.155.0
Animating reports whether the mask still needs frames: true exactly when it is Active. Together with Tick this makes LoadMask an Animator, so a host drives its busy spinner through TickTree / TreeAnimating with no manual bookkeeping.
func (*LoadMask) Draw ¶ added in v0.80.0
Draw dims the bounds and paints the spinner + message while Active; inactive or empty-bounds it paints nothing.
func (*LoadMask) HitTest ¶ added in v0.80.0
HitTest reports whether the mask should catch a pointer event: only while Active, so an inactive mask is fully transparent to clicks and an active one shields the content beneath it (the modal-scrim idiom, see Backdrop). While Active a covered event routes to the inherited Base.OnEvent no-op, i.e. it is swallowed and never reaches the content underneath.
type LogLevel ¶ added in v0.205.0
type LogLevel int
LogLevel is the severity of a log entry, which selects the ink its message is painted in. The four levels mirror the semantic tones the rest of the toolkit already uses (see Alert / Toast): Warn is amber and Error is brick red — the two that must jump out of a scrolling console — while Info paints in the plain OnSurface ink and Debug in the dim, subordinate tone. Any out-of-range value falls back to Info's neutral ink.
const ( // LogDebug is diagnostic noise a developer opts into: painted dim so it // recedes behind the entries that matter. LogDebug LogLevel = iota // LogInfo is a normal progress line ("compile started"): the neutral // OnSurface ink. LogInfo // LogWarn flags a non-fatal problem ("deprecated \\usepackage"): amber, the // same tone Alert/Toast use for a warning. LogWarn // LogError flags a failure the user must address ("undefined control // sequence"): brick red, matching Alert/Toast's error tone. LogError )
type LogView ¶ added in v0.205.0
type LogView struct {
Base
// MaxEntries bounds the retained scrollback. When > 0, Append drops the
// oldest entries so the history never exceeds MaxEntries; when 0 (the zero
// value) the history is unbounded. It is set-once bounding config, not
// reactive state — the entries themselves live in the unexported history
// below, mutated only through Append / Clear.
MaxEntries int
// contains filtered or unexported fields
}
LogView is a scrollable console panel: a vertical list of timestamped log entries, newest at the BOTTOM, that accumulates scrollback history. It is the widget a diagnostics/output tab is built on — a compiler log, a build console, a request trace — where lines arrive over time and the user scrolls back through what has already been printed.
Each entry carries a HOST-FORMATTED timestamp string, a LogLevel, and a message. The widget never reads the clock itself (no time.Now / time.Time), so its rendering is fully deterministic and testable: the host formats the time however it likes (a browser locale string, a monotonic build clock, …) and hands the finished text to Append. The timestamp is painted in a dim ink at the start of the row; the message follows in the level's ink. A message containing embedded newlines is rendered as one row per line, the continuation rows hanging under the message column (never clipped to a single line).
Scrolling is delegated to an embedded ScrollView, so the panel gets vertical AND horizontal scrolling, a wheel, drag/touch panning with momentum, and scrollbar thumbs for free — a long line scrolls sideways, a tall history scrolls up. History is bounded by MaxEntries: with a positive bound the oldest entries are dropped once the count exceeds it; 0 (the default) keeps everything.
Auto-follow: Append scrolls to the newest entry ONLY when the view is already pinned to the bottom. A user who has scrolled up to read history is therefore never yanked back down by an incoming line, while a user watching the tail keeps seeing the newest entry — the behaviour every good log console has.
LogView implements Widget (Draw + OnEvent for wheel / drag / key scrolling, Bounds / SetBounds / HitTest via Base). Its reactive history is internal — a host feeds it through Append / Clear and reads it through Len — so there is no host-settable state field; MaxEntries is set-once bounding config.
func NewLogView ¶ added in v0.205.0
func NewLogView() *LogView
NewLogView builds an empty, unbounded LogView. Set MaxEntries afterwards to bound the scrollback. Bounds start zero; the host sizes the panel via SetBounds like any other widget.
func (*LogView) A11y ¶ added in v0.205.0
A11y reports the panel as an ARIA log region, its Value the current entry count — mirroring the "N events" reading Timeline gives its list.
func (*LogView) Append ¶ added in v0.205.0
Append adds one entry — a host-formatted timestamp, a severity level, and a message (embedded newlines become separate rows) — to the bottom of the history. When MaxEntries is exceeded the oldest entries are dropped. The view scrolls to the new entry ONLY if it was already pinned to the bottom, so a user reading scrollback is not yanked down.
func (*LogView) Clear ¶ added in v0.205.0
func (lv *LogView) Clear()
Clear empties the history and returns the viewport to the top-left.
func (*LogView) Draw ¶ added in v0.205.0
Draw paints the console: a Surface ground, the scrolled rows (via the embedded viewport, which clips + translates them and paints the scrollbars), then a border on top so it frames the panel cleanly.
func (*LogView) Len ¶ added in v0.205.0
Len reports how many entries the history currently holds (after any MaxEntries eviction). A multi-line message counts as one entry.
type MarkdownEditor ¶ added in v0.38.0
type MarkdownEditor struct {
Base
// Source is the editable Markdown-source pane.
Source *TextView
// Preview is the read-only rendered pane, kept in sync with Source.
Preview *MarkdownView
// Split is the fraction (0..1, exclusive) of Bounds given to Source
// along the split axis; the remainder (minus the divider) goes to
// Preview. A value outside (0, 1) -- including the zero value, so a
// struct literal built without NewMarkdownEditor behaves sanely --
// falls back to 0.5.
Split float64
// SideBySide selects the split axis: true lays Source left / Preview
// right; false stacks Source above Preview.
SideBySide bool
}
MarkdownEditor is a split source/preview editor: a TextView holding editable Markdown source on one side, a MarkdownView rendering that source live on the other. It composes the two existing widgets purely through their public API (TextView.Text/SetText, MarkdownView.Source) -- it does not modify either.
Only the Source pane is interactive; a click landing in the Preview pane is a no-op (the preview is display-only, per MarkdownView's own docs). Every event routed to Source is followed by a re-sync of Preview.Source so the rendered pane never drifts from the edited buffer.
func NewMarkdownEditor ¶ added in v0.38.0
func NewMarkdownEditor(initial string) *MarkdownEditor
NewMarkdownEditor builds a MarkdownEditor seeded with initial Markdown source: Source gets a TextView pre-loaded with initial (an empty string still yields a valid single-line buffer, per NewTextView), Preview gets a MarkdownView already rendering that same text. Split defaults to 0.5 and SideBySide defaults to true (left/right).
func (*MarkdownEditor) A11y ¶ added in v0.105.0
func (m *MarkdownEditor) A11y() A11yInfo
A11y reports the MarkdownEditor as a textbox carrying its editable source text (Text() yields "" when the source pane is nil).
func (*MarkdownEditor) Draw ¶ added in v0.38.0
func (m *MarkdownEditor) Draw(p painter.Painter, theme *Theme)
Draw lays out + paints Source and Preview side by side (or stacked), split per Split/SideBySide, with a markdownEditorDividerW-px divider between them.
func (*MarkdownEditor) OnEvent ¶ added in v0.38.0
func (m *MarkdownEditor) OnEvent(ev Event)
OnEvent routes every event to Source, translated into its local frame, then resyncs Preview. Only EventClick is hit-tested against the pane split first: a click landing in the Preview pane (or on the divider) is a no-op, since Preview is display-only and nothing there should steal focus from Source. Non-click events (keyboard, IME, drag) always go to Source, matching the "only Source is interactive" model -- there is no competing widget that could plausibly want them instead.
func (*MarkdownEditor) SetText ¶ added in v0.38.0
func (m *MarkdownEditor) SetText(s string)
SetText replaces the source-pane text + resyncs Preview.
func (*MarkdownEditor) Text ¶ added in v0.38.0
func (m *MarkdownEditor) Text() string
Text returns the current source-pane text, or "" when Source is nil.
type MarkdownView ¶ added in v0.15.0
MarkdownView renders a subset of Markdown as laid-out text -- the read-only document widget the toolkit lacked. It handles the block structure that matters on a fixed-width bitmap font: ATX headings (`#`..`######`), bullet lists (`-`/`*`/`+`), fenced code blocks (```), and blank-line-separated paragraphs, word-wrapped to the widget width.
The 5x7 font has a single weight and size, so hierarchy is shown through layout rather than type scale: headings get an Accent underline (levels 1-2) and the Accent ink, bullets get a "• " marker with a hanging indent, and code blocks sit on a SurfaceAlt band. Inline emphasis (*bold*, _italic_) is not styled -- the bitmap font has no variants -- but its text still renders. Display-only; wrap in a ScrollView for long documents.
Example ¶
ExampleMarkdownView renders a Markdown subset (headings, lists, code, paragraphs) laid out for the bitmap font.
package main
import (
"github.com/go-widgets/painter"
"github.com/go-widgets/toolkit"
)
// newSurface returns a PixelPainter over a fresh w×h RGBA buffer — the render
// target the examples draw into. A CellPainter would render the same widgets to
// a terminal grid instead.
func newSurface(w, h int) *painter.PixelPainter {
return painter.NewPixelPainter(make([]byte, 4*w*h), w, h)
}
func main() {
md := toolkit.NewMarkdownView("# Title\n\nA paragraph.\n\n- one\n- two")
md.SetBounds(toolkit.Rect{X: 0, Y: 0, W: 260, H: 120})
md.Draw(newSurface(260, 120), toolkit.DefaultLight())
}
Output:
func NewMarkdownView ¶ added in v0.15.0
func NewMarkdownView(source string) *MarkdownView
NewMarkdownView builds a MarkdownView over the given Markdown source.
func (*MarkdownView) A11y ¶ added in v0.40.0
func (m *MarkdownView) A11y() A11yInfo
A11y reports the MarkdownView as a document. Source is typically a full document body, too long to usefully surface as an accessible Name.
type MatchState ¶ added in v0.151.0
type MatchState int
MatchState is the outcome of feeding one keystroke to a Keymap.
const ( // NoMatch means the keystroke completed no binding and continues no // pending chord; the chord state is reset. NoMatch MatchState = iota // Partial means the keystroke is a valid prefix of one or more longer // chords; the [Keymap] is now awaiting the next stroke. Partial // Complete means the keystroke completed a binding; the returned action // id should be run and the chord state is reset. Complete )
func (MatchState) String ¶ added in v0.151.0
func (s MatchState) String() string
String returns the state's name for diagnostics.
type Material ¶ added in v0.149.0
type Material struct {
Base
// Kind selects the default blur radius and colour wash, and is what a native
// back-end maps to its system material. See MaterialKind.
Kind MaterialKind
// Blend selects behind-window vs within-window blur. Honoured by a native
// back-end; the fallback blurs Source either way.
Blend MaterialBlend
// Source is the RGBA content behind the material (SW*SH*4 bytes) in surface
// coordinates, that the fallback blurs. An invalid or nil buffer degrades the
// fallback to a plain translucent tint.
Source []byte
SW, SH int
// Sigma overrides the Gaussian blur radius. Zero uses the Kind's default;
// a Kind whose default is 0 (Selection) paints no blur.
Sigma float64
// Tint overrides the colour wash. The zero value (A == 0) uses the Kind's
// default derived from the theme; a non-zero value is used verbatim.
Tint painter.RGBA
// Child is composited on top of the material, laid out to fill its Bounds.
// nil draws no child.
Child Widget
// contains filtered or unexported fields
}
Material is a translucent, blurred background panel with an optional child composited on top. It is the sidebar/menu/HUD "vibrancy" surface, expressed as an ordinary widget so a layout drops it in like any other.
It renders one of two ways:
Native backing. A back-end that can install a real platform vibrancy view (the Cocoa backend's NSVisualEffectView) discovers every Material in the tree with CollectMaterials, places a system effect view behind each one's Bounds, punches a transparent hole in the framebuffer there, and calls Material.SetNativeBacked(true). Draw then skips the fallback entirely — the system blur shows through the hole and applies its own material tint — and paints only the child. This is the seam; the mapping from MaterialKind to a system material lives in the back-end, not here, so this package names no platform.
Pure-Go fallback. Everywhere else (X11, Wayland, wasm, an image render) the material blurs a caller-supplied Source through a Gaussian and washes a translucent Tint over it. Source is the content behind the material in SURFACE coordinates — a compositor has the desktop wallpaper, a window shell has the pixels drawn before the material — because the Painter has no read-back, exactly as Thumbnail takes an explicit source buffer. The Gaussian is github.com/go-images/images.GaussianBlur, the fleet's image library, not a kernel written here.
With no Source and no native backing the material degrades to a plain translucent panel (just the Tint wash over whatever is already in the buffer), which still reads as a material, only without the softened backdrop.
func CollectMaterials ¶ added in v0.149.0
CollectMaterials returns every Material in the tree rooted at root, in visual order. A native back-end calls it each frame to reconcile its system vibrancy views: for each returned material it reads Kind/Blend/Bounds, installs or moves an effect view, and calls SetNativeBacked so the fallback stands down.
It descends the same way WalkA11y does — through any widget exposing its children — so a material nested in a layout is still found. Bounds are surface coordinates (this toolkit's bounds are absolute), which is what a back-end compositing native views needs.
func NewMaterial ¶ added in v0.149.0
func NewMaterial(kind MaterialKind) *Material
NewMaterial builds a Material of the given kind with no source and no child. Set Source (or SetSource) for the fallback blur, and Child for content on top.
func (*Material) A11y ¶ added in v0.149.0
A11y marks the material itself as presentational: it is decorative backing, so a screen reader looks through it to the child, which the walk still descends into (see WalkA11y).
func (*Material) Children ¶ added in v0.149.0
Children exposes the material's child so generic walks (accessibility, material collection) descend into it.
func (*Material) Draw ¶ added in v0.149.0
Draw paints the material and its child. See the type doc for the two paths.
func (*Material) HitTest ¶ added in v0.149.0
HitTest passes pointer events through the decorative backing to whatever is composited behind it UNLESS a child covers the point — the same event-transparent idiom as Backdrop, but a material carrying interactive content (a sidebar's list) must let that content be clicked.
func (*Material) Invalidate ¶ added in v0.149.0
func (m *Material) Invalidate()
Invalidate drops the cached blur. Call it after overwriting the contents of Source in place; SetSource already does.
func (*Material) NativeBacked ¶ added in v0.149.0
NativeBacked reports whether a native vibrancy view backs this material.
func (*Material) OnEvent ¶ added in v0.149.0
OnEvent forwards events to the child (translated to child-local coordinates), matching the single-child container convention. A move is forwarded unconditionally so the child can clear a hover face; other kinds land only when the point is inside the child.
func (*Material) SetBounds ¶ added in v0.149.0
SetBounds positions the material and lays the child out to fill it.
func (*Material) SetNativeBacked ¶ added in v0.149.0
SetNativeBacked records whether a back-end has installed a real platform vibrancy view behind this material. A back-end calls it after placing (true) or removing (false) the system effect view; the default is false (fallback).
func (*Material) SetSource ¶ added in v0.149.0
SetSource replaces the background buffer the fallback blurs and drops the cached blur. The buffer is referenced, not copied; length must be w*h*4.
func (*Material) Spec ¶ added in v0.149.0
func (m *Material) Spec() MaterialSpec
Spec returns the material's placement and role as plain data.
type MaterialBlend ¶ added in v0.149.0
type MaterialBlend int
MaterialBlend selects what a material blurs: the content BEHIND the window (the desktop showing through) or the content WITHIN the window drawn behind the material. It mirrors the two blending modes every platform vibrancy API offers. The pure-Go fallback treats both identically — it blurs whatever Source it is given — but a native back-end honours the distinction when it installs its system effect view.
const ( // BlendBehindWindow blurs what is behind the window itself. BlendBehindWindow MaterialBlend = iota // BlendWithinWindow blurs the window content drawn behind the material. BlendWithinWindow )
type MaterialKind ¶ added in v0.149.0
type MaterialKind int
MaterialKind names a standard translucent-background material — a blurred backdrop washed with a semi-transparent colour, the "vibrancy" a modern desktop paints behind a sidebar, a menu or a heads-up panel so the content behind the surface shows through, softened.
The vocabulary is deliberately generic UI roles (a sidebar, a menu, a titlebar, a HUD) rather than any one platform's material names: a native back-end maps each kind onto its own system material (see the window package's Cocoa backend, which maps them to NSVisualEffectView materials), and the pure-Go fallback maps each onto a blur radius (sigma) plus a colour wash. A widget tree therefore asks for "a sidebar material" and renders correctly on every back-end without naming a platform.
const ( // MaterialWindowBackground is the whole-window translucent ground. MaterialWindowBackground MaterialKind = iota // MaterialSidebar is the list rail beside primary content. MaterialSidebar // MaterialTitlebar is the strip along the top of a window. MaterialTitlebar // MaterialMenu is a menu / dropdown surface. MaterialMenu // MaterialPopover is a transient floating panel anchored to a control. MaterialPopover // MaterialHUD is a dark heads-up panel (always dark, independent of theme). MaterialHUD // MaterialSelection is a light accent wash over a selected region; it has // no blur of its own (sigma 0), only a translucent tint. MaterialSelection )
type MaterialSpec ¶ added in v0.149.0
type MaterialSpec struct {
Kind MaterialKind
Blend MaterialBlend
Rect Rect
}
MaterialSpec is one material's placement and role, as a native back-end reads it: what system material to install (Kind), how to blend it (Blend) and where (Rect, in surface coordinates). It is the plain-data view returned alongside the widget by CollectMaterials.
type Measurer ¶ added in v0.56.0
Measurer is an optional interface a widget may implement to advertise its natural size — an optional natural-size query. Box layouts consult it for cross-axis alignment (Align != BoxStretch); widgets that do not implement it fall back to their current cross Bounds, and failing that stretch to fill. availW/availH is the space the box can offer the child on each axis.
type MediaCard ¶ added in v0.155.0
type MediaCard struct {
Base
// Title is the headline, wrapped to the content width over as many lines as
// it needs. Empty draws no title.
Title string
// Thumbnail is the lead image; nil drops the image band. It is scaled to the
// full content width at its own aspect ratio.
Thumbnail *image.RGBA
// Meta is the optional byline strip drawn under the title; nil (or an
// all-hidden strip) draws nothing and reserves no space.
Meta *CardMeta
}
MediaCard is a content card led by a prominent thumbnail: the image spans the full content width at the top, the wrapped title sits below it, and an optional CardMeta strip closes the card. It is the tile a media / video / photo feed is built from.
Layout (top to bottom, inside the CardPadX/Y inset):
┌──────────────────────────┐ │ full-width thumbnail │ ← Thumbnail, at the image's own aspect │ │ ├──────────────────────────┤ │ Wrapped title over as │ ← Title, wrapped to the content width │ many lines as it needs │ │ author · 3h · ▲12 · 💬4 │ ← Meta (optional) └──────────────────────────┘
The thumbnail is scaled to the full content width at its own aspect ratio, so it fills the width with no letterbox; a nil Thumbnail drops the image band entirely. MediaCard is passive content — no hover, no selection.
func NewMediaCard ¶ added in v0.155.0
NewMediaCard builds a MediaCard with a title, an optional thumbnail (nil for none) and an optional meta strip (nil for none).
func (*MediaCard) A11y ¶ added in v0.155.0
A11y reports the media card as a group named by its title.
func (*MediaCard) Children ¶ added in v0.155.0
Children yields the meta strip when present, so a generic walk (accessibility, text selection) reaches it. The title and thumbnail are drawn directly and are not sub-widgets.
type Menu ¶
type Menu struct {
Base
Items []MenuItem
OnClose func()
// Scale multiplies every fixed metric — row height, insets, gutters, the
// check/submenu glyphs — so a HiDPI host gets a crisp, correctly sized menu
// instead of one laid out in raw pixels. A host that renders at the backing
// pixel ratio (optionally times a UI zoom) sets Scale to it, mirroring
// Browser.Scale. Zero or negative defers to the toolkit-wide [MetricScale],
// which is 1 unless a host set it -- so a menu that is told nothing still
// follows the one knob a HiDPI host is documented to turn.
Scale float64
// OnItemToggle fires when activating a checkable or radio row changes its
// Checked state (before the row's Action and OnClose run). i is the row
// index and checked is its NEW state: for a Checkable row the flipped value,
// for a RadioGroup member always true (its just-selected state; the siblings
// it cleared are not separately reported). Plain (non-checkish) rows never
// fire it. Nil is safe.
OnItemToggle func(i int, checked bool)
// contains filtered or unexported fields
}
Menu is a vertical popover-style list of MenuItems. Used by the compositor's right-click root menu, by MenuBar drop-downs and by any widget that needs an Openbox-style picker.
func (*Menu) A11y ¶ added in v0.40.0
A11y reports the Menu as a menu carrying the hovered row's label, if any.
func (*Menu) Draw ¶
Draw paints the menu's body + every row + a hover highlight on the currently-hovered row.
func (*Menu) Hover ¶
func (m *Menu) Hover() *mvvm.Observable[int]
Hover is the reactive index of the hovered row as a shared mvvm.Observable: -1 when no row is hovered, otherwise the row index. A host binds it (Get / Set / Subscribe); pointer moves and keyboard navigation Set it, and Draw reads it to highlight the row. There is no settable Hover field — the state is MVVM-only. Lazy-initialised to -1 (no hover), the constructor's default.
func (*Menu) OnEvent ¶
OnEvent: a click on an enabled row fires its Action + closes the menu via OnClose (if wired); a click (or hover, or ArrowRight) on a submenu-parent row opens its child Menu beside the row. Keyboard: ArrowUp/ArrowDown move the Hover highlight to the previous/next navigable row (skipping separators and disabled rows, wrapping at both ends); ArrowRight opens the hovered submenu; Enter/Space activate the hovered row (opening its submenu, or firing its Action); Escape calls OnClose. While a submenu is open, pointer events over the child route into it, keys drive the child, and ArrowLeft/Escape close it. A disabled Menu ignores keys.
func (*Menu) RowAt ¶ added in v0.192.0
RowAt reports the index of the menu entry under the widget-local point (x, y) — the SAME coordinate space Menu.OnEvent receives — that a click there would activate, or -1 when the point is outside the menu's bounds, over a separator, or over a disabled (no Action and no Submenu) row. In other words it returns EXACTLY the rows OnEvent acts on: a compositor that paints the menu and routes its own clicks can ask "which row is under this point" instead of mirroring MenuRowH, the body top pad, separator heights, the scroll offset and the scale/density factors — which drift out of sync when those change.
It shares its row resolution with the click path through [Menu.hitRow], so the answer can never diverge from what a click actually does; the x/y bounds guard ([Base.localInBounds]) is the only part specific to this query, mirroring the bounds check a host performs before forwarding a click. It does NOT recurse into an open submenu (a separate overlay the host positions and routes on its own); the index is always into this menu's own Menu.Items.
func (*Menu) RowHeight ¶ added in v0.192.0
RowHeight returns the pixel height of row i in the current scale and touch density: [Menu.rowH] for a normal row, the scaled MenuSeparatorH for a separator, or -1 when i is out of range. Paired with Menu.RowTop it gives a host the full row band [RowTop(i), RowTop(i)+RowHeight(i)) without re-deriving the metrics.
func (*Menu) RowTop ¶ added in v0.192.0
RowTop returns the widget-local top Y of row i — the Y at which its band begins in the same coordinate space Menu.RowAt takes and Menu.OnEvent receives, i.e. with the current scroll offset already applied (so it is negative for a row scrolled above the fold). It is the exact top Menu.Draw paints the row at. Panic-free for any i: an i past the last row returns the body's bottom edge and a negative i the top inset, matching the internal geometry.
type MenuBar ¶
MenuBar is a horizontal strip of top-level menu names (File, Edit, View, ...). Clicking a name opens its associated Menu as a popover just below the strip.
The MenuBar itself doesn't own the open Menu's drawing (the containing app composes it with whatever overlay surface it has); MenuBar just exposes Active so the app knows which menu to render.
func (*MenuBar) A11y ¶ added in v0.40.0
A11y reports the MenuBar as a menubar carrying the currently-open top-level menu's name, if any.
func (*MenuBar) Active ¶
func (b *MenuBar) Active() *mvvm.Observable[int]
Active is reactive state as a shared mvvm.Observable; edits Set it. Lazily created.
func (*MenuBar) HandleShortcut ¶
HandleShortcut walks every menu's items and fires the Action of the first item whose Shortcut equals code (case-sensitive; the host is expected to normalise Ctrl+N vs Cmd+N before calling). Returns true if an item fired, false if no match. Menu ordering + item ordering give a deterministic priority — first match wins.
Skipped: separators, disabled items (nil Action). A matching item with a submenu still fires its Action (if any); the submenu is not opened by a shortcut.
Typical usage from a wasmbox client's Go main:
case "keydown":
code := formatShortcut(ev) // host builds "Ctrl+N" etc.
if state.menuBar.HandleShortcut(code) { render(); return }
state.editor.OnEvent(...) // fallthrough: forward to focus
func (*MenuBar) Mnemonic ¶
Mnemonic returns the first letter of the i-th menu name (upper-case, or 0 if the index is out of range / the name is empty). Useful for a host that wants to draw "_F_ile"-style underlines under the mnemonic character.
func (*MenuBar) NameOriginX ¶
NameOriginX returns the X offset of the i-th top-level name within the bar (cumulative sum of NameWidth up to i, exclusive). Same motivation as NameWidth: a host that positions a popover under a clicked name reads this to align on the correct column.
func (*MenuBar) NameWidth ¶
NameWidth returns the pixel width of the i-th top-level name after the auto-size rule: max(MenuBarItemW, TextWidth(name) + 2*pad). Exposed so hosts that render their own popover under a clicked name know how wide the "click zone" was.
func (*MenuBar) OnEvent ¶
OnEvent: a click on a name toggles its menu (Active = idx or -1). Also honours mnemonic keyboard shortcuts on EventKeyDown when the Code carries an "Alt+X" hint (X = one of the top-level names' first letter, case-insensitive) — matches the GNOME/Windows menu-bar Alt+letter convention. The host is responsible for formatting the key event's Code as "Alt+F" etc. before forwarding.
type MenuItem ¶
type MenuItem struct {
Label string
Action func()
Separator bool
Shortcut string
Checkable bool
Checked bool
RadioGroup int
// Icon, when set, paints a leading glyph in the row's icon cell, to the left
// of the label. The Menu reserves an icon gutter (shifting every row's label
// right) whenever ANY item has one, so labels stay aligned. It is handed the
// square cell rect to fill and the row's current ink (so the glyph inverts on
// a hovered row and greys out on a disabled one), keeping the toolkit free of
// any particular icon set — the host draws whatever it likes into the cell.
Icon func(p painter.Painter, cell Rect, ink RGBA)
}
MenuItem is one row in a Menu. Label is the human text; Action is the callback fired on click. A nil Action turns the row into a disabled (greyed-out) entry; a non-empty Submenu lets it open a nested Menu (popover) on hover or click.
Separator items render as a thin SurfaceAlt line + are not clickable. They have empty Label + nil Action.
Shortcut is a hint string ("Ctrl+N", "Cmd+O", …) drawn right-aligned on the row in the muted SurfaceAlt tone. Purely visual: the host app is responsible for actually wiring the key combo to the item's Action (there is no cross-platform "Ctrl vs Cmd" logic in the toolkit — different apps route keys through different SDKs).
Checkable marks the row as a toggle: activating it flips Checked (in addition to still calling Action, if any) + the row renders a ✓ glyph in a left-hand gutter when Checked.
RadioGroup, when non-zero, makes the row a member of a mutually exclusive set: every item in the same Menu sharing the same RadioGroup value is a sibling. Activating one sets its Checked to true + clears Checked on every sibling; the row renders a • (bullet) glyph instead of a check mark. RadioGroup implies checkable behaviour — Checkable does not need to also be set. RadioGroup == 0 means "not part of any radio group".
type ModernDockStyle ¶ added in v0.179.0
type ModernDockStyle struct{}
ModernDockStyle is the macOS look: a SurfaceAlt ground and flat, rounded item faces — Surface at rest, Accent when active — with a running dot under the icon. It is the AppDock default.
func (ModernDockStyle) DrawFace ¶ added in v0.179.0
func (ModernDockStyle) DrawFace(p painter.Painter, theme *Theme, r Rect, st DockItemState) RGBA
func (ModernDockStyle) DrawGround ¶ added in v0.179.0
func (ModernDockStyle) DrawGround(p painter.Painter, theme *Theme, r Rect)
type Momentum ¶ added in v0.185.0
type Momentum struct {
// Friction is the fraction of velocity retained per second during a fling
// (0 < Friction < 1). Smaller = more drag = a shorter glide; a value near 1
// coasts a long way. It is a per-second figure, applied as Friction^dt each
// tick, so it is independent of the frame rate.
Friction float64
// StopVelocity is the speed (in px/s, always compared as a magnitude) at or
// below which a coasting fling is considered stopped and settles to rest.
// It must be > 0 so the exponential tail terminates.
StopVelocity float64
// Bounce enables rubber-band overscroll. When true, a drag past a bound is
// resisted (see DragBy) and a fling that reaches a bound stretches past it
// and springs back. When false, both the drag and the fling clamp hard at
// the bound — the behaviour a mouse/desktop scroller wants.
Bounce bool
// Stiffness is the spring constant (per second squared) pulling an
// overscrolled offset back toward its bound. Higher = a snappier return.
Stiffness float64
// Damping is the spring's velocity damping (per second). It bleeds energy
// out of the return so the offset settles onto the bound instead of
// oscillating around it.
Damping float64
// MaxOverscroll caps how far (px) past a bound the offset may travel, for
// both a resisted drag and a spring. Zero means "no travel past the bound"
// even with Bounce on. The rubber-band's resisted stretch asymptotes to this.
MaxOverscroll float64
// SnapDistance is the overshoot magnitude (px) within which the spring, once
// it is also below StopVelocity, snaps exactly to the bound and rests. It
// terminates the spring's asymptotic tail the way StopVelocity terminates
// the fling's.
SnapDistance float64
// contains filtered or unexported fields
}
Momentum is the pure-logic inertial ("fling") scroll engine that gives a touch surface its signature feel: let go of a flick and the content keeps gliding, decelerating to a smooth stop; push past an edge and it stretches against a rubber band, then springs back to rest exactly on the boundary.
It is ONE axis. A two-dimensional scroller composes two Momentum values, one for X and one for Y — exactly the way ScrollView keeps OffsetX and OffsetY as independent clamps — so the same engine drives a vertical list, a horizontal carousel, or both at once without either axis knowing about the other. It holds no widget reference, no geometry beyond the [min, max] offset window it is told to clamp against, and draws nothing: a consumer feeds it a release velocity (or raw drag samples via VelocityTracker), reads back an offset each frame, and paints wherever that offset says.
Determinism ¶
Like the rest of the toolkit (GestureRecognizer, Animator) the engine owns no goroutine, no timer and never reads a clock: it is advanced by an explicit Momentum.Tick whose dt (elapsed seconds) is supplied by the caller. Given the same configuration, bounds, release velocity and dt sequence it produces the same offsets on every platform and in every test — no time.Now, no wall-clock, so it is as reproducible under a unit test as it is under a 60 Hz present loop, and it is safe in the wasm and headless contexts where a real clock may be unavailable.
Physics model — exponential deceleration + damped-spring overscroll ¶
The fling uses EXPONENTIAL velocity decay: velocity retains a fixed FRACTION of itself per second, v(t) = v0 · Friction^t. This is the model behind the familiar iOS/UIScrollView "flywheel" glide (Android's OverScroller reaches for the same asymptotic-friction curve), and it is chosen over a constant deceleration (v decreasing by a fixed amount per second) for two concrete reasons:
- Feel. Constant deceleration stops abruptly — the last instant of motion is as fast as any other and then it simply ceases. Exponential decay eases out, spending most of its travel slowing gently, which is what reads as "momentum" rather than "a puck sliding to a halt".
- Frame-rate independence. Because retention COMPOUNDS, the total decay over an interval is Friction^dt regardless of how that interval is chopped into ticks: one 1/30 s tick and two 1/60 s ticks leave the velocity in the same place (Friction^(a+b) = Friction^a · Friction^b). A constant-deceleration model has to special-case the tick in which velocity crosses zero or it drifts with the frame rate. So a fling looks identical whether the host is rendering at 30, 60 or 120 Hz.
A tiny residual velocity would let the exponential creep forever, so once |velocity| falls below StopVelocity the fling settles to rest — the clean stop. Overscroll (when Momentum.Bounce is enabled) is a critically-tuned damped spring: past a bound the offset is pulled back by a spring force (−Stiffness·overshoot) opposed by damping (−Damping·velocity), so it decelerates, reverses and returns, and the instant it reaches the bound it snaps exactly onto it and rests — no residual jitter, no drift past home into the content. With Bounce disabled a fling that reaches a bound clamps exactly to it and stops, so a plain (non-touch) scroller keeps hard edges.
func NewMomentum ¶ added in v0.185.0
func NewMomentum() *Momentum
NewMomentum returns a ready-to-use single-axis engine with the default deceleration and (enabled) rubber-band spring tuning, bounds [0, 0] (a caller sets real bounds with SetBounds) and offset 0, at rest. Callers wanting a hard-edged desktop scroller set Bounce = false; callers wanting different physics may override any field or build a Momentum{} literal directly — the Tick logic depends only on the field values, not on how the struct was made.
func (*Momentum) BeginDrag ¶ added in v0.185.0
func (m *Momentum) BeginDrag()
BeginDrag starts a finger drag from the current offset. It stops any coast in progress and seeds the unclamped drag accumulator, so the subsequent DragBy deltas track from where the content actually is. A caller wires this to its touch-down / press.
func (*Momentum) DragBy ¶ added in v0.185.0
DragBy moves the content by delta (in offset units — positive scrolls toward max) for one drag sample and returns the new displayed offset. Inside the bounds the content tracks the finger one-for-one. Past a bound, with Bounce on, the movement is rubber-banded: the displayed overshoot is a diminishing function of the raw finger overshoot, asymptoting to MaxOverscroll, so the content stretches less and less the harder it is pulled — the classic elastic edge. With Bounce off it clamps hard at the bound. DragBy without a preceding BeginDrag begins one implicitly, so a caller may just start dragging.
func (*Momentum) EndDrag ¶ added in v0.185.0
EndDrag releases a finger drag with the given release velocity (px/s, signed like the offset). It is BeginDrag's partner: it clears the drag state and, via Fling, hands the released velocity to the coast/spring. A release with a tiny velocity while in bounds simply comes to rest; a release while overscrolled always springs home regardless of velocity.
func (*Momentum) Fling ¶ added in v0.185.0
Fling launches a coast from the current offset at the given release velocity (px/s, signed). If the offset is currently past a bound (a release mid- rubber-band) it enters the spring instead, so it always returns home. If it is within bounds it begins an exponential-deceleration fling — unless the speed is already at/below StopVelocity, in which case it just rests. A caller that tracks its own release velocity (see VelocityTracker) can call Fling directly without the Begin/EndDrag pair.
func (*Momentum) Offset ¶ added in v0.185.0
Offset returns the current scroll offset (may be fractional; may be slightly outside the bounds mid-overscroll).
func (*Momentum) OffsetInt ¶ added in v0.185.0
OffsetInt returns the current offset rounded to the nearest integer pixel, the form a pixel-blitting consumer paints with.
func (*Momentum) SetBounds ¶ added in v0.185.0
SetBounds sets the inclusive offset window [min, max] the engine clamps against. If the two are given out of order they are swapped, so a caller may pass them in either order. Bounds may be updated at any time (e.g. when the content is re-measured); the current offset is NOT forcibly re-clamped here, so a live spring keeps its overshoot — call SetOffset to re-clamp explicitly.
func (*Momentum) SetOffset ¶ added in v0.185.0
SetOffset jumps the offset to o, clamped to [min, max], and halts all motion (velocity 0, at rest, any drag cancelled). It is the way to seed or reset the scroll position — after a keyboard jump, a programmatic scroll-to, or a content re-measure that must re-clamp.
func (*Momentum) Settling ¶ added in v0.185.0
Settling reports whether the engine still owes motion — a coasting fling or a returning spring. A host schedules another frame while this is true and may stop once it goes false, mirroring Animator.Animating.
func (*Momentum) Stop ¶ added in v0.185.0
func (m *Momentum) Stop()
Stop halts any in-flight motion at the CURRENT offset without re-clamping it (a finger coming down on a coasting list stops it dead, wherever it is). Use SetOffset to also clamp.
func (*Momentum) Tick ¶ added in v0.185.0
Tick advances the engine by dt seconds and reports whether it is still settling (a host uses the return exactly like Settling to decide whether to schedule another frame). A non-positive dt, or a call at rest, is a no-op.
The step is semi-implicit: the velocity is updated first, then the offset is integrated with the NEW velocity. Doing it in this order keeps the fling unconditionally stable (it can never gain energy) and makes each tick an exact, closed-form function of the previous one.
type MomentumScroller ¶ added in v0.185.0
type MomentumScroller struct {
// View is the wrapped scroll view. Its OffsetX / OffsetY are driven by the
// two axis engines.
View *ScrollView
// AxisX, AxisY are the per-axis inertial engines. A caller may retune either
// (friction, bounce, spring) or disable one by leaving Bounce off and its
// motion clamped; both are live by default.
AxisX, AxisY *Momentum
// contains filtered or unexported fields
}
MomentumScroller drives a ScrollView from a host that routes touch itself, with its own clock.
A ScrollView now drives the same Momentum engines on its own — see ScrollView.Tick — so nothing has to be wrapped to get touch scrolling, rubber-band overscroll and spring-back. This type remains for the host that wants to own the timing, feed its own dt, or retune an axis: wrapping claims the view (see ScrollView.SetScrollDriver) so the two never both move it.
It is deliberately ADDITIVE: a ScrollView that is never wrapped keeps its own behaviour — wheel, keys, scrollbar drag and content pan all run through ScrollView.OnEvent as before — because this type modifies no ScrollView code and only ever runs when a host routes touch events to it explicitly. That is the gate: default (non-touch) scrolling is unchanged because nothing on the default path was touched.
It composes two engines, one per axis (exactly as ScrollView keeps OffsetX and OffsetY as independent clamps), reads the ScrollView's own geometry for the bounds so nothing is duplicated, and writes the resulting offsets back to the ScrollView's exported OffsetX / OffsetY — writing them directly (not via Scroll) so an overscrolled offset can sit briefly past a bound for the rubber-band, which Scroll would otherwise clamp away.
A host wires it up as: TouchDown on press, TouchMove on each drag sample, TouchUp on release, then Tick(dt) each frame while Settling reports true.
func NewMomentumScroller ¶ added in v0.185.0
func NewMomentumScroller(v *ScrollView) *MomentumScroller
NewMomentumScroller wraps v with a default-tuned inertial engine on each axis.
func (*MomentumScroller) Settling ¶ added in v0.185.0
func (s *MomentumScroller) Settling() bool
Settling reports whether either axis still owes motion.
func (*MomentumScroller) Tick ¶ added in v0.185.0
func (s *MomentumScroller) Tick(dt float64) bool
Tick advances both axes by dt seconds, writes the new offsets to the view, and reports whether either axis is still settling — the host schedules another frame while this is true.
func (*MomentumScroller) TouchDown ¶ added in v0.185.0
func (s *MomentumScroller) TouchDown(ev Event)
TouchDown starts a touch drag at ev's position. It stops any coast in progress, syncs the bounds and seeds each engine from the view's current offset, so the drag tracks from exactly where the content sits.
func (*MomentumScroller) TouchMove ¶ added in v0.185.0
func (s *MomentumScroller) TouchMove(ev Event, dt float64)
TouchMove feeds one drag sample taken dt seconds after the previous one. The content follows the finger — moving the finger up (decreasing Y) scrolls toward the end, an increasing offset — so the per-axis delta is the finger's movement negated, exactly the convention ScrollView's content pan uses. It is a no-op until a TouchDown has armed the drag.
func (*MomentumScroller) TouchUp ¶ added in v0.185.0
func (s *MomentumScroller) TouchUp()
TouchUp releases the drag, launching each axis into a coast at the velocity its tracker smoothed from the recent samples (a release while overscrolled springs home regardless). Harmless if no drag was active.
type MultiTouchRecognizer ¶ added in v0.184.0
type MultiTouchRecognizer struct {
// OnPinch fires on each anchor move while engaged with the current
// Scale (see MultiTouchState.Scale).
OnPinch func(scale float64)
// OnRotate fires on each anchor move while engaged with the current
// Rotation in radians (see MultiTouchState.Rotation).
OnRotate func(radians float64)
// OnPan fires on each anchor move while engaged with the centroid
// translation since begin (dx, dy).
OnPan func(dx, dy float64)
// OnMultiBegin fires when the gesture engages (a second contact lands,
// or the survivors of an anchor lift re-engage). The passed state is the
// begin reference: Scale 1, Rotation 0, Pan 0.
OnMultiBegin func(m MultiTouchState)
// OnMultiUpdate fires after OnPinch/OnRotate/OnPan on each anchor move,
// carrying the full current state.
OnMultiUpdate func(m MultiTouchState)
// OnMultiEnd fires when an anchor lifts, carrying the last state before
// the gesture ended.
OnMultiEnd func(m MultiTouchState)
// contains filtered or unexported fields
}
MultiTouchRecognizer turns a stream of EventTouchStart / EventTouchMove / EventTouchEnd events (see widget.go) carrying two or more concurrent contacts into the three canonical two-finger gestures — pinch (scale), rotate (angle) and two-finger pan (centroid translation). Like GestureRecognizer it is pure logic: it does not draw, hold a Widget reference or consult a clock, so any widget (or a host, ahead of dispatch) can embed one and feed it its full event stream unfiltered.
Contacts are identified by Event.Code, exactly as the single-touch GestureRecognizer identifies its one pointer; a MultiTouchRecognizer simply keeps every live contact instead of one. Contacts are held in arrival order.
Engagement. The gesture is disengaged while fewer than two contacts are down. The instant a second contact lands, the recognizer engages: it adopts the first two contacts (in arrival order) as its two "anchors", records their span, vector angle and centroid as the begin reference, and fires OnMultiBegin with a fresh state (Scale 1, Rotation 0, Pan 0). While engaged, every move of either anchor recomputes the state and fires OnPinch, OnRotate, OnPan and OnMultiUpdate (in that order). A move of any other (non-anchor) contact is tracked but changes nothing and fires nothing.
Disengagement. When an anchor lifts, the gesture ends: OnMultiEnd fires with the last state. If two or more contacts are still down (e.g. a third finger was resting, or more than two were down), the recognizer immediately re-engages on the first two survivors and fires a new OnMultiBegin — so a finger leaving a three-finger gesture cleanly hands off to the remaining pair. Lifting a non-anchor contact leaves the gesture untouched.
This is additive to and independent of GestureRecognizer: the single-touch tap / long-press / swipe path is unchanged. A host that wants both simply feeds the same events to both recognizers.
func NewMultiTouchRecognizer ¶ added in v0.184.0
func NewMultiTouchRecognizer() *MultiTouchRecognizer
NewMultiTouchRecognizer returns a ready-to-use MultiTouchRecognizer. It has no thresholds to configure — every anchor move reports the exact geometry — so the zero value works identically; the constructor exists only for parity with NewGestureRecognizer and for callers who prefer it.
func (*MultiTouchRecognizer) Engaged ¶ added in v0.184.0
func (g *MultiTouchRecognizer) Engaged() bool
Engaged reports whether a two-contact gesture is currently in progress (between an OnMultiBegin and its OnMultiEnd).
func (*MultiTouchRecognizer) Feed ¶ added in v0.184.0
func (g *MultiTouchRecognizer) Feed(ev Event)
Feed consumes one input event. Only EventTouchStart, EventTouchMove and EventTouchEnd are meaningful; every other kind is ignored so a host can feed its full event stream unfiltered.
func (*MultiTouchRecognizer) State ¶ added in v0.184.0
func (g *MultiTouchRecognizer) State() MultiTouchState
State returns the current gesture geometry. It is meaningful while Engaged is true and, after an OnMultiEnd, holds the last state the gesture reached.
type MultiTouchState ¶ added in v0.184.0
type MultiTouchState struct {
// Scale is the current distance between the two anchor contacts divided
// by that distance at begin — a pinch factor: > 1 spreading apart, < 1
// pinching together. When the two contacts engaged on the exact same
// point (a zero initial span, from which no ratio is defined) Scale is
// held at 1 for the life of the gesture.
Scale float64
// Rotation is the signed change, in radians, of the vector from the
// first anchor to the second since begin, normalized to (-Pi, Pi]:
// positive is the atan2 direction (counter-clockwise in a
// y-down-is-positive screen it reads as clockwise).
Rotation float64
// CenterX, CenterY are the current centroid (midpoint) of the two anchor
// contacts, in widget-local pixels.
CenterX float64
CenterY float64
// PanX, PanY are the centroid's translation since begin: CenterX minus
// the begin centroid, and likewise for Y — a two-finger pan.
PanX float64
PanY float64
// Span is the current distance between the two anchor contacts.
Span float64
}
MultiTouchState is the geometry of an engaged two-contact gesture, sampled relative to the moment the gesture engaged (its "begin"). Every field is a float64 so sub-pixel scale, radian angles and half-pixel centroids stay exact — the two integer contact positions are averaged, so a centroid can legitimately land on a .5.
At begin, Scale is 1, Rotation is 0 and PanX/PanY are 0 by construction; they then track continuously as the two anchor contacts move.
type Node ¶ added in v0.61.0
type Node struct {
Widget Widget // leaf content; when set, Layout/Children are ignored
Layout Layout // container content: how Children are arranged
Children []Node
Flex int // parent box layout: proportional weight
Size int // parent box layout: fixed main-axis size; border: band thickness
Region Region // parent border layout: which region this node occupies
// contains filtered or unexported fields
}
Node describes one piece of a UI tree. It is either a leaf (Widget set) or a container (Layout + Children). Flex/Size/Region are the layout configuration this node contributes to ITS PARENT's layout (the parent reads them when it adds this node as an Item); they are ignored on the root.
func BorderNode ¶ added in v0.61.0
BorderNode builds a border container node; children carry their Region via At.
func FitNode ¶ added in v0.61.0
FitNode builds a container node whose children each fill it (FitLayout).
func HBoxNode ¶ added in v0.61.0
HBoxNode builds a horizontal box container node. Like NewHBox it seeds the DefaultBoxSpacing gutter (via NewBoxLayout).
func VBoxNode ¶ added in v0.61.0
VBoxNode builds a vertical box container node. Like NewVBox it seeds the DefaultBoxSpacing gutter.
func (Node) Build ¶ added in v0.61.0
Build instantiates the node into a Widget: a leaf returns its Widget as-is; a non-leaf builds a *Container with the node's Layout and its recursively-built children (each added with its parent-layout config).
func (Node) Flexed ¶ added in v0.61.0
Flexed sets this node's parent-box flex weight and returns it (fluent).
type Notebook ¶
type Notebook struct {
Base
Tabs []NotebookTab
TabSide TabSide
// contains filtered or unexported fields
}
Notebook is a tabbed container. A n.stripH()-thick strip on the side chosen by TabSide (Top by default) hosts the tabs; the rest is the active page's body. For Top/Bottom the tabs run horizontally (each scaled(NotebookTabWidth) wide, shrunk to fit); for Left/Right they stack vertically (each NotebookTabStripH tall) and the strip SCROLLS: the mouse wheel over a vertical strip shifts the stacked tabs (clamped at both ends), and arrow-key tab switching scrolls the strip to keep the active tab in view, so a strip with more tabs than fit stays fully reachable. Clicking a tab Sets the Active Observable (notifying its subscribers).
The reactive active-tab index is MVVM-only: it lives in an unexported Observable exposed via Notebook.Active. Tabs and TabSide are set-once layout config and stay plain fields.
func NewNotebook ¶
func NewNotebook() *Notebook
NewNotebook returns an empty Notebook with no tabs + the Active Observable initialised to 0.
func (*Notebook) A11y ¶ added in v0.40.0
A11y reports the Notebook as a tablist named by its active tab.
func (*Notebook) Active ¶
func (n *Notebook) Active() *mvvm.Observable[int]
Active is the active tab index as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Active field. A tab click or a keyboard tab move Sets it; subscribers are notified. The accessor lazy-inits to 0 so a bare &Notebook{} is usable without a constructor.
func (*Notebook) AddTab ¶
AddTab appends a tab to the strip with label + the page widget shown when that tab is active.
func (*Notebook) Draw ¶
Draw paints the strip (on the chosen side) + the active page. The whole render is clipped to Bounds() so nothing ever escapes the widget's box (a defence-in-depth over tabW's fit-to-width), and the active page is clipped to its body rect so an oversized page cannot paint over the tab strip.
func (*Notebook) Focused ¶ added in v0.101.0
func (f *Notebook) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*Notebook) OnEvent ¶
OnEvent: a click on a tab (any side) selects it; a click in the body — or any non-click event — routes to the active page, translated into its local frame.
func (*Notebook) ScrollTabsBy ¶ added in v0.108.0
ScrollTabsBy shifts a vertical strip's scroll offset by delta tabs (negative scrolls up), clamped to [0, maxTabScroll()] and written back. A no-op for a horizontal strip.
func (*Notebook) SetFocused ¶ added in v0.101.0
func (f *Notebook) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
type NotebookTab ¶
NotebookTab is one entry in a Notebook. Label is the human title painted on the tab; Page is the widget shown when the tab is active.
type Notification ¶
type Notification struct {
Base
Text string
// Life is the number of Tick() calls remaining before the
// notification auto-hides. NotificationLife (~180 ≈ 3 s at 60 Hz)
// is a reasonable default; Show() re-arms it. Set directly for
// long-lived notifications (e.g. Life = 3600 for a persistent
// "network offline" banner the host manually Hide()s later).
Life int
// contains filtered or unexported fields
}
Notification is a transient toast — an auto-dismissing banner that slides in over the app's normal frame, holds for a few ticks, then hides itself. Cousin of Tooltip (both are informational overlays) but with three key differences:
- Notification is time-bounded (Tick decrements Life; hides at 0).
- Notification is positioned by the host (typically top-right or bottom-centre), NOT anchored to a source widget.
- Notification stays up while the user is doing something else — Tooltip requires the mouse to hover over its anchor.
The host drives Life via Tick() from its own animation loop (typically a rAF tick). One Notification instance can be reused — call Show(text) to re-arm it with a fresh Life budget.
func NewNotification ¶
func NewNotification(text string) *Notification
NewNotification builds a hidden notification with the given text + the default Life budget pre-armed (so a caller who forgets to call Show still gets a sensible time-out on the first Tick loop).
func (*Notification) A11y ¶ added in v0.40.0
func (n *Notification) A11y() A11yInfo
A11y reports the Notification as a status region named by its message.
func (*Notification) AnchorIn ¶ added in v0.33.0
func (n *Notification) AnchorIn(host Rect, corner Corner)
AnchorIn sizes the notification to its Text + positions it at corner of host, inset by NotificationMargin. A convenience over the host computing SetBounds by hand for the common "top-right"/"bottom-centre" placements the type is designed for.
func (*Notification) Draw ¶
func (n *Notification) Draw(p painter.Painter, theme *Theme)
Draw paints the toast when Visible. Filled Accent panel with a 1-px Border stroke, Text in the Background ink (inverted for contrast). Nothing drawn when hidden.
func (*Notification) Hide ¶
func (n *Notification) Hide()
Hide dismisses the notification immediately (independent of Life).
func (*Notification) Show ¶
func (n *Notification) Show(text string)
Show makes the notification visible + resets Life to NotificationLife. Bounds are auto-sized to the text width + the standard padding; the host is responsible for positioning (SetBounds) BEFORE calling Show — Show only refreshes the width to match the current Text.
func (*Notification) Tick ¶
func (n *Notification) Tick()
Tick decrements Life by 1. When Life reaches 0, the notification auto-hides. The host calls this from its animation loop; a rAF-driven caller ticks 60 Hz so NotificationLife = 180 ≈ 3 s. Callers wanting a paused notification (freeze on user hover) just skip the Tick during the pause.
func (*Notification) Visible ¶
func (n *Notification) Visible() *mvvm.Observable[bool]
Visible is the notification's reactive show/hide state as a shared mvvm.Observable; Show/Hide Set it. Lazily created, defaulting to hidden.
type Orientation ¶ added in v0.25.2
type Orientation int
Orientation selects whether a linear widget (Scale, RangeSlider, ProgressBar, LevelBar, …) runs across its width or up its height. Horizontal is the zero value, so an unset Orientation keeps the original left-to-right layout.
A vertical widget fills / travels from the BOTTOM up, matching how a physical meter, fader, or level indicator reads.
const ( // Horizontal runs left-to-right (the default). Horizontal Orientation = iota // Vertical runs bottom-to-top. Vertical )
type Overlay ¶ added in v0.18.0
Overlay is a z-ordered stacking container: a primary Content child that fills the bounds, plus a stack of Layers painted on top of it in order (the last Layer is topmost). It is the piece the widget model was missing so transient widgets -- Popover, Toast, Notification, Tooltip, ContextMenu -- can float above the main UI without the host arranging screen positions or z-order.
Unlike Stack (which shows exactly one page at a time), an Overlay draws every layer, and events route top-down: the topmost Layer whose HitTest covers the point handles the event; if none do, the event falls through to Content -- unless Modal is set, in which case a miss while any Layer is up is swallowed (a modal backdrop). Layers self-position via their own Bounds; only Content is resized to fill the Overlay.
Example ¶
ExampleOverlay stacks z-ordered layers above a primary child. Events route top-down; a Modal overlay swallows clicks that miss every layer.
package main
import (
"github.com/go-widgets/painter"
"github.com/go-widgets/toolkit"
)
// newSurface returns a PixelPainter over a fresh w×h RGBA buffer — the render
// target the examples draw into. A CellPainter would render the same widgets to
// a terminal grid instead.
func newSurface(w, h int) *painter.PixelPainter {
return painter.NewPixelPainter(make([]byte, 4*w*h), w, h)
}
func main() {
base := toolkit.NewLabel("main content")
ov := toolkit.NewOverlay(base)
ov.Push(toolkit.NewTooltip("floating layer"))
ov.SetBounds(toolkit.Rect{X: 0, Y: 0, W: 200, H: 120})
ov.Draw(newSurface(200, 120), toolkit.DefaultLight())
}
Output:
func NewOverlay ¶ added in v0.18.0
NewOverlay builds an Overlay around the given primary child (which may be nil and set later).
func (*Overlay) A11y ¶ added in v0.40.0
A11y reports the Overlay as a group carrying its modal state.
func (*Overlay) Children ¶ added in v0.137.0
Children yields the content first and then the layers, bottom to top, which is the order they are painted in.
func (*Overlay) Clear ¶ added in v0.18.0
func (o *Overlay) Clear()
Clear removes every layer, leaving just the Content.
func (*Overlay) OnEvent ¶ added in v0.18.0
OnEvent routes to the topmost layer whose HitTest covers the point; failing that, to Content -- or, when Modal and a layer is present, nowhere (the backdrop swallows the click). Coordinates are passed through unchanged (an Overlay is a surface-frame container, like Paned).
func (*Overlay) Pop ¶ added in v0.18.0
Pop removes and returns the topmost layer, or nil when there are none.
type PagedMode ¶ added in v0.200.0
type PagedMode int
PagedMode selects PagedView's layout: a continuous vertical stack of page cards, or a single centred page with prev/next paging.
type PagedView ¶ added in v0.200.0
type PagedView struct {
Base
// contains filtered or unexported fields
}
PagedView is a reusable document / page viewer: it shows a sequence of pre-rendered page bitmaps with two layout modes, uniform zoom, and full keyboard + mouse navigation. It OWNS an inner ScrollView (the scrollable pane) and its own toolbar strip, so a host wires only the pages in and reads the navigation state out — it never hand-rolls paging, zoom or a toolbar.
Content is a []*image.RGBA of page bitmaps at natural size (see PagedView.SetPages); PagedView never knows how they were rasterised (SVG, LaTeX, PDF — all the caller's concern). It scales every page's blit uniformly by PagedView.Zoom.
The two modes (PagedView.Mode):
- PagedContinuous stacks every page vertically as a card (a subtle drop shadow + 1px border, a gap between cards), the whole stack scrollable.
- PagedPaginated shows ONE page centred in the pane; the toolbar grows a prev / "n / N" / next group, and the current page still scrolls within the pane when it is taller than the pane (a PDF single-page-continuous feel: the wheel scrolls WITHIN the page first and only flips at the edge).
All reactive state is MVVM-only, exposed through shared mvvm.Observable accessors (there are no settable state fields, mirroring DropDown / Switch): PagedView.Mode, PagedView.CurrentPage (1-based), and PagedView.Zoom (percent). CurrentPage updates identically whether the change came from a toolbar button, a key, or the wheel.
func NewPagedView ¶ added in v0.200.0
NewPagedView builds a PagedView over the given pages (any of which may be nil, which is skipped when drawn). It starts in PagedContinuous mode at 100% zoom on page 1.
func (*PagedView) A11y ¶ added in v0.200.0
A11y describes the widget as a document, with the current page position as its value ("n / N") and — when there is at least one page — a machine-readable aria-valuemin/max/now triple over [1, PageCount].
func (*PagedView) CurrentPage ¶ added in v0.200.0
func (pv *PagedView) CurrentPage() *mvvm.Observable[int]
CurrentPage is the 1-based current page as a shared mvvm.Observable: 1 for the first page, PagedView.PageCount for the last, and 1 when there are no pages. A toolbar button, a nav key, or a wheel edge-flip all Set it through the same clamp; subscribers are notified on change.
func (*PagedView) Draw ¶ added in v0.200.0
Draw paints the toolbar strip (buttons + read-outs) then the scrollable pane of page cards, and finally the focus ring.
func (*PagedView) Focused ¶ added in v0.200.0
func (f *PagedView) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*PagedView) HitTest ¶ added in v0.200.0
HitTest covers the full bounds (the toolbar + pane are both interactive).
func (*PagedView) Mode ¶ added in v0.200.0
func (pv *PagedView) Mode() *mvvm.Observable[PagedMode]
Mode is the layout mode as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Mode field. The two toolbar mode buttons Set it; subscribers are notified on change.
func (*PagedView) OnEvent ¶ added in v0.200.0
OnEvent drives the widget: a toolbar click routes to the button under it; a wheel scroll pages-or-scrolls (see onScroll); a nav key steps the page; every other pointer event (drag / release for the pane's pan + scrollbars) forwards to the inner ScrollView. A Disabled PagedView ignores everything.
func (*PagedView) PageAt ¶ added in v0.202.0
PageAt maps a WIDGET-LOCAL pointer position — (x, y) as delivered to PagedView.OnEvent, i.e. relative to the widget's own bounds — to the page under it and the point WITHIN that page's NATURAL (un-zoomed) image pixels. It accounts for the toolbar strip height, the layout mode (a continuous stack vs a single centred page), the zoom factor (divided back out so localX / localY are natural pixels), the scroll offset, and each card's own position and the gaps between them. The returned page is 1-based, matching PagedView.CurrentPage.
ok is false when the point falls in the toolbar strip, in a gap between cards, over the scrollbar gutter, or outside every page (including every case where there are no pages) — the localX / localY are then zero. This is the seam that turns a click on a rendered page into a (page, natural-point) a host can look up against its own source map. It is the exact inverse of PagedView.ScrollToPage's vertical mapping (they share naturalToContentY / contentYToNatural).
func (*PagedView) ScrollOffset ¶ added in v0.202.0
ScrollOffset reports the inner ScrollView's current content offset, in DEVICE pixels: how far the page content has scrolled right (x) and down (y) inside the pane. A host persists it to restore the scroll position across a reload, or pairs it with PagedView.PageAt / PagedView.ScrollToPage to reason about what is currently visible. It is a thin passthrough of the owned ScrollView's OffsetX / OffsetY.
func (*PagedView) ScrollToPage ¶ added in v0.202.0
ScrollToPage scrolls the pane so that natural-coordinate localY of the given 1-based page is brought to the TOP of the viewport (clamped to the legal scroll range at both ends). It is the vertical inverse of PagedView.PageAt: a round-trip — PageAt to read a point, then ScrollToPage with the page + localY it returned — lands that point at the top of the pane to within a pixel.
In PagedPaginated mode it also makes page the current page (so the single displayed card is that page). A page outside [1, PageCount] or an empty view is a no-op. This is the seam that turns an editor caret move into a scroll: a host maps the caret's source line to (page, natural-Y) via its own source map and calls this to bring that line into view.
func (*PagedView) SetBounds ¶ added in v0.200.0
SetBounds records the placement and relays out the pane + toolbar so events arriving before the first Draw already hit-test against the right geometry.
func (*PagedView) SetFocused ¶ added in v0.200.0
func (f *PagedView) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
func (*PagedView) SetPages ¶ added in v0.200.0
SetPages replaces the page bitmaps (each a natural-size RGBA the caller rasterised; nil entries are skipped when drawn). CurrentPage is clamped back into range: to the last page if it now points past the end, to 1 if the set is empty or the page was below 1.
func (*PagedView) Zoom ¶ added in v0.200.0
func (pv *PagedView) Zoom() *mvvm.Observable[int]
Zoom is the zoom level in PERCENT as a shared mvvm.Observable (100 = natural size). The zoom-in / zoom-out / fit buttons Set it (clamped to [pagedZoomMin, pagedZoomMax]); subscribers are notified on change.
type Pagination ¶ added in v0.8.0
type Pagination struct {
Base
// Total is the page count (config). The reactive current page is MVVM-only:
// it lives in an unexported Observable exposed via [Pagination.Current].
Total int
// contains filtered or unexported fields
}
Pagination is a page-navigator strip: a "<" prev button, a series of page-number buttons, and a ">" next button. Clicking a page number Sets the Current Observable to that page; clicking prev or next steps by one (clamped). When Current is at either extreme the corresponding step button renders in a disabled tone and swallows clicks.
When Total exceeds paginationMaxButtons the middle of the range collapses into a "1 ... k-1 k k+1 ... Total" window so the widget's footprint stays bounded. Non-numeric window slots ("...") are drawn but not clickable — the hit-test skips them.
func NewPagination ¶ added in v0.8.0
func NewPagination(current, total int) *Pagination
NewPagination builds a Pagination with the given current and total page counts. Current is clamped to [1, Total] when Total > 0, and to 1 when Total <= 0 (the widget then renders empty and swallows events).
func (*Pagination) A11y ¶ added in v0.40.0
func (p *Pagination) A11y() A11yInfo
A11y reports the Pagination as navigation carrying its "current/total" page position.
func (*Pagination) Current ¶ added in v0.8.0
func (pg *Pagination) Current() *mvvm.Observable[int]
Current is the active page as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Current field. A click on prev / next / a page number, or an arrow / Home / End key, Sets it (clamped to [1, Total]); subscribers are notified on change. A bare &Pagination{} lazily initialises the Observable to 0 on first access.
func (*Pagination) Draw ¶ added in v0.8.0
func (pg *Pagination) Draw(p painter.Painter, theme *Theme)
Draw paints the widget body, each button in its correct tint, and the button labels. Total <= 0 paints only the body — no buttons. Bounds that cannot accommodate a single button are treated the same as Total <= 0 so a mis-sized Pagination degrades gracefully.
func (*Pagination) Focused ¶ added in v0.101.0
func (f *Pagination) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*Pagination) OnEvent ¶ added in v0.8.0
func (pg *Pagination) OnEvent(ev Event)
OnEvent routes an EventClick to whichever button contains (X, Y). Prev/next step Current by one when enabled; a numeric slot sets Current to its page. Ellipsis slots and out-of-band clicks are no-ops.
func (*Pagination) SetFocused ¶ added in v0.101.0
func (f *Pagination) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
type PagingToolbar ¶ added in v0.80.0
type PagingToolbar struct {
Base
// PageCount is the total number of pages (config).
PageCount int
// ShowRefresh adds a trailing reload button that fires OnRefresh (config).
ShowRefresh bool
// OnRefresh fires when the Refresh button is clicked. Nil is safe. This is an
// action seam, not reactive state, so it stays a plain func field.
OnRefresh func()
// contains filtered or unexported fields
}
PagingToolbar is a record-navigation toolbar: First (|<), Prev (<), a "Page N of M" indicator, Next (>), Last (>|) and an optional Refresh button. Navigation clamps to [1, PageCount] and Sets the Page Observable only when the page actually changes; the extreme buttons render in a disabled tone and swallow clicks at the ends of the range. Unlike Pagination (a strip of numbered page buttons), this is the compact toolbar Ext's PagingToolbar provides for a data grid's footer.
func NewPagingToolbar ¶ added in v0.80.0
func NewPagingToolbar(page, count int) *PagingToolbar
NewPagingToolbar builds a toolbar at the given page/count. page is clamped into [1, count] and count to a floor of 1 (an empty grid still reads as "Page 1 of 1" with every nav button disabled).
func (*PagingToolbar) A11y ¶ added in v0.130.0
func (t *PagingToolbar) A11y() A11yInfo
A11y reports the PagingToolbar as a toolbar whose value is the position it controls — the one thing a reader needs from it.
func (*PagingToolbar) Draw ¶ added in v0.80.0
func (pt *PagingToolbar) Draw(p painter.Painter, theme *Theme)
Draw paints the toolbar body, buttons (extreme ones dimmed at the range ends), the "Page N of M" indicator and the optional Refresh button.
func (*PagingToolbar) OnEvent ¶ added in v0.80.0
func (pt *PagingToolbar) OnEvent(ev Event)
OnEvent routes an EventClick to whichever element contains it: First/Last jump to the ends, Prev/Next step by one (all clamped, Setting the Page Observable only on an actual change), Refresh fires OnRefresh. The indicator + gaps are inert.
func (*PagingToolbar) Page ¶ added in v0.80.0
func (pt *PagingToolbar) Page() *mvvm.Observable[int]
Page is the 1-based current page as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Page field. A First / Prev / Next / Last click Sets it (clamped to [1, PageCount]); subscribers are notified on change. A bare &PagingToolbar{} lazily initialises the Observable to 0 on first access.
type PaletteCommand ¶ added in v0.35.0
type PaletteCommand struct {
Label string
Action func()
}
PaletteCommand is one entry in a CommandPalette: a human-readable Label the user searches for and an Action to run when it is chosen. Action may be nil (e.g. a placeholder or disabled entry); activating such a command simply dismisses the palette without running anything.
type Paned ¶
type Paned struct {
Base
First, Second Widget
Orientation int
OnPositionChanged func(pos int)
// contains filtered or unexported fields
}
Paned splits its bounds into two child regions separated by a PanedHandleW-px draggable handle. Position is the handle's offset (in pixels) from the leading edge of First; orientation chooses whether that's measured along X (PanedHorizontal) or Y (PanedVertical).
The toolkit's full event model is click-only in v0.2, so drag is exposed via direct OnDragHandle helpers callers wire to their own mouse-tracking state.
func NewHPaned ¶
NewHPaned builds a horizontal Paned with a sensible default Position (mid-bounds, applied at first SetBounds).
func (*Paned) A11y ¶ added in v0.40.0
A11y reports the Paned as a plain grouping container for its two panes.
func (*Paned) MoveHandle ¶
MoveHandle slides the splitter to pos (clamped) and re-lays out children. Fires OnPositionChanged with the new value.
func (*Paned) Position ¶
func (p *Paned) Position() *mvvm.Observable[int]
Position is reactive state as a shared mvvm.Observable; edits Set it. Lazily created.
type PieChart ¶ added in v0.14.0
type PieChart struct {
Base
Values []float64
Colors []RGBA // optional per-slice palette override; cycles by index
// contains filtered or unexported fields
}
PieChart plots proportional Values as wedges of a filled disc -- the part-of- whole complement to LineChart/BarChart. Wedges start at 12 o'clock and run clockwise, sized by each value's share of the total. Colours cycle through a built-in categorical palette unless Colors is set. Display-only.
It fills each wedge per-pixel over painter.Painter's putPixel (no arc primitive needed), so it renders as pixels (WUI/GUI) or promoted cells (TUI). A zero or empty total draws nothing.
Example ¶
ExamplePieChart fills a disc with one proportional wedge per value.
package main
import (
"github.com/go-widgets/painter"
"github.com/go-widgets/toolkit"
)
// newSurface returns a PixelPainter over a fresh w×h RGBA buffer — the render
// target the examples draw into. A CellPainter would render the same widgets to
// a terminal grid instead.
func newSurface(w, h int) *painter.PixelPainter {
return painter.NewPixelPainter(make([]byte, 4*w*h), w, h)
}
func main() {
chart := toolkit.NewPieChart([]float64{3, 5, 2, 4})
chart.SetBounds(toolkit.Rect{X: 0, Y: 0, W: 120, H: 120})
chart.Draw(newSurface(120, 120), toolkit.DefaultLight())
}
Output:
func NewPieChart ¶ added in v0.14.0
NewPieChart builds a PieChart over the given values with the default palette.
func (*PieChart) A11y ¶ added in v0.40.0
A11y reports the PieChart as an img carrying its slice count.
func (*PieChart) Draw ¶ added in v0.14.0
Draw fills the disc, colouring each pixel by the wedge its angle falls in.
func (*PieChart) Hover ¶ added in v0.90.0
func (c *PieChart) Hover() *mvvm.Observable[bool]
Hover is the reactive slice-outline toggle as a shared mvvm.Observable; false draws no outline. Lazily created, defaulting to off.
func (*PieChart) HoverIndex ¶ added in v0.90.0
func (c *PieChart) HoverIndex() *mvvm.Observable[int]
HoverIndex is the reactive hovered slice index as a shared mvvm.Observable. Lazily created, defaulting to 0.
func (*PieChart) OnEvent ¶ added in v0.100.0
OnEvent outlines the pie slice under the pointer, clearing when it leaves.
type Popover ¶ added in v0.8.0
Popover is a Visible floating container for a single child widget, modelled on GTK 4's Popover -- a rectangular panel with a border stroke and an optional Title header. Popover is the natural home for dropdown menus, ephemeral pickers and detail overlays that the host wants to show and hide without tearing down and rebuilding the underlying child.
Distinct from Card (a passive display container) in two ways:
- Popover has a Visible toggle: the whole widget short-circuits Draw + OnEvent when hidden so the host does not have to unlink the child from the tree between showings.
- Popover forwards input events to its child with coordinates translated for the pad + optional title header, so the child sees widget-local coords in its own frame.
func NewPopover ¶ added in v0.8.0
NewPopover constructs a hidden Popover wrapping child. child may be nil, in which case the Popover renders as an empty framed panel.
func (*Popover) Draw ¶ added in v0.8.0
Draw paints the surface fill + border, optionally draws the Title at the top-left inside PopoverPad, then draws Child (if non-nil) into the inset child rect. Nothing drawn when !Visible.
func (*Popover) OnEvent ¶ added in v0.8.0
OnEvent forwards the event to Child with coordinates translated into the child's local frame. No-op when !Visible or Child is nil. Mirrors the translateEvent pattern used by HBox / VBox / Grid.
func (*Popover) Visible ¶ added in v0.8.0
func (p *Popover) Visible() *mvvm.Observable[bool]
Visible is the popover's reactive show/hide toggle as a shared mvvm.Observable; the host Sets it to show or dismiss the panel. Lazily created, defaulting to hidden.
type PostCard ¶ added in v0.157.0
type PostCard struct {
Base
// Pill is the coloured tag text (e.g. the source). Empty draws no pill.
Pill string
// PillColor is the pill body colour; the zero value (A==0) lets the pill fall
// back to Theme.Accent.
PillColor RGBA
// PillInk is the pill text colour. The zero value (A==0) derives a readable
// ink from PillColor (near-black on a light pill, near-white on a dark one),
// or falls back to Theme.Background when PillColor is also unset.
PillInk RGBA
// Subtitle is the muted text beside the pill (e.g. the channel). Empty hides
// it; when both Pill and Subtitle are empty the whole badge row collapses.
Subtitle string
// Title is the headline, wrapped to the content width over as many lines as it
// needs, capped at MaxTitleLines with the last shown line ellipsised. Empty
// draws no title.
Title string
// Meta is the muted footer line (e.g. "▲128 · 3h"). Empty hides it.
Meta string
// Thumbnail is the optional lead image; nil drops the whole thumbnail column.
// It is scaled to fit its box preserving aspect ratio.
Thumbnail *image.RGBA
// ThumbW / ThumbH size the thumbnail column; a non-positive value selects the
// default (DefaultPostCardThumbW / DefaultPostCardThumbH).
ThumbW, ThumbH int
// ThumbPlaceholder is the muted label drawn in the thumbnail box when the post
// declares media but no decoded Thumbnail has landed yet (e.g. "image", "video").
// A non-empty value reserves the thumbnail column even while Thumbnail is nil, so
// the card does not reflow (grow a column) when the image finally arrives; the
// box shows the label centred on the SurfaceAlt ground until then, reproducing
// the historical "loading" card. Empty draws no column unless Thumbnail is set.
ThumbPlaceholder string
// MaxTitleLines caps the wrapped title; a non-positive value selects the
// default (DefaultPostCardTitleLines).
MaxTitleLines int
// Per-element fonts give the card its type hierarchy — a larger bold title
// over a smaller muted subtitle / meta, and a small pill — instead of one
// uniform size. Each is optional: a nil font falls back to the card's
// EffectiveFont (Base.Font, else the package font), so the zero value keeps
// the previous single-font behaviour. TitleFont sizes the headline lines,
// SubtitleFont the channel text, MetaFont the footer, PillFont the badge label.
TitleFont, SubtitleFont, MetaFont, PillFont Font
// contains filtered or unexported fields
}
PostCard is a rich feed row: a coloured pill (a source / category tag) with a muted subtitle beside it, a wrapped multi-line title, a muted meta line pinned to the bottom, and an optional lead thumbnail pinned to the top of a right-hand column. It is the generic form of a social / news / discussion card — an app maps its own item onto the fields (Pill = source, Subtitle = channel, Title = headline, Meta = score · age, Thumbnail = cached image) rather than hand-drawing the row.
Layout (inside the CardPadX/Y inset):
┌──────────────────────────────┬────────┐ │ [Pill] subtitle │ │ ← badge row: pill + muted subtitle │ A wrapped title over as many │ thumb │ ← Title, one Label per wrapped line │ lines as it needs to fit │ │ │ │ │ ← flex spacer pushes meta down │ meta · line · here │ │ ← Meta (muted), bottom-pinned └──────────────────────────────┴────────┘
The content column is an HBox flex child; the thumbnail (when present) is a fixed column whose image is pinned to the top, so a tall (multi-line) card leaves blank space below the thumbnail rather than stretching it. The subtitle, each wrapped title line and the meta line are real Labels exposed through Children, so CollectRuns lifts them out as selectable text runs.
PostCard is passive content: it lays out and paints itself and reports its exact height through Measure(width); a feed list (CardList / VirtualList) puts selection / hover / disabled affordances on top.
func NewPostCard ¶ added in v0.157.0
NewPostCard builds a PostCard from its four text fields. Set PillColor, Thumbnail and the sizing fields afterwards as needed.
func (*PostCard) Children ¶ added in v0.157.0
Children yields the card's selectable Labels in visual order — the subtitle, each wrapped title line, then the meta line — so CollectRuns lifts them out as text runs and a11y / selection walks reach them. The pill and thumbnail are decoration and are not returned. Calling Children re-assembles the tree at the card's current bounds, so the returned Labels carry laid-out positions.
func (*PostCard) Draw ¶ added in v0.157.0
Draw paints the shared card frame, then the assembled content and thumbnail. The muted inks (subtitle, meta) and the title ink are theme-derived here, at paint time. Content fills exactly Measure(Bounds().W): the same layout drives both.
type ProgressBar ¶
type ProgressBar struct {
Base
Label string
Orientation Orientation
Indeterminate bool
Phase float64 // 0..1, only used when Indeterminate
// contains filtered or unexported fields
}
ProgressBar is a bar with a filled portion proportional to Fraction in [0,1]. Orientation picks the fill direction: Horizontal (default) fills left→right, Vertical fills bottom→top. An optional Label is centred over the bar in Theme.OnSurface ink (drawn for the horizontal orientation, where it fits).
When Indeterminate is set the bar ignores Fraction and instead animates a short chunk sliding along the track, driven by Phase (0..1, advance it from the host frame loop like a Spinner) — for work whose completion is unknown (a page fetch, an open-ended request).
func NewProgressBar ¶
func NewProgressBar() *ProgressBar
NewProgressBar builds an empty (Fraction=0) ProgressBar with no label.
func (*ProgressBar) A11y ¶ added in v0.40.0
func (p *ProgressBar) A11y() A11yInfo
A11y reports the ProgressBar as a progressbar carrying its fraction as a whole-number percentage, plus the numeric range triple over the fraction's natural [0, 1] span (Now is the raw fraction).
func (*ProgressBar) Animating ¶ added in v0.155.0
func (pb *ProgressBar) Animating() bool
Animating reports whether the bar still needs frames: true exactly when it is Indeterminate (a determinate bar is a static fill and needs no repaint).
func (*ProgressBar) Draw ¶
func (pb *ProgressBar) Draw(p painter.Painter, theme *Theme)
Draw paints border + track + fill + optional centered label.
func (*ProgressBar) Fraction ¶
func (pb *ProgressBar) Fraction() *mvvm.Observable[float64]
Fraction is the determinate fill level in [0,1] as a shared mvvm.Observable: SetFraction (or a direct Set) drives it and Draw reads it live. Lazily created, defaulting to 0 (empty). Ignored while Indeterminate.
func (*ProgressBar) SetFraction ¶
func (p *ProgressBar) SetFraction(f float64)
SetFraction clamps + assigns Fraction. 0 = empty, 1 = full.
func (*ProgressBar) Tick ¶ added in v0.155.0
func (pb *ProgressBar) Tick(deltaSeconds float64)
Tick advances the indeterminate sweep by deltaSeconds, wrapping Phase modulo 1 so it stays bounded. A determinate bar (the default) has no animation, so Tick is a no-op for it — matching what Animating reports. Together they make an indeterminate ProgressBar an Animator, driven by TickTree / TreeAnimating.
type ProgressCircle ¶ added in v0.9.0
type ProgressCircle struct {
Base
// contains filtered or unexported fields
}
ProgressCircle is a fake-circular progress indicator: a rounded square track with a "cup-filling" band that rises from the bottom as Fraction grows from 0 to 1. Not a true arc — the pixel-blitting toolkit does not carry a curve rasteriser — but it conveys the same "circular progress" intent at the same abstraction level as Spinner (a rotating radial line) and Avatar (a rounded square).
Layout: an outer square filled in theme.SurfaceAlt (the "track"), an inner square inset by scaled(ProgressCircleStroke) on all sides filled in theme.Surface (the "hole" that the caption sits in), and a horizontal Accent band inside the ring whose height is proportional to Fraction. The band grows from the bottom edge upward for the familiar "filling up" visual. The percentage caption ("XX%") is drawn in theme.OnSurface centred inside the inner square.
func NewProgressCircle ¶ added in v0.9.0
func NewProgressCircle() *ProgressCircle
NewProgressCircle constructs a ProgressCircle at Fraction=0.
func (*ProgressCircle) A11y ¶ added in v0.40.0
func (p *ProgressCircle) A11y() A11yInfo
A11y reports the ProgressCircle as a progressbar carrying its fraction as a whole-number percentage.
func (*ProgressCircle) Draw ¶ added in v0.9.0
func (pc *ProgressCircle) Draw(p painter.Painter, theme *Theme)
Draw paints the track, the hole, the fill band, and the centred percentage caption. Draw clamps Fraction defensively so an out-of-range Set still renders a valid frame.
func (*ProgressCircle) Fraction ¶ added in v0.9.0
func (pc *ProgressCircle) Fraction() *mvvm.Observable[float64]
Fraction is the fill fraction in [0, 1] as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Fraction field. 0 = empty, 1 = full; Draw clamps defensively so an out-of-range Set still renders a valid frame.
type PropertyGrid ¶ added in v0.80.0
type PropertyGrid struct {
Base
// OnChange fires when a Value cell edit is committed (Enter): name is the
// edited property, value its new text. Nil is safe.
OnChange func(name, value string)
// contains filtered or unexported fields
}
PropertyGrid is a two-column Name/Value grid for viewing and editing a set of named properties: the Value column is inline-editable, and committing an edit fires OnChange with the property's name and new value. It composes an editable Table (see NewTable / TableColumn.Editable), so it inherits the Table's rendering, selection, windowed scrolling and per-cell editing -- PropertyGrid just adds the property-oriented API on top (Add / SetValue / Value, keyed by name rather than row index).
The Name column is read-only; only Value cells open an editor on click.
func NewPropertyGrid ¶ added in v0.80.0
func NewPropertyGrid() *PropertyGrid
NewPropertyGrid builds an empty property grid (Name | Value, Value editable). Populate it with Add / SetValue.
func (*PropertyGrid) A11y ¶ added in v0.105.0
func (pg *PropertyGrid) A11y() A11yInfo
A11y reports the PropertyGrid as a grid carrying the selected property's name, or "" with no selection (or before the backing table is built).
func (*PropertyGrid) Add ¶ added in v0.80.0
func (pg *PropertyGrid) Add(name, value string)
Add appends a property row. Duplicate names are allowed (Value/SetValue address the first match), mirroring how a raw Table allows duplicate rows.
func (*PropertyGrid) Clear ¶ added in v0.80.0
func (pg *PropertyGrid) Clear()
Clear removes every property.
func (*PropertyGrid) Draw ¶ added in v0.80.0
func (pg *PropertyGrid) Draw(p painter.Painter, theme *Theme)
Draw paints the grid via its backing Table.
func (*PropertyGrid) OnEvent ¶ added in v0.80.0
func (pg *PropertyGrid) OnEvent(ev Event)
OnEvent forwards to the backing Table (selection, and Value-cell editing).
func (*PropertyGrid) RemoveAt ¶ added in v0.87.0
func (pg *PropertyGrid) RemoveAt(i int)
RemoveAt removes the property at row index i, keeping the name index and the backing Table in sync and clearing a now-stale selection. An out-of-range i is a no-op. Exposed so a host can implement a "delete property" menu action (hit-test the row with PropertyGrid.Table().RowAt).
func (*PropertyGrid) SetBounds ¶ added in v0.80.0
func (pg *PropertyGrid) SetBounds(r Rect)
SetBounds positions the grid and its backing Table.
func (*PropertyGrid) SetValue ¶ added in v0.80.0
func (pg *PropertyGrid) SetValue(name, value string)
SetValue updates the named property's value in place, or appends it if it is not present yet.
func (*PropertyGrid) Table ¶ added in v0.80.0
func (pg *PropertyGrid) Table() *Table
Table exposes the underlying Table for advanced configuration (column widths, RowIcon, sorting, ...). Mutating Rows directly is not supported -- go through Add / SetValue so the name index stays in sync.
func (*PropertyGrid) Value ¶ added in v0.80.0
func (pg *PropertyGrid) Value(name string) string
Value returns the current value of the named property, or "" if there is no such property.
type PullState ¶ added in v0.187.0
type PullState int
PullState is the position of a PullToRefresh in its idle→pulling→armed→ refreshing→idle state machine.
const ( // PullIdle is the resting state: nothing revealed, no contact consuming a // pull. A spring-back to home also reports PullIdle while it animates. PullIdle PullState = iota // PullPulling is an in-progress pull that has not yet reached the arm // threshold; releasing springs back home without refreshing. PullPulling // PullArmed is a pull that has passed the threshold; releasing fires // OnRefresh. PullArmed // PullRefreshing is the post-release spinning state, held until Done. PullRefreshing )
type PullToRefresh ¶ added in v0.187.0
type PullToRefresh struct {
Base
// Child is the wrapped, scrollable content (typically a [ScrollView]). It
// fills the container's bounds and is painted pushed DOWN by the current
// pull distance, revealing the indicator in the gap above it.
Child Widget
// OnRefresh fires once, on release, when a pull that reached the arm
// threshold is let go — the app's cue to start reloading. It also fires from
// a programmatic [PullToRefresh.Refresh]. The widget then stays in
// [PullRefreshing] until the app calls [PullToRefresh.Done]. Nil is safe.
OnRefresh func()
// AtTop reports whether the wrapped content is scrolled to its top, i.e.
// whether a downward drag should pull-to-refresh rather than scroll the
// child. When nil the container is treated as always at the top (a pure pull
// surface). Wrapping a [ScrollView] sv, wire it to
// ptr.AtTop = func() bool { return sv.OffsetY <= 0 }
AtTop func() bool
// Threshold is the pull distance, in LOGICAL pixels, past the top that a
// drag must reach to arm a refresh. Zero (or negative) selects the default
// (pullThresholdLogical). It is scaled by [scaled] to device pixels, so it
// grows with both HiDPI and touch density.
Threshold int
// Style selects the refreshing spinner's look; the zero value is the
// default hand style (see [SpinnerStyle]).
Style SpinnerStyle
// contains filtered or unexported fields
}
PullToRefresh is a container that wraps scrollable content and adds the signature "pull down past the top to reload" touch gesture. When the wrapped content is already scrolled to its top and a finger drags DOWN past that bound, the content is pushed down against a rubber band (reusing the same Momentum overscroll model that drives touch flinging) and a refresh affordance — a chevron while pulling, a Spinner once refreshing — is revealed in the gap that opens at the top. Pull past a threshold and release and the widget fires PullToRefresh.OnRefresh and stays in a spinning refreshing state until the app declares the reload finished by calling PullToRefresh.Done. Release before the threshold and the content simply springs back home, no refresh.
State machine ¶
The interaction is a four-state machine — PullIdle → PullPulling → PullArmed → PullRefreshing → PullIdle — advanced entirely by explicit, clock-free calls so it is as reproducible under a unit test as under a 60 Hz present loop (exactly like Momentum, Spinner and GestureRecognizer):
- PullIdle: at rest, nothing revealed. A touch-down at the top arms a candidate pull without leaving idle.
- PullPulling: a finger is dragging the content down but has not yet pulled far enough; the revealed pull distance is below the arm threshold. Releasing here springs back to idle with no refresh.
- PullArmed: the pull has passed the threshold; releasing now WILL refresh. The affordance flips (chevron turns up, tinted Accent) to signal "release to refresh".
- PullRefreshing: PullToRefresh.OnRefresh has fired, the spinner spins, and the content rests pulled-down by the indicator's rest height until PullToRefresh.Done returns it home.
How it consumes momentum and density ¶
The rubber-band pull and every spring-back are one embedded single-axis Momentum engine whose offset IS the pull distance: dragging feeds it Momentum.DragBy (so the pull stretches with the same diminishing-returns elastic curve as an overscrolled list, asymptoting to a hard wall), and each release/Done sets the engine's bounds to the target hold distance and flings it there under the damped spring, which lands exactly on that distance and rests. No clock, no goroutine — PullToRefresh.Tick advances the spring and the spinner from a caller-supplied dt, making the widget an Animator.
Every metric is density-aware: the arm threshold, the refreshing rest height and the indicator box all pass through [scaled] (HiDPI × touch density) and the indicator/rest heights additionally through TouchTarget, so under DensityTouch the affordance is a comfortable fingertip target and under the DensityCompact desktop default it is byte-for-byte its base size.
func NewPullToRefresh ¶ added in v0.187.0
func NewPullToRefresh(child Widget) *PullToRefresh
NewPullToRefresh wraps child in a PullToRefresh at rest (PullIdle) with the default arm threshold and a default-styled spinner. A caller wires OnRefresh (and, when wrapping a scroll view, AtTop) and drives the touch gesture with TouchDown/TouchMove/TouchUp plus Tick each frame.
func (*PullToRefresh) A11y ¶ added in v0.187.0
func (w *PullToRefresh) A11y() A11yInfo
A11y reports the container as a group, marked busy while it is refreshing so a screen reader announces the in-progress reload.
func (*PullToRefresh) Animating ¶ added in v0.187.0
func (w *PullToRefresh) Animating() bool
Animating reports whether the widget still needs frames: while a spring-back is settling, or while it is refreshing (the spinner spins). At rest it is false, so a host stops repainting.
func (*PullToRefresh) ChildOffset ¶ added in v0.187.0
func (w *PullToRefresh) ChildOffset() (int, int)
ChildOffset reports that the content is painted pushed DOWN by the current pull, so WalkA11y places the child's accessible rectangle where it is drawn (mirroring how ScrollView reports its scroll offset).
func (*PullToRefresh) Children ¶ added in v0.187.0
func (w *PullToRefresh) Children() []Widget
Children yields the wrapped content so a generic walk (accessibility, animation, text selection) reaches it. See children.go for why every widget holding a Widget must expose it.
func (*PullToRefresh) Done ¶ added in v0.187.0
func (w *PullToRefresh) Done()
Done ends the refreshing state: the spinner stops and the content springs back home to idle. It is the app's signal that the reload OnRefresh kicked off has finished. A no-op unless a refresh is in progress.
func (*PullToRefresh) Draw ¶ added in v0.187.0
func (w *PullToRefresh) Draw(p painter.Painter, theme *Theme)
Draw paints the child pushed down by the current pull, then the indicator in the gap that opens above it. The child is clipped to the container's bounds so content pushed off the bottom cannot overdraw a neighbour; back-ends without a clipper/translator fall back exactly as ScrollView does.
func (*PullToRefresh) OnEvent ¶ added in v0.187.0
func (w *PullToRefresh) OnEvent(ev Event)
OnEvent routes the touch stream through the pull gesture and forwards everything else (and any touch it does not consume) to the child, translated into the child's frame by the current pull so a press lands where the content is drawn. This lets a wrapped view keep its wheel/keyboard/mouse behaviour untouched while the container adds pull-to-refresh on top.
func (*PullToRefresh) Pull ¶ added in v0.187.0
func (w *PullToRefresh) Pull() float64
Pull returns the current revealed pull distance in device pixels (>= 0). It is the spring engine's offset, floored at 0, and may be fractional mid-spring.
func (*PullToRefresh) PullInt ¶ added in v0.187.0
func (w *PullToRefresh) PullInt() int
PullInt returns the current pull distance rounded to the nearest device pixel — the height the content is pushed down by, and the indicator gap.
func (*PullToRefresh) Refresh ¶ added in v0.187.0
func (w *PullToRefresh) Refresh()
Refresh triggers a refresh programmatically (e.g. from a toolbar button or a keyboard shortcut), exactly as an armed release would: it springs the content down to the rest height, spins the spinner, and fires OnRefresh. A no-op while already refreshing.
func (*PullToRefresh) Refreshing ¶ added in v0.187.0
func (w *PullToRefresh) Refreshing() bool
Refreshing reports whether a refresh is in progress (state PullRefreshing).
func (*PullToRefresh) State ¶ added in v0.187.0
func (w *PullToRefresh) State() PullState
State returns the current position in the state machine.
func (*PullToRefresh) Tick ¶ added in v0.187.0
func (w *PullToRefresh) Tick(dt float64)
Tick advances the spring-back animation and the spinner by dt seconds, making PullToRefresh an Animator. A non-positive dt is a no-op.
func (*PullToRefresh) TouchDown ¶ added in v0.187.0
func (w *PullToRefresh) TouchDown(ev Event)
TouchDown begins a touch contact at ev. It stops any spring-back in progress (so a finger coming down on a settling pull catches it) and records the start position; the contact does not become a pull until TouchMove sees it drag downward while at the top. It never interrupts an in-flight refresh.
func (*PullToRefresh) TouchMove ¶ added in v0.187.0
func (w *PullToRefresh) TouchMove(ev Event) bool
TouchMove feeds one drag sample. Until the contact has begun pulling it only starts one when the content is at its top and the finger has moved net-down from the start; once pulling, every sample stretches the rubber band and re-evaluates the arm threshold. Returns true when it consumed the sample as a pull (so a caller routing raw events knows not to also scroll the child).
func (*PullToRefresh) TouchUp ¶ added in v0.187.0
func (w *PullToRefresh) TouchUp()
TouchUp releases the contact. An armed release enters the refreshing state (springing the content down to the rest height and firing OnRefresh); an un-armed pull springs back home to idle; anything else is a no-op.
type RGBA ¶
RGBA is a 32-bit colour value packed as bytes (Red, Green, Blue, Alpha). Alias of painter.RGBA so widgets emit values that flow unchanged through any Painter back-end (pixel buffer, cell grid, SVG stream). Alpha is honoured by the pixel rasteriser; the stock widgets all paint opaque pixels (A=0xFF).
type RadarChart ¶ added in v0.82.0
type RadarChart struct {
Base
Axes []string
Series [][]float64
Max float64 // normalisation max; when <= 0, taken from the data
Colors []RGBA // optional per-series palette override; cycles by index
// contains filtered or unexported fields
}
RadarChart plots one or more series over a set of shared axes as closed polygons on a polygonal (spider) grid -- the multivariate complement to LineChart. Each of the N Axes gets a spoke from the centre (the first at 12 o'clock, the rest clockwise); a series' value on each axis, normalised to Max, sets how far out along that spoke its polygon vertex sits. Faint concentric N-gon rings and the spokes form the grid, each axis labelled just past its outer tip. Every series polygon is filled with a translucent tint of its colour and outlined in the solid colour. Colours cycle through the shared categorical palette unless Colors is set. Display-only.
It renders through painter.Painter, so the same chart draws as pixels (WUI/GUI) or promoted cells (TUI). No axes (or a degenerate size) draws nothing; a series shorter than the axis count treats the missing values as 0.
func NewRadarChart ¶ added in v0.82.0
func NewRadarChart(axes []string, series [][]float64) *RadarChart
NewRadarChart builds a RadarChart over the given axis labels and series.
func (*RadarChart) A11y ¶ added in v0.105.0
func (c *RadarChart) A11y() A11yInfo
A11y reports the RadarChart as an img carrying its axis count -- the salient dimension of a radar plot.
func (*RadarChart) AxisAt ¶ added in v0.90.0
func (c *RadarChart) AxisAt(localX, localY int) (axis int, ok bool)
AxisAt returns the axis whose spoke is nearest (in angle) to widget-local (x, y), and ok=false when the chart has no axes. Exposed so a host can show that axis's values on hover.
func (*RadarChart) Draw ¶ added in v0.82.0
func (c *RadarChart) Draw(p painter.Painter, theme *Theme)
Draw paints the grid rings, spokes and axis labels, then each series as a translucent fill under a solid outline.
func (*RadarChart) Hover ¶ added in v0.90.0
func (c *RadarChart) Hover() *mvvm.Observable[bool]
Hover is the reactive spoke-highlight toggle as a shared mvvm.Observable; false draws no highlight. Lazily created, defaulting to off.
func (*RadarChart) HoverAxis ¶ added in v0.90.0
func (c *RadarChart) HoverAxis() *mvvm.Observable[int]
HoverAxis is the reactive hovered axis index as a shared mvvm.Observable. Lazily created, defaulting to 0.
func (*RadarChart) OnEvent ¶ added in v0.100.0
func (c *RadarChart) OnEvent(ev Event)
OnEvent highlights the radar spoke nearest the pointer, clearing when it leaves.
type RadioButton ¶
RadioButton is a circular toggle paired with a label. RadioButtons are typically grouped via RadioGroup so exactly one in the group is Checked at any time. A standalone RadioButton (not added to a group) behaves like a CheckButton (toggleable on click).
func NewRadioButton ¶
func NewRadioButton(label string) *RadioButton
NewRadioButton constructs a standalone RadioButton with the given label. Add it to a RadioGroup with group.Add(r) for mutual-exclusion behaviour.
func (*RadioButton) A11y ¶ added in v0.19.0
func (r *RadioButton) A11y() A11yInfo
A11y reports the RadioButton as a radio with its checked state.
func (*RadioButton) Checked ¶
func (r *RadioButton) Checked() *mvvm.Observable[bool]
Checked is the current checked state as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Checked field. A click (standalone) or a group selection Sets it, notifying subscribers.
func (*RadioButton) Draw ¶
func (r *RadioButton) Draw(p painter.Painter, theme *Theme)
Draw paints the circular mark + label. The "circle" is a 12 x 12 box with a 1-pixel inset on every side, painted as a stroked rectangle (approximate to avoid bringing in trig). When Checked, a smaller Accent-filled rect sits inside as the radio dot.
func (*RadioButton) Focused ¶ added in v0.101.0
func (f *RadioButton) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*RadioButton) HitRect ¶ added in v0.187.0
func (r *RadioButton) HitRect() Rect
HitRect is the radio button's interactive rectangle: its drawn Bounds clamped up to the density hit-target and centred over them (see [touchHitRect]). Like the checkbox, its short row grows to the >=44px finger floor under DensityTouch while the drawn 12px mark is untouched; byte-identical to Bounds under DensityCompact.
func (*RadioButton) HitTest ¶ added in v0.187.0
func (r *RadioButton) HitTest(px, py int) bool
HitTest reports whether a surface point falls on the radio button's (touch-clamped) hit rect.
func (*RadioButton) OnEvent ¶
func (r *RadioButton) OnEvent(ev Event)
OnEvent: on click, route through the group (if any) so siblings clear; otherwise toggle Checked locally.
func (*RadioButton) SetFocused ¶ added in v0.101.0
func (f *RadioButton) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
type RadioGroup ¶
type RadioGroup struct {
Members []*RadioButton
// contains filtered or unexported fields
}
RadioGroup makes a set of RadioButtons mutually exclusive. Active is the index of the currently-checked member, or -1 when none has been clicked yet.
func NewRadioGroup ¶
func NewRadioGroup() *RadioGroup
NewRadioGroup builds an empty group with Active = -1.
func (*RadioGroup) Active ¶
func (g *RadioGroup) Active() *mvvm.Observable[int]
Active is the index of the currently-checked member as a shared mvvm.Observable, or -1 when none has been clicked yet: a host binds it (Set / Subscribe / two-way) — there is no settable Active field. A click on a member, or an arrow key moving the checked member, Sets it and notifies subscribers. A bare &RadioGroup{} lazy-inits Active to 0; NewRadioGroup starts it at -1.
func (*RadioGroup) Add ¶
func (g *RadioGroup) Add(r *RadioButton)
Add appends r to the group + remembers its membership so a click on any member can clear the others.
type RangeSlider ¶ added in v0.10.0
type RangeSlider struct {
Base
Min, Max float64
Orientation Orientation
// Step is the increment an arrow key applies to the keyboard-focused handle.
// When it is <= 0 the slider falls back to 1% of the range, so a caller that
// never sets Step still gets sensible keyboard nudges.
Step float64
// contains filtered or unexported fields
}
RangeSlider is a two-handle slider selecting a sub-interval [Low, High] within a continuous Min..Max range -- a price band, a date window, a volume gate. It is the two-thumb sibling of Scale: the same rounded track and circular white thumbs, but the Accent fill spans the selected band between the handles rather than from the left edge.
A click grabs whichever handle is nearest the cursor and jumps it there; a subsequent drag moves that same handle, clamped so Low never crosses High.
The two reactive handle positions are MVVM-only: each lives in an unexported mvvm.Observable exposed via RangeSlider.Low and RangeSlider.High. There are no settable Low/High fields and no OnChange callback -- a host binds Low()/High() (Set / Subscribe / two-way) and a click, drag or key adjustment Sets them (clamped so Low <= High), notifying subscribers on change.
Example ¶
ExampleRangeSlider selects a sub-interval; SetRange orders + clamps its ends.
package main
import (
"fmt"
"github.com/go-widgets/toolkit"
)
func main() {
rs := toolkit.NewRangeSlider(0, 100, 20, 80)
rs.SetRange(90, 10) // passed out of order → normalised to Low <= High
fmt.Printf("%.0f-%.0f\n", rs.Low().Get(), rs.High().Get())
}
Output: 10-90
func NewRangeSlider ¶ added in v0.10.0
func NewRangeSlider(min, max, low, high float64) *RangeSlider
NewRangeSlider builds a RangeSlider spanning [min, max] with the given initial band. The band is clamped and ordered so Low <= High.
func (*RangeSlider) A11y ¶ added in v0.40.0
func (s *RangeSlider) A11y() A11yInfo
A11y reports the RangeSlider as a group carrying its "low..high" band -- two cooperating handles read more naturally as one control's range value than as two independent sliders. The numeric triple exposes the track bounds in Min/Max; Now carries the Low handle (the arrow keys' default handle), since a single aria-valuenow cannot hold both -- the full band stays in Value.
func (*RangeSlider) Draw ¶ added in v0.10.0
func (s *RangeSlider) Draw(p painter.Painter, theme *Theme)
Draw paints the rounded track, the Accent band between the two handles, and a circular white thumb at each handle -- matching Scale's macOS styling.
func (*RangeSlider) Focused ¶ added in v0.101.0
func (f *RangeSlider) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*RangeSlider) High ¶ added in v0.10.0
func (s *RangeSlider) High() *mvvm.Observable[float64]
High is the upper handle's position as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) -- there is no settable High field. A click, drag or key adjustment Sets it (clamped so Low <= High); subscribers are notified on change.
func (*RangeSlider) HighThumbHitRect ¶ added in v0.187.0
func (s *RangeSlider) HighThumbHitRect() Rect
HighThumbHitRect is the finger grab for the High handle; see LowThumbHitRect.
func (*RangeSlider) Low ¶ added in v0.10.0
func (s *RangeSlider) Low() *mvvm.Observable[float64]
Low is the lower handle's position as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) -- there is no settable Low field. A click, drag or key adjustment Sets it (clamped so Low <= High); subscribers are notified on change.
func (*RangeSlider) LowThumbHitRect ¶ added in v0.187.0
func (s *RangeSlider) LowThumbHitRect() Rect
LowThumbHitRect and HighThumbHitRect are the finger grabs for the two handles: each drawn thumb clamped up to the touch minimum on both axes and centred over the knob, so a 16-logical-pixel handle exposes a 44px target under DensityTouch. At DensityCompact each equals its drawn thumb byte-for-byte.
func (*RangeSlider) OnEvent ¶ added in v0.10.0
func (s *RangeSlider) OnEvent(ev Event)
OnEvent: a click grabs the nearer handle and jumps it to the cursor; a drag moves the grabbed handle; a mouse-up releases it. Each move re-clamps so the handles never cross, and notifies the handle's Observable.
func (*RangeSlider) SetFocused ¶ added in v0.101.0
func (f *RangeSlider) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
func (*RangeSlider) SetRange ¶ added in v0.10.0
func (s *RangeSlider) SetRange(low, high float64)
SetRange clamps both bounds to [Min, Max] and swaps them if low > high, so the invariant Low <= High always holds, then Sets both Observables.
type Rating ¶ added in v0.8.0
type Rating struct {
Base
// Max is the number of cells (config). The reactive rating is MVVM-only: the
// current value lives in an unexported Observable exposed via [Rating.Value].
Max int
// FilledColor / EmptyColor override the themed star tones for this widget
// only. Each is honoured when opaque (A != 0); a zero value (the default)
// inherits Theme.StarFilled / Theme.StarEmpty (or their fallbacks). They are
// set-once appearance config, not reactive state.
FilledColor RGBA
EmptyColor RGBA
// contains filtered or unexported fields
}
Rating is a horizontal star-rating strip: Max cells drawn left-to-right, each carrying a real five-pointed star. Cells with index < Value paint the filled (gold) star; cells with index >= Value paint the empty (grey) star. The star is a proper concave 10-vertex polygon, anti-aliased through the painter's path-fill capability on pixel back-ends and scanline-filled on a cell grid, so the row reads as stars — not squares, not an ASCII "*".
The two tones come from the Theme (StarFilled / StarEmpty) so a rating re-tints with the rest of the palette in light and dark; a host may override either per-widget via Rating.FilledColor / Rating.EmptyColor.
A click on cell index i sets Value to i+1 (so the leftmost cell yields 1, the rightmost Max), notifying the Value Observable's subscribers. Clicks outside the strip (X to the right of the last cell) are ignored — the parent container already routes only hits inside Bounds() but a stray x >= Max*pitch would otherwise resolve to an out-of-range index.
func NewRating ¶ added in v0.8.0
NewRating constructs a Rating with the given value and max. Max defaults to 5 when non-positive; Value is clamped to the [0, Max] interval so a bogus caller input can never render more filled stars than Max.
func (*Rating) A11y ¶ added in v0.40.0
A11y reports the Rating as a slider carrying its "value/max" score, plus the numeric Min/Max/Now range triple (Min is 0, the empty rating).
func (*Rating) Draw ¶ added in v0.8.0
Draw paints Max stars left-to-right. Stars at index < Value use the filled (gold) tone; the rest use the empty (grey) tone. A thin outline — a darkened blend of each star's own fill — defines the shape against the surface, especially the grey empty stars. The cell edge and pitch route through scaled so the strip grows with HiDPI and touch density; Draw and OnEvent derive the pitch from the same constants, so the drawn stars and the click-to-index mapping can never drift.
func (*Rating) Focused ¶ added in v0.101.0
func (f *Rating) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*Rating) HitRect ¶ added in v0.187.0
HitRect is the rating strip's interactive rectangle: its drawn Bounds clamped up to the density hit-target and centred over them (see [touchHitRect]). A star row is only ~14 logical pixels tall, so under DensityTouch its hit height grows to the >=44px finger floor for a comfortable vertical reach while the drawn stars are untouched; byte-identical to Bounds under DensityCompact. The per-cell index still derives from the drawn cell pitch, so which star a press selects is unaffected by the clamp.
func (*Rating) HitTest ¶ added in v0.187.0
HitTest reports whether a surface point falls on the rating strip's (touch-clamped) hit rect.
func (*Rating) OnEvent ¶ added in v0.8.0
OnEvent handles a click by resolving the star index from ev.X and setting Value = index+1. Non-click events are ignored (matches Switch / ToggleButton). Clicks with X to the right of the last cell (index >= Max) are ignored so a spurious hit doesn't push Value past Max.
func (*Rating) SetFocused ¶ added in v0.101.0
func (f *Rating) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
func (*Rating) Value ¶ added in v0.8.0
func (r *Rating) Value() *mvvm.Observable[int]
Value is the current rating as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Value field. A click or a key adjustment Sets it (clamped to [0, Max]); subscribers are notified.
type Rect ¶
Rect is an axis-aligned rectangle in pixel coordinates. X/Y is the top-left corner; W/H are width/height. Aliased to painter.Rect so widgets can render on any painter.Painter (PixelPainter for a pixel buffer, CellPainter for a terminal grid) without a type conversion. Contains() is inherited from painter.Rect.
func FitBounds ¶ added in v0.46.0
FitBounds returns the largest rect of source aspect (srcW:srcH) that fits entirely within bounds, centred in it — the geometry ScaleFit paints into. Consumers can call it to size/lay out an image area (e.g. grow a box to the image's fitted height) before drawing. When srcW or srcH is non-positive the aspect is unknown and bounds is returned unchanged.
type Region ¶ added in v0.59.0
type Region int
Region names a Border layout edge (or the centre) an Item occupies. The zero value RegionCenter fills what the edge regions leave.
type RichEditor ¶ added in v0.218.0
type RichEditor struct {
Base
// contains filtered or unexported fields
}
RichEditor is a WYSIWYG editing surface over a richdoc.Document: it lays the document out as formatted, wrapped content (headings, paragraphs, lists, quotes, code, tables, rules, math and inline emphasis) and edits it in place.
Its reactive state is MVVM-only: the document lives on the Doc() Observable, and the caret, selection, focus flag and vertical scroll offset are each their own Observable accessor, so a host binds/subscribes instead of touching a field — a consumer re-serialises to Markdown/LaTeX/ODT/RTF by subscribing to Doc(). The formatting verbs (ToggleStrong, SetBlockType, ToggleList, ...) are methods that mutate the model and refresh; a visible toolbar is the consumer's job.
Every edit produces a NEW *richdoc.Document (via richdoc.Clone), so Doc() subscribers always see an immutable snapshot and the pointer change guarantees notification.
func NewRichEditor ¶ added in v0.218.0
func NewRichEditor(doc *richdoc.Document) *RichEditor
NewRichEditor builds an editor over doc (a nil doc starts an empty document).
func (*RichEditor) A11y ¶ added in v0.218.0
func (e *RichEditor) A11y() A11yInfo
A11y exposes the editor as a textbox whose value is the document's plain text, so an assistive technology reads the edited content.
func (*RichEditor) ActiveInlineStyles ¶ added in v0.227.0
func (e *RichEditor) ActiveInlineStyles() InlineStyles
ActiveInlineStyles returns the inline styles active at the caret or across the selection — see InlineStyles. It is read-only: it never mutates the document.
func (*RichEditor) Caret ¶ added in v0.218.0
func (e *RichEditor) Caret() *mvvm.Observable[DocPos]
Caret is the caret position as a bindable Observable.
func (*RichEditor) CaretPixel ¶ added in v0.218.0
func (e *RichEditor) CaretPixel(pos DocPos) (x, y int)
CaretPixel returns the top-left device pixel of the caret for pos, in the same surface coordinates Draw paints in (scroll already applied), so a host or a test can place/probe the caret without duplicating the layout math.
func (*RichEditor) ClearSelection ¶ added in v0.218.0
func (e *RichEditor) ClearSelection()
ClearSelection collapses the selection onto the caret.
func (*RichEditor) CurrentBlockKind ¶ added in v0.227.0
func (e *RichEditor) CurrentBlockKind() BlockKind
CurrentBlockKind reports the BlockKind of the caret's top-level block, so a toolbar can light the matching Paragraph / Heading / Quote / Code-block button. A heading maps to BlockH1..BlockH6 by its level (clamped to that range); a code block to BlockCodeKind, a block quote to BlockQuoteKind; everything else — an ordinary paragraph, a list (whose editable content is paragraph-like), an atomic block, or an out-of-range caret — reports BlockParagraph.
func (*RichEditor) CurrentListOrdered ¶ added in v0.227.0
func (e *RichEditor) CurrentListOrdered() (ordered, isList bool)
CurrentListOrdered reports whether the caret's top-level block is a list, and if so whether it is ordered (numbered). isList is false — and ordered is then meaningless (false) — when the caret is not directly on a List block or the caret is out of range. A toolbar lights its bullet-list button when isList && !ordered and its numbered-list button when isList && ordered.
func (*RichEditor) DeleteSelection ¶ added in v0.218.0
func (e *RichEditor) DeleteSelection()
DeleteSelection removes the selected range and parks the caret at its start. No-op on an empty selection.
func (*RichEditor) Doc ¶ added in v0.218.0
func (e *RichEditor) Doc() *mvvm.Observable[*richdoc.Document]
Doc is the edited document as a shared mvvm.Observable: a host binds it two-way or subscribes to re-serialise on every edit. Lazily created so a bare &RichEditor{} works.
func (*RichEditor) Document ¶ added in v0.218.0
func (e *RichEditor) Document() *richdoc.Document
Document returns an independent deep copy of the current document — the snapshot a consumer serialises. Mutating it never affects the editor.
func (*RichEditor) Draw ¶ added in v0.218.0
func (e *RichEditor) Draw(p painter.Painter, theme *Theme)
Draw paints the border + surface, the block chrome, selection bands, the formatted runs and (when focused) the caret, windowed by the scroll offset, and a scrollbar when the content overflows.
func (*RichEditor) Focused ¶ added in v0.218.0
func (e *RichEditor) Focused() *mvvm.Observable[bool]
Focused reports (and drives) keyboard focus; Draw paints the caret + accent border while it is true.
func (*RichEditor) HasSelection ¶ added in v0.218.0
func (e *RichEditor) HasSelection() bool
HasSelection reports whether the selection covers any cells.
func (*RichEditor) InsertText ¶ added in v0.218.0
func (e *RichEditor) InsertText(s string)
InsertText inserts s at the caret, one rune at a time; a '\n' splits the block (or, inside a code block, inserts a literal newline). It is the programmatic counterpart of typing.
func (*RichEditor) OnEvent ¶ added in v0.218.0
func (e *RichEditor) OnEvent(ev Event)
OnEvent dispatches clicks (caret placement + drag selection), the wheel and keyboard navigation/editing.
func (*RichEditor) ScrollOffset ¶ added in v0.218.0
func (e *RichEditor) ScrollOffset() *mvvm.Observable[int]
ScrollOffset is the vertical scroll position in device pixels (0 == top), bindable. Reads clamp on the fly, so a stale value after the document shrank is harmless.
func (*RichEditor) Selection ¶ added in v0.218.0
func (e *RichEditor) Selection() *mvvm.Observable[DocSelection]
Selection is the highlighted range as a bindable Observable; empty means none.
func (*RichEditor) SetBlockType ¶ added in v0.218.0
func (e *RichEditor) SetBlockType(kind BlockKind)
SetBlockType converts the caret's top-level block to kind, preserving its inline content (a conversion to code flattens styling to plain text).
func (*RichEditor) SetDocument ¶ added in v0.218.0
func (e *RichEditor) SetDocument(d *richdoc.Document)
SetDocument replaces the whole document and parks the caret at the start. It goes through Doc(), so bindings/subscribers fire.
func (*RichEditor) ToggleCode ¶ added in v0.218.0
func (e *RichEditor) ToggleCode()
ToggleCode toggles an inline code span (exclusive of other styles on rebuild).
func (*RichEditor) ToggleEmph ¶ added in v0.218.0
func (e *RichEditor) ToggleEmph()
ToggleEmph toggles italic (emphasis).
func (*RichEditor) ToggleList ¶ added in v0.218.0
func (e *RichEditor) ToggleList(ordered bool)
ToggleList wraps the caret's block into a single-item list, converts an existing list between bullet/numbered, or (when the ordered-ness already matches) unwraps the list back into its items' blocks.
func (*RichEditor) ToggleStrikethrough ¶ added in v0.218.0
func (e *RichEditor) ToggleStrikethrough()
ToggleStrikethrough toggles a strike-out.
func (*RichEditor) ToggleStrong ¶ added in v0.218.0
func (e *RichEditor) ToggleStrong()
ToggleStrong toggles bold over the selection, or arms bold for the next typed rune when the caret is collapsed.
type RichEditorToolbar ¶ added in v0.227.0
type RichEditorToolbar struct {
Base
// IconSize is the square edge, in LOGICAL pixels, of each icon button
// (set-once layout config); it is routed through the HiDPI metric scale (see
// [SetMetricScale]) at build, so the strip stays crisp at any DPI like every
// sibling widget. Zero is replaced by [RichEditorToolbarIconSize] at build.
IconSize int
// Spacing is the gap, in LOGICAL pixels, between adjacent buttons and around
// the group dividers (set-once layout config); it is metric-scaled at build.
// Negative is clamped to 0.
Spacing int
// contains filtered or unexported fields
}
RichEditorToolbar is a horizontal strip of icon buttons that drive a bound RichEditor's formatting verbs and reflect the formatting in force at its caret / selection. It groups the buttons into three logical clusters — inline styles, block kinds and lists — with a thin divider between groups:
- Inline: Bold, Italic, Strikethrough, Code.
- Block: Paragraph, H1, H2, H3, Quote, Code block.
- Lists: Bullet list, Numbered list.
Clicking a button invokes the matching verb on the editor. The toolbar subscribes to the editor's Caret / Selection / Doc observables and lights the button whose formatting is active (a pressed pill drawn via each button's sticky Selected() state): Bold lights when the caret/selection is strong, the current block's Paragraph/Heading/Quote/Code button lights, and the list button lights while the caret is inside a list of that kind.
All reactive state is MVVM: the pressed/active state lives on each child Button's Selected() Observable (which the toolbar drives from the editor model), and the editor's own state lives on its Observables. IconSize and Spacing are set-once layout config, read once when the buttons are built by NewRichEditorToolbar; change them before construction, not after.
func NewRichEditorToolbar ¶ added in v0.227.0
func NewRichEditorToolbar(ed *RichEditor) *RichEditorToolbar
NewRichEditorToolbar builds a toolbar bound to ed. A nil ed yields an inert toolbar (the buttons render but do nothing and never light). IconSize and Spacing take their defaults; set them on a struct literal before calling this to override, or use the returned toolbar's zero-config defaults.
func (*RichEditorToolbar) A11y ¶ added in v0.227.0
func (t *RichEditorToolbar) A11y() A11yInfo
A11y reports the strip as a toolbar carrying its button count, so an assistive technology announces it as a formatting toolbar; its buttons are exposed as children (see RichEditorToolbar.Children).
func (*RichEditorToolbar) Children ¶ added in v0.227.0
func (t *RichEditorToolbar) Children() []Widget
Children yields the button strip's children (buttons + dividers) so a11y and focus tree walkers descend into the individual buttons.
func (*RichEditorToolbar) Dispose ¶ added in v0.227.0
func (t *RichEditorToolbar) Dispose()
Dispose removes the editor subscriptions so a discarded toolbar stops being notified. Safe to call more than once.
func (*RichEditorToolbar) Draw ¶ added in v0.227.0
func (t *RichEditorToolbar) Draw(p painter.Painter, theme *Theme)
Draw paints the toolbar surface then every button + divider.
func (*RichEditorToolbar) Editor ¶ added in v0.227.0
func (t *RichEditorToolbar) Editor() *RichEditor
Editor returns the bound editor (nil for an inert toolbar).
func (*RichEditorToolbar) Measure ¶ added in v0.227.0
func (t *RichEditorToolbar) Measure(availW, availH int) (w, h int)
Measure reports the strip's natural size: the summed button + divider widths with the inter-child gaps, by the icon-button height. It lets a box layout size the toolbar to its content.
func (*RichEditorToolbar) OnEvent ¶ added in v0.227.0
func (t *RichEditorToolbar) OnEvent(ev Event)
OnEvent forwards the event to the button strip (which routes clicks to the button under the pointer).
func (*RichEditorToolbar) SetBounds ¶ added in v0.227.0
func (t *RichEditorToolbar) SetBounds(r Rect)
SetBounds positions the toolbar and lays its buttons out left to right.
type Role ¶ added in v0.19.0
type Role string
Role is a widget's accessibility role, named after the WAI-ARIA roles a host maps them onto.
const ( RoleButton Role = "button" RoleText Role = "text" RoleTextbox Role = "textbox" RoleCheckbox Role = "checkbox" RoleRadio Role = "radio" RoleSwitch Role = "switch" RoleSlider Role = "slider" )
The roles the built-in widgets report.
const ( RoleSearchbox Role = "searchbox" RoleCombobox Role = "combobox" RoleListbox Role = "listbox" RoleGrid Role = "grid" RoleTree Role = "tree" RoleTablist Role = "tablist" RoleMenu Role = "menu" RoleMenuBar Role = "menubar" RoleAlert Role = "alert" RoleStatus Role = "status" RoleProgressbar Role = "progressbar" RoleMeter Role = "meter" RoleImg Role = "img" RoleGroup Role = "group" RoleDialog Role = "dialog" RoleTooltip Role = "tooltip" RoleBanner Role = "banner" RoleList Role = "list" RoleDocument Role = "document" RoleToolbar Role = "toolbar" RolePresentation Role = "presentation" )
Additional Role constants for the widgets implemented in this file. See a11y.go for the original seven roles (button/text/textbox/checkbox/ radio/switch/slider) and the Accessible/A11yInfo/CollectA11y machinery they share with everything below.
const RoleLog Role = "log"
RoleLog is the ARIA role="log" a LogView reports: a live region where new entries are appended and older ones are retained, which a screen reader announces incrementally as they arrive.
const RoleTab Role = "tab"
RoleTab is the WAI-ARIA role each TabBar item reports in the accessibility tree — a "tab" inside the bar's "tablist" (see RoleTablist). It sits beside the roles in a11y.go / a11y_more.go.
type Rule ¶ added in v0.42.0
Rule validates a single string value. It returns a non-nil error -- whose Error() text is the message shown to the user (e.g. via FormField.Error) -- when value fails the rule's check, or nil when value passes.
func All ¶ added in v0.42.0
All combines rules into a single Rule that runs them in order and fails on the first one that fails, discarding the rest -- the same short-circuit semantics as Validate. Useful for grouping a related set of rules (e.g. a password policy) behind one Rule value.
type Scale ¶
type Scale struct {
Base
Min, Max float64
Orientation Orientation
// Step is the increment an arrow key applies to the value. When it is <= 0
// the scale falls back to keyStep (1% of the range), so a caller that never
// sets Step still gets sensible keyboard nudges. PageUp/PageDown always move a
// whole page (keyPage, 10% of the range) regardless of Step.
Step float64
// contains filtered or unexported fields
}
Scale is a horizontal slider over a continuous Min..Max range. Click on the track jumps the thumb to that x-position and notifies the Value Observable's subscribers; dragging the thumb (or anywhere along the track with the button held) scrubs the value continuously through the same math. The 4-px track sits across the vertical midpoint in Theme.SurfaceAlt; the 10-px square thumb sits at the value's position in Theme.Accent.
The reactive position is MVVM-only: it lives in an unexported mvvm.Observable exposed via Scale.Value. There is no settable Value field and no OnChange callback — a host binds Value() (Set / Subscribe / two-way) and a click or key adjustment Sets it (clamped to [Min, Max]), notifying subscribers on change.
func NewScale ¶
NewScale builds a Scale spanning [min, max] with the given initial value. Min == Max is allowed but renders a non-interactive track.
func (*Scale) A11y ¶ added in v0.19.0
A11y reports the Scale as a slider carrying its current value, both as a human-readable Value string and as the numeric Min/Max/Now range triple.
func (*Scale) Draw ¶
Draw paints a macOS-style slider: a rounded track whose filled portion (up to the thumb) is Accent and whose remainder is SurfaceAlt, with a circular white thumb -- matching the Switch's pill track + circular knob.
func (*Scale) Focused ¶ added in v0.101.0
func (f *Scale) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*Scale) OnEvent ¶
OnEvent: a click (or a drag while the button is held) moves the thumb to the pointer's position along the track and notifies the Value Observable; arrow / Home / End / Page keys move the value while focused. A single thumb needs no drag-grab state -- the position->SetValue math handles any coordinate identically, so a drag is just a click that keeps arriving.
func (*Scale) SetFocused ¶ added in v0.101.0
func (f *Scale) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
func (*Scale) SetValue ¶
SetValue clamps v to [Min, Max] and Sets the Value Observable — the shared mutate path for a click, a drag and every key adjustment. Subscribers are notified on change (an unchanged value is a no-op, per mvvm.Observable).
func (*Scale) ThumbHitRect ¶ added in v0.187.0
ThumbHitRect is the finger grab for the slider knob: the drawn thumb rectangle clamped up to the touch minimum on each axis and centred over it. A knob only scaleThumbSize (16 logical px) across otherwise offers a 24px target under DensityTouch; this lifts it to the 44px floor without changing the painted thumb. At DensityCompact it equals the drawn thumb byte-for-byte.
func (*Scale) Value ¶
func (s *Scale) Value() *mvvm.Observable[float64]
Value is the current position as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Value field. A click, a drag or a key adjustment Sets it (clamped to [Min, Max]); subscribers are notified on change.
type ScaleMode ¶ added in v0.45.0
type ScaleMode int
ScaleMode selects how an Image's source pixels map onto its bounds.
const ( // ScaleStretch fills the whole bounds, ignoring the source aspect ratio // (nearest-neighbour). It is the zero value, so existing callers are // unchanged. ScaleStretch ScaleMode = iota // ScaleFit ("contain") preserves the source aspect ratio, scaling the image // to the largest size that fits entirely within the bounds and centring it; // the margin around it is left untouched. ScaleFit )
type ScatterChart ¶ added in v0.82.0
type ScatterChart struct {
Base
Series [][]ScatterPoint
Colors []RGBA // optional per-series palette override; cycles by index
// contains filtered or unexported fields
}
ScatterChart plots one or more series of (X, Y) points as small filled dots over a left+bottom axis frame -- the two-dimensional companion to LineChart. Both axes auto-scale to the combined data range (a flat range on either axis pads by ±1 so the points sit mid-plot rather than on an edge). Colours cycle through the shared categorical palette unless Colors is set. Display-only.
It renders through painter.Painter, so the same chart draws as pixels (WUI/GUI) or promoted cells (TUI). An empty Series draws just the axes.
func NewScatterChart ¶ added in v0.82.0
func NewScatterChart(series [][]ScatterPoint) *ScatterChart
NewScatterChart builds a ScatterChart over the given series.
func (*ScatterChart) A11y ¶ added in v0.105.0
func (c *ScatterChart) A11y() A11yInfo
A11y reports the ScatterChart as an img carrying its series count.
func (*ScatterChart) Draw ¶ added in v0.82.0
func (c *ScatterChart) Draw(p painter.Painter, theme *Theme)
Draw paints the axis frame then one dot per point, coloured by series.
func (*ScatterChart) Hover ¶ added in v0.90.0
func (c *ScatterChart) Hover() *mvvm.Observable[bool]
Hover is the reactive point-ring toggle as a shared mvvm.Observable; false draws no ring. Lazily created, defaulting to off.
func (*ScatterChart) HoverPoint ¶ added in v0.90.0
func (c *ScatterChart) HoverPoint() *mvvm.Observable[int]
HoverPoint is the reactive hovered point index as a shared mvvm.Observable. Lazily created, defaulting to 0.
func (*ScatterChart) HoverSeries ¶ added in v0.90.0
func (c *ScatterChart) HoverSeries() *mvvm.Observable[int]
HoverSeries is the reactive hovered series index as a shared mvvm.Observable. Lazily created, defaulting to 0.
func (*ScatterChart) NearestPoint ¶ added in v0.90.0
func (c *ScatterChart) NearestPoint(localX, localY int) (series, point int, pt ScatterPoint, ok bool)
NearestPoint returns the point closest (in pixels) to widget-local (x, y) — its series index, point index, the point, and ok=false when the chart has no data. Exposed so a host can show the value on hover.
func (*ScatterChart) OnEvent ¶ added in v0.100.0
func (c *ScatterChart) OnEvent(ev Event)
OnEvent rings the scatter point nearest the pointer, clearing when it leaves.
type ScatterPoint ¶ added in v0.82.0
type ScatterPoint struct{ X, Y float64 }
ScatterPoint is one (X, Y) sample plotted by a ScatterChart.
type Schema ¶ added in v0.180.0
type Schema struct {
Databases []DatabaseInfo
}
Schema is the whole object tree a DataSource exposes.
type Scope ¶ added in v0.151.0
type Scope int
Scope is where a key binding applies, and therefore its resolution priority. A binding on a more specific scope shadows a less specific one that shares the same chord: the focused widget's bindings win over the window's, which win over the application-global ones. This is what lets a text field bind Ctrl+A to "select all" while the app keeps Ctrl+A as "select all items" elsewhere — same chord, different active scope.
const ( // ScopeGlobal applies application-wide and is ALWAYS active during // resolution regardless of the active mask; it has the lowest priority. ScopeGlobal Scope = iota // ScopeWindow applies while a particular window/view is focused; it // overrides ScopeGlobal. ScopeWindow // ScopeWidget applies while a particular widget is focused; it has the // highest priority and overrides both ScopeWindow and ScopeGlobal. ScopeWidget )
type ScopeMask ¶ added in v0.151.0
type ScopeMask uint8
ScopeMask is the set of scopes that are active for one resolution — the window and/or widget contexts currently focused. ScopeGlobal is implicitly always active, so a zero mask still resolves global bindings.
const ( // MaskGlobal marks ScopeGlobal active (implied by every resolution). MaskGlobal ScopeMask = 1 << ScopeGlobal // MaskWindow marks ScopeWindow active. MaskWindow ScopeMask = 1 << ScopeWindow // MaskWidget marks ScopeWidget active. MaskWidget ScopeMask = 1 << ScopeWidget )
func ActiveScopes ¶ added in v0.151.0
ActiveScopes builds a ScopeMask from the given scopes, always including ScopeGlobal so global bindings resolve even when no window/widget context is supplied.
type ScrollView ¶
ScrollView is a viewport over a child widget whose content may be larger than the visible area. The child's own Bounds is logical (= content size); ScrollView paints the child clipped to its own Bounds, with origin shifted by -OffsetX/-OffsetY.
A thin scrollbar track (8 px) is painted on the right edge, and — when the content is wider than the viewport — along the bottom edge too, each in Theme.SurfaceAlt with a Theme.Accent thumb sized proportionally to the viewport/content ratio. Scroll(dx, dy) moves on both axes.
func NewScrollView ¶
func NewScrollView(child Widget) *ScrollView
NewScrollView builds a ScrollView around child. Call SetContentSize after construction to declare the child's logical extent so the thumb is sized correctly + scrolling is clamped.
func (*ScrollView) A11y ¶ added in v0.40.0
func (s *ScrollView) A11y() A11yInfo
A11y reports the ScrollView as a plain grouping container for its child.
func (*ScrollView) Animating ¶ added in v0.184.0
func (s *ScrollView) Animating() bool
Animating reports whether the view still needs frames: while an engine is settling, or while a finger is down and its speed is being measured.
func (*ScrollView) ChildOffset ¶ added in v0.137.0
func (s *ScrollView) ChildOffset() (int, int)
ChildOffset reports how far the scrolled content is PAINTED from where its bounds say it is — see childOffsetter for why that difference exists at all.
The rubber band counts. Draw shifts the content by the offset PLUS the overscroll, so during a bounce a reader given the offset alone would be told the content sits at the bound while it is visibly past it. Accessibility bridges read this between frames, and the whole reason this method exists is that they must not be lied to about where a control is.
func (*ScrollView) Children ¶ added in v0.137.0
func (s *ScrollView) Children() []Widget
Children yields the scrolled content.
func (*ScrollView) Draw ¶
func (s *ScrollView) Draw(p painter.Painter, theme *Theme)
Draw paints the child clipped to the viewport, then the scrollbar track + thumb on the right edge.
func (*ScrollView) HitTest ¶
func (s *ScrollView) HitTest(px, py int) bool
HitTest covers the full bounds (the scrollbar is interactive too).
func (*ScrollView) OffsetX ¶
func (s *ScrollView) OffsetX() *mvvm.Observable[int]
OffsetX/OffsetY are the scroll offsets as shared [mvvm.Observable]s; the wheel and clamping Set them. Lazily created.
func (*ScrollView) OffsetY ¶
func (s *ScrollView) OffsetY() *mvvm.Observable[int]
func (*ScrollView) OnEvent ¶ added in v0.95.0
func (s *ScrollView) OnEvent(ev Event)
OnEvent gives ScrollView native wheel + keyboard scrolling. A ScrollView measures its content in pixels rather than rows, so it converts the EventScroll Delta (expressed in ROWS) into a pixel offset using its effective font's line height — one wheel notch moves one text line. The arrow keys scroll a line, Page{Up,Down} a viewport height, and Home / End jump to the top / bottom; Scroll() clamps every result. All conversions go through Scroll(0, dy) (vertical only — horizontal scrolling stays under the host's control via Scroll directly). Any other event kind is ignored, so a ScrollView remains a passive viewport for clicks exactly as before.
func (*ScrollView) Overscroll ¶ added in v0.195.0
func (s *ScrollView) Overscroll() (x, y int)
Overscroll reports the rubber-band displacement, in pixels: negative past the start of the content, positive past its end, zero at rest. It is what Draw shifts the content by ON TOP of the scroll offset, so a caller wanting to know where the content actually sits adds it to OffsetX/OffsetY; a caller wanting the scroll position ignores it, which is why the offsets stay clamped.
func (*ScrollView) Scroll ¶
func (s *ScrollView) Scroll(dx, dy int)
Scroll mutates the offsets by (dx, dy) and clamps to [0, contentSize - viewportSize] so the thumb never falls off the track. Negative offsets are clamped to 0.
func (*ScrollView) ScrollDriven ¶ added in v0.185.0
func (s *ScrollView) ScrollDriven() bool
ScrollDriven reports whether something else owns this view's scrolling.
A MomentumScroller wrapping a ScrollView consumes the same drag the view's own content pan does, and a host has to deliver those events to the widget tree anyway — buttons and lists need them. So without this the gesture lands twice and the view scrolls at double speed: one 30-pixel drag sample moved a wrapped view 60 pixels.
A driven view therefore stands down: it neither pans nor flings on its own, and leaves OffsetX/OffsetY entirely to its driver. Everything else — wheel, arrow and page keys, scrollbar thumbs — keeps working, because those are not what the driver consumes.
func (*ScrollView) SetContentSize ¶
func (s *ScrollView) SetContentSize(w, h int)
SetContentSize tells the ScrollView how big the child's logical drawing area is. Used by Scroll() to clamp + by Draw() to size the thumb. Caller is responsible for invoking this when the child grows / shrinks.
func (*ScrollView) SetScrollDriver ¶ added in v0.185.0
func (s *ScrollView) SetScrollDriver(driver any)
SetScrollDriver hands this view's touch scrolling to driver, or takes it back when driver is nil. NewMomentumScroller calls it, so wrapping a view is all a caller has to do.
func (*ScrollView) Tick ¶ added in v0.184.0
func (s *ScrollView) Tick(dt float64)
Tick advances the touch-scroll engines by dt seconds, and turns the movement the last frame's drag accumulated into a velocity.
It makes ScrollView an Animator, so a host already calling TickTree drives the coast with no extra wiring. Velocity is measured HERE rather than in the drag handler because a toolkit.Event carries no timestamp: the only clock in the toolkit is the dt a host hands to a tick.
type Scrollbar ¶ added in v0.53.0
type Scrollbar struct {
Base
Total int // total content length along the scroll axis
Viewport int // visible length
Horizontal bool // false = vertical (the default for a scrollbar)
// contains filtered or unexported fields
}
Scrollbar is a slim, grabbable scrollbar for scrollable content: a rounded track with a thumb sized to Viewport/Total and positioned by Offset, showing where the view sits within the whole. Vertical by default; set Horizontal for a bottom scrollbar. When everything fits (Total <= Viewport) the thumb fills the track.
The thumb is a live affordance, not merely an indicator: dragging it, or clicking the track above/below it, moves Offset and fires OnScroll. Both the paint (ThumbRect) and the interaction (OnEvent) read one shared geometry (geom), the same sbGeom the embedded ScrollView/Table scrollbars use, so the drawn thumb and the drag target can never drift apart. A host that only wants a read-only indicator simply leaves OnScroll nil and never routes events to it.
func NewScrollbar ¶ added in v0.53.0
func NewScrollbar() *Scrollbar
NewScrollbar builds an empty vertical scrollbar.
func (*Scrollbar) A11y ¶ added in v0.130.0
A11y reports the Scrollbar as presentational. Scroll position is a property of the region being scrolled, not a control to announce on its own.
func (*Scrollbar) Draw ¶ added in v0.53.0
Draw paints the track and the thumb. The thumb is drawn in Theme.Border so it reads against the SurfaceAlt track in both light and dark themes.
func (*Scrollbar) Offset ¶ added in v0.53.0
func (s *Scrollbar) Offset() *mvvm.Observable[int]
Offset is the scroll offset as a shared mvvm.Observable; a drag/click Sets it (subscribers replace the old OnScroll callback). Lazily created.
func (*Scrollbar) OnEvent ¶ added in v0.104.0
OnEvent makes the thumb grabbable: an EventClick on the thumb starts a drag (recording the grab offset); an EventClick on the track above/below the thumb pages one viewport toward the click; an EventMouseDrag maps the pointer to a clamped Offset while the grab is active; EventMouseUp releases it. All of it runs through the shared scrollDrag policy against geom(), so a standalone Scrollbar drags exactly like an embedded one. A Disabled scrollbar ignores input. Coordinates are widget-local.
type SearchEntry ¶ added in v0.8.0
type SearchEntry struct {
Base
Icon func(p painter.Painter, r Rect, ink RGBA)
// contains filtered or unexported fields
}
SearchEntry is a single-line text input decorated with a leading search-prefix glyph and, when the text is non-empty, a trailing "clear" affordance on the right. Think GTK's SearchEntry: an Entry whose visual chrome hints at its role and offers a one-click reset. The widget appends printable characters, deletes on Backspace, and clears on a click in the right-side X slot. It draws a simple end-of-text caret when Focused (set by the host), measured with its own font so it always aligns; it has no cursor navigation or IME — callers needing those should reach for Entry / TextView instead.
The reactive text is MVVM-only: the current value lives in an unexported Observable exposed via SearchEntry.Text. A host binds it (Set / Subscribe / two-way); typing, Backspace, and the clear affordance Set it — there is no settable Text field and no OnChange callback.
An optional leading Icon lets the host paint a real magnifier (or any glyph) in the left prefix slot instead of the "?" text stand-in. When set, Draw invokes Icon with the prefix slot's rect + the OnSurface ink and skips the "?" text; when nil, the classic "?" stand-in is drawn, so existing callers are unaffected. This mirrors Banner.Icon.
func NewSearchEntry ¶ added in v0.8.0
func NewSearchEntry(text string) *SearchEntry
NewSearchEntry builds a SearchEntry pre-loaded with initial text. The constructor does not notify subscribers for the initial value so callers can wire a subscription after construction without a spurious notification.
func (*SearchEntry) A11y ¶ added in v0.40.0
func (s *SearchEntry) A11y() A11yInfo
A11y reports the SearchEntry as a searchbox carrying its current text.
func (*SearchEntry) ClearHitRect ¶ added in v0.187.0
func (s *SearchEntry) ClearHitRect() Rect
ClearHitRect is the finger target for the trailing "clear" affordance: the drawn clear slot clamped up to the touch minimum on each axis and centred over it, so the narrow 16-logical-pixel glyph still exposes a 44px grab under DensityTouch. At DensityCompact it equals the drawn slot byte-for-byte.
func (*SearchEntry) Draw ¶ added in v0.8.0
func (s *SearchEntry) Draw(p painter.Painter, theme *Theme)
Draw paints the entry body, the leading prefix glyph, the current text, and (when the text is non-empty) the trailing clear affordance.
func (*SearchEntry) Focused ¶ added in v0.48.0
func (f *SearchEntry) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*SearchEntry) HitRect ¶ added in v0.187.0
func (s *SearchEntry) HitRect() Rect
HitRect is the SearchEntry's field-level tap target: Bounds clamped up to the touch minimum on each axis and centred. Byte-identical to Bounds at DensityCompact.
func (*SearchEntry) OnEvent ¶ added in v0.8.0
func (s *SearchEntry) OnEvent(ev Event)
OnEvent handles character insertion (EventChar), Backspace deletion (EventKeyDown / "Backspace"), and click-to-clear in the right icon slot (EventClick, when the text is non-empty). Other events are ignored. Every mutation routes through the Text Observable's Set, notifying subscribers.
func (*SearchEntry) SetFocused ¶ added in v0.101.0
func (f *SearchEntry) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
func (*SearchEntry) Text ¶ added in v0.8.0
func (s *SearchEntry) Text() *mvvm.Observable[string]
Text is the current field value as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Text field. A keystroke edit, a Backspace, or a click on the clear affordance Sets it; subscribers are notified on change.
type SegmentedBar ¶ added in v0.36.0
type SegmentedBar struct {
Base
Segments []BarSegment
Orientation Orientation
}
SegmentedBar is a single bar split into proportional colored bands laid end to end -- e.g. a disk-usage meter showing used/free/reserved as one stacked strip. Orientation picks the layout axis: Horizontal (default) lays segments left→right, Vertical lays them bottom→top (the first segment sits at the bottom), matching ProgressBar/LevelBar's vertical convention.
func NewSegmentedBar ¶ added in v0.36.0
func NewSegmentedBar(segs []BarSegment) *SegmentedBar
NewSegmentedBar builds a SegmentedBar with the given segments.
func (*SegmentedBar) A11y ¶ added in v0.40.0
func (s *SegmentedBar) A11y() A11yInfo
A11y reports the SegmentedBar as a group. The individual segments carry no independent accessible identity (BarSegment is a plain data struct, not a Widget), so the bar as a whole is the accessible unit.
func (*SegmentedBar) Draw ¶ added in v0.36.0
func (s *SegmentedBar) Draw(p painter.Painter, theme *Theme)
Draw paints a 1-px border around the whole bar + each segment's proportional share filled with its own color, separated by a 1-px Theme.Border line. A zero (or empty) total draws a bare Theme.SurfaceAlt track. Integer-rounding leftover is pushed onto the last segment so the bands always sum to exactly the bar's length, mirroring Table.columnWidths's remainder handling.
func (*SegmentedBar) Total ¶ added in v0.36.0
func (s *SegmentedBar) Total() float64
Total sums every segment's Value.
type SelectableText ¶ added in v0.122.0
type SelectableText interface {
TextRuns() []TextRun
}
SelectableText is implemented by widgets that expose their drawn text to the selection subsystem. Runs are returned in absolute coordinates (the widget offsets by its own Bounds), so a container can concatenate the runs of all its children into one TextSelection.
type Selection ¶
Selection is a (start, end) range of TextView positions. Positions are (line, col) pairs in rune coordinates -- same model the TextView cursor uses. Start <= End in canonical order; SelectionRange normalises any (anchor, cursor) pair the caller hands it.
Selection is pure data; the TextView holds one + uses it to drive painting + range-delete + clipboard ops.
func SelectionRange ¶
SelectionRange returns a canonical Selection from an anchor + a cursor: whichever pair is "earlier" in document order becomes the start.
type Size ¶ added in v0.124.0
type Size struct{ W, H int }
Size is a width/height pair in painter units (pixels for a PixelPainter, cells for a CellPainter). It is the dimensional companion of Rect for the places a widget needs an extent without a position — e.g. the fixed cell footprint a VirtualGrid reflows its items into. Kept a plain value type (no methods, no state) so it costs nothing and composes freely.
type Skeleton ¶ added in v0.8.0
type Skeleton struct {
Base
Kind SkeletonKind
Lines int
// LineH / LineGap / LastFrac tune SkeletonText. Zero (the default)
// falls through to SkeletonLineH / SkeletonLineGap / SkeletonLastFrac
// so an untuned SkeletonText renders like a paragraph of body text.
LineH int
LineGap int
LastFrac float64
// Radius is the corner radius for SkeletonRect and for SkeletonText
// bars. Zero falls through to a shape-appropriate default
// (SkeletonRectRadius for a rect; a third of the line height for a
// text bar). SkeletonCircle ignores it (a circle is fully rounded).
Radius int
// Animated turns the shimmer band on. Phase (0..1) is the sweep
// position: 0 parks the band just off the leading edge (flat grey),
// rising to 1 sweeps it off the trailing edge.
Animated bool
Phase float64
}
Skeleton is a placeholder rendered while real content is loading. Every Skeleton fills in Theme.SurfaceAlt so the shape reads as "content coming" without demanding attention.
When Animated is set, Draw overlays a diagonal shimmer band — a lighter tint that sweeps across the base grey. The band position is Phase (0..1); the consumer advances Phase every frame (typically via SetPhase(elapsed*speed), which wraps for you). A stopped Skeleton (Animated == false, the zero value) renders flat grey, so the widget is cheap when the host has no animation loop.
A caller typically swaps a Skeleton for the real widget once data arrives; there is no Visible flag because dropping the widget from the tree is cheaper than gating every Draw on a bool.
Skeleton is passive: it displays and does not respond to input.
func NewSkeleton ¶ added in v0.8.0
func NewSkeleton(kind SkeletonKind, lines int) *Skeleton
NewSkeleton constructs a Skeleton of the given kind + line count. The lines argument is honoured only when kind == SkeletonText; if it is non-positive in that case it defaults to 3 (a natural stand-in for a paragraph). For the non-text kinds the value is stored verbatim but ignored by Draw.
func (*Skeleton) A11y ¶ added in v0.40.0
A11y reports the Skeleton as a decorative presentation element -- a screen reader should not announce a loading placeholder as content.
func (*Skeleton) Animating ¶ added in v0.155.0
Animating reports whether the skeleton still needs frames: true exactly when its shimmer is Animated (a static placeholder needs no repaint).
func (*Skeleton) Draw ¶ added in v0.8.0
Draw paints the placeholder appropriate for Kind, then (when Animated) sweeps the shimmer band over each filled region.
func (*Skeleton) SetPhase ¶ added in v0.109.0
SetPhase sets the shimmer sweep position and switches the shimmer on. t may be any float (e.g. elapsedSeconds*speed); it is wrapped into [0,1) so the caller can feed a monotonically increasing clock without tracking the cycle. Returns the Skeleton so the call chains.
func (*Skeleton) Tick ¶ added in v0.155.0
Tick advances the shimmer sweep by deltaSeconds, wrapping Phase modulo 1 so it stays bounded. It advances only while Animated (a flat, un-animated Skeleton needs no frames), matching what Animating reports. Together they make an animated Skeleton an Animator, driven by TickTree / TreeAnimating — the per-frame counterpart of the absolute-clock SetPhase.
type SkeletonGroup ¶ added in v0.109.0
type SkeletonGroup struct {
Base
// Animated + Phase mirror Skeleton; SetPhase drives both and they
// cascade to every child at Draw time.
Animated bool
Phase float64
// contains filtered or unexported fields
}
SkeletonGroup composes several primitive Skeletons into one loading placeholder — an avatar + text lines + a media block, a whole loading page, etc. It is a thin container: Draw positions each child relative to the group's Bounds and forwards the group's shimmer Phase so the whole composition gleams in sync.
SkeletonGroup is passive and decorative (A11y reports it as a presentation element, like the primitive Skeleton).
func NewPageSkeleton ¶ added in v0.109.0
func NewPageSkeleton(bounds Rect) *SkeletonGroup
NewPageSkeleton builds a loading web-page placeholder inside bounds: a top bar, alternating paragraph line-groups and media blocks. This is what a webengine browser client shows while the browserproxy fetches a page. It is a preset (a composition of the primitives), not a bespoke widget, so it is reusable + inspectable via Items().
func NewSkeletonCard ¶ added in v0.109.0
func NewSkeletonCard(bounds Rect) *SkeletonGroup
NewSkeletonCard builds a content-card skeleton inside bounds: a circle avatar top-left, a two-line text header beside it, and a rounded media block filling the rest — the classic "post is loading" placeholder. It is a composition of the primitives, so callers can inspect / tweak the children via Items().
func (*SkeletonGroup) A11y ¶ added in v0.109.0
func (g *SkeletonGroup) A11y() A11yInfo
A11y reports the SkeletonGroup as a decorative presentation element, for the same reason as the primitive Skeleton: a composed loading placeholder is not content.
func (*SkeletonGroup) Add ¶ added in v0.109.0
func (g *SkeletonGroup) Add(s *Skeleton, local Rect) *SkeletonGroup
Add appends a primitive Skeleton at the given LOCAL rectangle and returns the group so calls chain.
func (*SkeletonGroup) Animating ¶ added in v0.155.0
func (g *SkeletonGroup) Animating() bool
Animating reports whether the group still needs frames: true exactly when its shimmer is Animated.
func (*SkeletonGroup) Draw ¶ added in v0.109.0
func (g *SkeletonGroup) Draw(p painter.Painter, theme *Theme)
Draw positions each child relative to the group's Bounds, forwards the shimmer state, and paints it.
func (*SkeletonGroup) Items ¶ added in v0.109.0
func (g *SkeletonGroup) Items() []SkeletonItem
Items returns the group's children with their local rectangles, for inspection / layout tests.
func (*SkeletonGroup) SetPhase ¶ added in v0.109.0
func (g *SkeletonGroup) SetPhase(t float64) *SkeletonGroup
SetPhase sets the group's shimmer position (wrapped into [0,1)) and switches the shimmer on for every child. Returns the group so calls chain. The consumer advances this every frame.
func (*SkeletonGroup) Tick ¶ added in v0.155.0
func (g *SkeletonGroup) Tick(deltaSeconds float64)
Tick advances the group's shimmer sweep by deltaSeconds, wrapping Phase modulo 1. It advances only while Animated and cascades to every child at Draw time (Draw copies the group's Phase into each child), so ticking the group is enough to animate the whole composition. Together with Animating this makes SkeletonGroup an Animator driven by TickTree / TreeAnimating.
type SkeletonItem ¶ added in v0.109.0
SkeletonItem is one positioned child of a SkeletonGroup: a primitive Skeleton plus its rectangle in the group's LOCAL coordinate system (relative to the group's top-left).
type SkeletonKind ¶ added in v0.8.0
type SkeletonKind int
SkeletonKind selects the placeholder shape. The kinds cover the dominant loading-state patterns:
- SkeletonText draws N rounded bars stacked vertically (a paragraph or a list row).
- SkeletonRect draws one rounded block — the modern "media / card body loading" affordance, corner-radius configurable.
- SkeletonCircle draws a true circle — an avatar / status-dot placeholder.
- SkeletonAvatar / SkeletonBlock are the original pixel-exact swap-parity variants (a three-band pill matching Avatar, and a square inset fill). They are kept for callers that swap a Skeleton for the Avatar / Block widget pixel-for-pixel.
Every kind fills in Theme.SurfaceAlt (the muted "content coming" tone) and, when Animated, is swept by a diagonal shimmer band (a lighter tint) whose position is driven by Phase.
const ( // SkeletonText draws Lines horizontal bars stacked vertically. The // last bar is LastFrac of the width so the shape reads as "wrapped // text" rather than a solid block. SkeletonText SkeletonKind = iota // SkeletonAvatar draws a rounded square in SurfaceAlt matching the // Avatar widget's three-band pill — so a Skeleton row lines up // pixel-for-pixel with the real Avatar it will be swapped for. SkeletonAvatar // SkeletonBlock draws one filled square rectangle covering Bounds() // inset by SkeletonLinePad — the original media-thumbnail affordance. SkeletonBlock // SkeletonRect draws one rounded-corner block covering Bounds(). The // corner radius is Skeleton.Radius (default SkeletonRectRadius). SkeletonRect // SkeletonCircle draws a true circle inscribed in (and centred // within) Bounds() — the avatar placeholder for the rounded family. SkeletonCircle )
type SourceItem ¶ added in v0.136.0
SourceItem is one row of a SourceList: an optional leading icon and a label. Key is an opaque caller-supplied identity (a path, a mailbox id, ...) the host can read back after OnSelect; the widget itself never interprets it.
type SourceList ¶ added in v0.136.0
type SourceList struct {
Base
// Sections is the ordered list of labelled groups. Mutating it and calling
// SetBounds (or letting the next SetBounds run) re-lays the rows out.
Sections []SourceSection
// OnSelect fires after a click selects an item row, with the section index
// and the row index within that section. Nil-guarded.
OnSelect func(section, row int)
// OnReorder fires after a successful drag-reorder within a section, with the
// section index and the row's original + final indices. Nil-guarded.
OnReorder func(section, from, to int)
// contains filtered or unexported fields
}
SourceList is a macOS-style "source list" (an NSOutlineView sidebar): one or more labelled sections, each a run of rows carrying a leading icon and a label, with the selected row drawn as a rounded accent pill. A section can be marked Reorderable, in which case its rows can be dragged to reorder within that section (via the toolkit's DragSource/DropTarget contract). It generalizes the file-manager sidebar / mail-and-settings navigator: a flat ListBox cannot express section headers or per-section reorderability, which is exactly the gap SourceList fills.
Layout: a thin panel filled with Theme.SurfaceAlt, a hairline Theme.Border on its right edge, then top-to-bottom a section header (drawn in muted ink, and only when the section has a non-empty Title) followed by its item rows. Each item row shows its icon (when non-nil) left-aligned, then the label elided to the remaining width; the selected row paints a Theme.Accent pill behind it and switches the ink to Theme.Background. All painting is clipped to the widget bounds, so a panel shorter than its content never bleeds a row below its edge.
Selection + navigation: a click selects the row under the pointer and fires OnSelect(section, row). Selected / SetSelected read and drive the highlighted row programmatically.
Drag-to-reorder: pressing a row in a Reorderable section arms a drag whose payload is SourceRowDragPrefix + "<section>:<row>" (see DragData); a host wires its native pointer gestures to the toolkit's EventDragMove / EventDragLeave / EventDrop, and the SourceList tracks the pressed row, paints an insertion line on EventDragMove, and reorders the section's items on EventDrop, firing OnReorder. A press on a non-reorderable section arms nothing, so those rows can be selected but never reordered.
Example ¶
ExampleSourceList builds a two-section sidebar, selects a row and reports it.
sl := NewSourceList(
SourceSection{Title: "Favourites", Reorderable: true, Items: []SourceItem{
{Label: "Documents", Key: "docs"},
{Label: "Downloads", Key: "dl"},
}},
SourceSection{Title: "Locations", Items: []SourceItem{
{Label: "Home", Key: "home"},
}},
)
sl.SetBounds(Rect{X: 0, Y: 0, W: 200, H: 300})
sl.Draw(newP(makeSurface(200, 300), 200), DefaultLight())
sl.OnEvent(Event{Kind: EventClick, Y: 40}) // select the first favourite
sec, row := sl.Selected()
fmt.Printf("selected section %d row %d\n", sec, row)
Output: selected section 0 row 0
func NewSourceList ¶ added in v0.136.0
func NewSourceList(sections ...SourceSection) *SourceList
NewSourceList builds a SourceList over sections. Nothing is selected initially (Selected returns -1, -1) and no press is armed; call SetBounds to lay the rows out before drawing.
func (*SourceList) A11y ¶ added in v0.136.0
func (s *SourceList) A11y() A11yInfo
A11y reports the SourceList as navigation. Value is the selected item's label, or empty when nothing is selected.
func (*SourceList) AcceptsDrop ¶ added in v0.136.0
func (s *SourceList) AcceptsDrop(payload string) bool
AcceptsDrop reports whether payload is one of this widget's own reorder payloads. It makes the SourceList a DropTarget for its own rows.
func (*SourceList) DragData ¶ added in v0.136.0
func (s *SourceList) DragData() string
DragData reports the reorder payload for the pressed row (a Reorderable section's row), or "" when no reorderable press is armed. It makes the SourceList a DragSource.
func (*SourceList) Draw ¶ added in v0.136.0
func (s *SourceList) Draw(p painter.Painter, theme *Theme)
Draw paints the panel, its sections and rows, and (while dragging) the reorder insertion line, clipped to the widget bounds.
func (*SourceList) OnEvent ¶ added in v0.136.0
func (s *SourceList) OnEvent(ev Event)
OnEvent selects on a click, arms/drives a reorder drag on a Reorderable section, and is inert while Disabled.
func (*SourceList) Selected ¶ added in v0.136.0
func (s *SourceList) Selected() (section, row int)
Selected returns the highlighted item as (section, row), or (-1, -1) when nothing is selected.
func (*SourceList) SetBounds ¶ added in v0.136.0
func (s *SourceList) SetBounds(r Rect)
SetBounds records the widget bounds and recomputes the row layout.
func (*SourceList) SetSelected ¶ added in v0.136.0
func (s *SourceList) SetSelected(section, row int)
SetSelected highlights the item at (section, row). An out-of-range pair clears the selection to (-1, -1).
type SourceSection ¶ added in v0.136.0
type SourceSection struct {
Title string
Items []SourceItem
Reorderable bool
}
SourceSection is a labelled group of SourceItems. When Reorderable is true its rows can be dragged to reorder within the section; when false (the default) its rows are selectable but fixed in order.
type Sparkline ¶ added in v0.82.0
type Sparkline struct {
Base
// Values is the data series, plotted left-to-right and auto-scaled between
// its own min and max across the bounds height.
Values []float64
// Kind selects the form: SparkLine (polyline, default) or SparkBar.
Kind SparkKind
// Fill is the ink for the line/bars. The zero value (A == 0) inherits the
// theme's Accent colour.
Fill RGBA
// ShowLast emphasises the final data point: a small dot on a SparkLine, a
// brighter final bar on a SparkBar.
ShowLast bool
// contains filtered or unexported fields
}
Sparkline is a tiny, axis-less inline trend chart -- the kind embedded in a KPI card, a table cell or a status row to show a series' shape at a glance. Unlike LineChart / BarChart it draws no axes, labels or gridlines: the whole bounds is plot area, and the Values are auto-scaled to fit it. Display-only.
It renders through painter.Painter, so the same spark draws as anti-aliased pixels (WUI/GUI) or promoted cells (TUI). An empty series draws nothing; a single value renders as a dot.
func NewSparkline ¶ added in v0.82.0
NewSparkline builds a SparkLine over the given values.
func (*Sparkline) A11y ¶ added in v0.105.0
A11y reports the Sparkline as an img carrying its data-point count (it plots a single series, like LineChart).
func (*Sparkline) Draw ¶ added in v0.82.0
Draw paints the spark: nothing for an empty series or a sub-pixel bounds, a dot for a lone value, otherwise a polyline (SparkLine) or bar row (SparkBar).
func (*Sparkline) HitTest ¶ added in v0.82.0
HitTest returns false unconditionally: a Sparkline is decorative, like Label.
func (*Sparkline) Hover ¶ added in v0.90.0
func (s *Sparkline) Hover() *mvvm.Observable[bool]
Hover is the reactive "crosshair shown" flag as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Hover field. A pointer move over the spark Sets it true, a move off Sets it false; Draw reads it. Lazily initialised to false when first accessed.
func (*Sparkline) HoverIndex ¶ added in v0.90.0
func (s *Sparkline) HoverIndex() *mvvm.Observable[int]
HoverIndex is the reactive index of the hovered sample as a shared mvvm.Observable: a host binds it — there is no settable HoverIndex field. A pointer move Sets it to the nearest data point; Draw reads it. Lazily initialised to 0 when first accessed.
type SpinButton ¶
SpinButton is an integer input with `+` and `−` buttons on the right. Click `+` adds Step, click `−` subtracts Step (clamped to [Min, Max]). The value is rendered as a decimal string in the left portion of the body.
Min, Max and Step are config; the reactive value is MVVM-only: it lives in an unexported Observable exposed via SpinButton.Value. A host binds it (Set / Subscribe / two-way) — there is no settable Value field. A +/− click or a stepper key Sets it (clamped to [Min, Max]); subscribers are notified.
func NewSpinButton ¶
func NewSpinButton(min, max, initial, step int) *SpinButton
NewSpinButton builds a SpinButton spanning [min, max] with the given initial + step. Step <= 0 is clamped to 1 so clicks never no-op silently.
func (*SpinButton) A11y ¶ added in v0.40.0
func (s *SpinButton) A11y() A11yInfo
A11y reports the SpinButton as a spinbutton carrying its numeric value, both as a Value string and as the numeric Min/Max/Now range triple.
func (*SpinButton) Draw ¶
func (s *SpinButton) Draw(p painter.Painter, theme *Theme)
Draw paints the body (with the value text) + the two stacked buttons on the right.
func (*SpinButton) Focused ¶ added in v0.101.0
func (f *SpinButton) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*SpinButton) OnEvent ¶
func (s *SpinButton) OnEvent(ev Event)
OnEvent: click on the upper-right button increments; click on the lower-right button decrements.
func (*SpinButton) SetFocused ¶ added in v0.101.0
func (f *SpinButton) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
func (*SpinButton) SetValue ¶
func (s *SpinButton) SetValue(v int)
SetValue clamps v to [Min, Max] and Sets the Value Observable — the shared mutate path for a +/− button click and every stepper key. Subscribers are notified on change (an unchanged value is a no-op, per mvvm.Observable).
func (*SpinButton) Value ¶
func (s *SpinButton) Value() *mvvm.Observable[int]
Value is the current value as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Value field. A +/− click or a stepper key Sets it (clamped to [Min, Max]); subscribers are notified.
type Spinner ¶
type Spinner struct {
Base
Phase float64 // 0..1, full cycle (config; host-driven per frame)
Style SpinnerStyle // look; zero value = SpinnerHand (config)
// contains filtered or unexported fields
}
Spinner is an indeterminate loading indicator. When active, Draw paints the selected Style advanced by Phase in Theme.Accent. The caller drives Phase via Tick(dt) so the animation cadence stays tied to the host's frame loop (no goroutine, no timer). The reactive running state is MVVM-only: it lives in an unexported Observable exposed via Spinner.Active; Phase and Style stay plain config fields.
func NewSpinner ¶
func NewSpinner() *Spinner
NewSpinner builds a Spinner stopped at Phase=0 in the default (hand) style.
func (*Spinner) A11y ¶ added in v0.40.0
A11y reports the Spinner as a status region carrying "busy" while active.
func (*Spinner) Active ¶
func (s *Spinner) Active() *mvvm.Observable[bool]
Active is the spinner's running state as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Active field. The spinner draws and reports itself animating exactly while it is true.
func (*Spinner) Animating ¶ added in v0.155.0
Animating reports whether the spinner still needs frames: true exactly when it is Active, so a host stops repainting once the spinner is stopped. It makes Spinner an Animator, driven by TickTree / TreeAnimating.
type SpinnerStyle ¶ added in v0.110.0
type SpinnerStyle int
SpinnerStyle selects an indeterminate-spinner look. The zero value is SpinnerHand, so an untouched Spinner keeps the original rotating-hand rendering.
const ( // SpinnerHand is a single rotating radial line from the centre (the // original, and the zero-value default). SpinnerHand SpinnerStyle = iota // SpinnerDots is a ring of dots orbiting the centre, the leading dot in // Accent and the trail fading toward SurfaceAlt. SpinnerDots // SpinnerRing is a comet-like arc sweeping around the circle, its head in // Accent fading to SurfaceAlt along the tail. SpinnerRing // SpinnerBars is a row of vertical bars whose heights pulse out of phase, // like an audio equalizer. SpinnerBars )
type SplitButton ¶ added in v0.9.0
type SplitButton struct {
Base
Label string
Arrow bool
OnClick func()
OnArrow func()
// contains filtered or unexported fields
}
SplitButton is a two-part button: a primary action face on the left plus an attached narrow arrow face on the right that opens a secondary action (typically a menu). Mirrors GTK's SplitButton and GtkMenuButton — one click target for the default action, a separate click target for "show the alternatives".
When Arrow is false the arrow slot is not drawn and OnArrow is ignored — the widget degrades to a solid Accent-face action button, so a caller can toggle the split visual at runtime without swapping widgets.
The two faces share theme.Accent as their fill; the label + arrow glyph render in accentInk(theme) — theme.Extra["OnAccent"] with a fall-through to theme.Background, matching Button + Table + Avatar.
func NewSplitButton ¶ added in v0.9.0
func NewSplitButton(label string, onClick func()) *SplitButton
NewSplitButton constructs a SplitButton with Arrow enabled by default and OnArrow left nil. onClick may be nil (a no-op primary action is still rendered).
func (*SplitButton) A11y ¶ added in v0.40.0
func (b *SplitButton) A11y() A11yInfo
A11y reports the SplitButton as a button named by its label.
func (*SplitButton) Draw ¶ added in v0.9.0
func (s *SplitButton) Draw(p painter.Painter, theme *Theme)
Draw paints the two-slot face. Both slots fill in theme.Accent; when Arrow is true a 1-px theme.Border separator is drawn between them and a small "v" glyph is centred in the arrow slot. Ink for both the label and the arrow glyph is accentInk(theme) so the text stays legible against the Accent face and honours any theme.Extra["OnAccent"] override.
func (*SplitButton) Focused ¶ added in v0.101.0
func (f *SplitButton) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*SplitButton) HitRect ¶ added in v0.187.0
func (s *SplitButton) HitRect() Rect
HitRect is the split button's interactive rectangle: its drawn Bounds clamped up to the density hit-target and centred over them (see [touchHitRect]). Byte-identical to Bounds under DensityCompact; a finger-sized target under DensityTouch. The internal main/arrow split stays measured against the scaled arrow width, so which callback a press fires is unaffected by the clamp.
func (*SplitButton) HitTest ¶ added in v0.187.0
func (s *SplitButton) HitTest(px, py int) bool
HitTest reports whether a surface point falls on the split button's (touch-clamped) hit rect.
func (*SplitButton) OnEvent ¶ added in v0.9.0
func (s *SplitButton) OnEvent(ev Event)
OnEvent routes clicks to OnClick or OnArrow depending on where the click landed. ev.X is widget-local; when Arrow is true a click in the right SplitButtonArrowW pixels fires OnArrow, otherwise OnClick. Both callbacks are nil-safe. Non-click event kinds are ignored.
func (*SplitButton) SetFocused ¶ added in v0.101.0
func (f *SplitButton) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
type Spreadsheet ¶ added in v0.180.0
type Spreadsheet struct {
Base
// OnCellChange, when set, fires after a committed edit with the cell and the
// raw text that was stored — the seam a view model observes.
OnCellChange func(ref formula.Ref, raw string)
// contains filtered or unexported fields
}
Spreadsheet is an A1-addressed formula grid: columns A, B, ..., Z, AA, ... across 1-based rows, each cell holding a literal (number or text) or a leading-"=" formula. It composes the toolkit's shared grid primitives — the same fillRect / strokeRect / cellTextX painting, the stock Entry as the inline editor, and the scrollDrag / sbGeom scrollbar machinery ScrollView and Table already use — rather than duplicating Table's data-grid rendering.
It is a DISTINCT widget from Table, not a "formula mode" bolted onto it, because the two contracts are orthogonal. Table is a data grid: named, individually-sized, sortable/groupable columns over Rows [][]string. A spreadsheet is uniform A1 cells over a formula engine with a dependency graph and recomputation. Forcing Table's Columns/Rows model to also carry cell references, formulas and recalculation would bloat exactly the data-grid contract that makes Table simple; a separate widget keeps both clean while still sharing the low-level painting and scrolling helpers.
The formula engine (parse, evaluate, dependency-ordered recalc, cycle detection) lives in internal/formula; the widget is a thin view over a formula.Model.
func NewSpreadsheet ¶ added in v0.180.0
func NewSpreadsheet(cols, rows int) *Spreadsheet
NewSpreadsheet builds an empty cols x rows sheet with cell A1 active. Negative dimensions clamp to 0 (the underlying model's contract).
func (*Spreadsheet) A11y ¶ added in v0.180.0
func (s *Spreadsheet) A11y() A11yInfo
A11y reports the Spreadsheet as a grid named by its active cell's A1 address, with the cell's displayed value.
func (*Spreadsheet) Active ¶ added in v0.180.0
func (s *Spreadsheet) Active() (col, row int)
Active reports the currently selected cell.
func (*Spreadsheet) BeginEdit ¶ added in v0.180.0
func (s *Spreadsheet) BeginEdit()
BeginEdit opens an inline editor over the active cell, seeded with its current raw text — the command entry point Enter / F2 and a view model use.
func (*Spreadsheet) CancelEdit ¶ added in v0.180.0
func (s *Spreadsheet) CancelEdit()
CancelEdit discards the open editor without touching the cell. A no-op when no edit is open.
func (*Spreadsheet) CellDisplay ¶ added in v0.180.0
func (s *Spreadsheet) CellDisplay(col, row int) string
CellDisplay is the computed text shown in cell (col,row).
func (*Spreadsheet) CellRaw ¶ added in v0.180.0
func (s *Spreadsheet) CellRaw(col, row int) string
CellRaw is the raw text stored in cell (col,row) — the formula or literal a user typed, which the editor re-opens.
func (*Spreadsheet) Cols ¶ added in v0.180.0
func (s *Spreadsheet) Cols() int
Cols reports the sheet's column count.
func (*Spreadsheet) CommitEdit ¶ added in v0.180.0
func (s *Spreadsheet) CommitEdit()
CommitEdit stores the open editor's text into the active cell (recomputing dependents), fires OnCellChange, and closes the editor. A no-op when no edit is open.
func (*Spreadsheet) Draw ¶ added in v0.180.0
func (s *Spreadsheet) Draw(p painter.Painter, theme *Theme)
Draw paints the sheet: the cell grid (clipped to its viewport), the frozen column-letter and row-number header bands, the active-cell selection box, the scrollbars, and finally the inline editor overlay when a cell is being edited.
func (*Spreadsheet) Editing ¶ added in v0.180.0
func (s *Spreadsheet) Editing() bool
Editing reports whether an inline cell edit is open.
func (*Spreadsheet) Focused ¶ added in v0.180.0
func (f *Spreadsheet) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*Spreadsheet) OnEvent ¶ added in v0.180.0
func (s *Spreadsheet) OnEvent(ev Event)
OnEvent drives selection, scrolling and inline editing.
While an editor is open it owns the keyboard: characters and edit keys route to it, Enter commits and moves down, Tab commits and moves right, Escape cancels, and a click elsewhere commits first and then selects the clicked cell. With no editor open, arrow keys move the active cell, Enter/F2 opens an editor seeded with the cell's current text, a printable character opens one seeded with that character, the wheel scrolls, and a scrollbar press/drag scrolls; a grid click selects the cell under the pointer.
func (*Spreadsheet) Rows ¶ added in v0.180.0
func (s *Spreadsheet) Rows() int
Rows reports the sheet's row count.
func (*Spreadsheet) ScrollBy ¶ added in v0.180.0
func (s *Spreadsheet) ScrollBy(dCol, dRow int)
ScrollBy shifts the visible window by (dCol, dRow) cells, clamped.
func (*Spreadsheet) ScrollOffset ¶ added in v0.180.0
func (s *Spreadsheet) ScrollOffset() (col, row int)
ScrollOffset reports the top-left visible cell (the current scroll position), in cell units — the observable a view model (or a test) reads.
func (*Spreadsheet) SetCell ¶ added in v0.180.0
func (s *Spreadsheet) SetCell(col, row int, raw string)
SetCell stores raw (a literal or a leading-"=" formula) in cell (col,row) and recomputes every dependent cell. An out-of-bounds cell is a no-op.
func (*Spreadsheet) SetFocused ¶ added in v0.180.0
func (f *Spreadsheet) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
type Stack ¶
Stack holds N named pages (Widgets) but shows only ONE at a time -- the page named by Visible. Use AddPage / SetVisible to navigate. Events route to the visible page only.
Suitable for application "screens" (settings vs main vs about), wizard steps, or anywhere the user expects a CLEAN swap with no transition.
func NewStack ¶
func NewStack() *Stack
NewStack builds an empty Stack with no pages + no visible name.
func (*Stack) A11y ¶ added in v0.130.0
A11y reports the Stack as presentational: only the visible child is content.
func (*Stack) AddPage ¶
AddPage registers a page under name. If this is the first page, it auto-becomes Visible so an unconfigured Stack still draws something.
func (*Stack) SetBounds ¶
SetBounds also propagates to the visible page so it fills the Stack's rect. Other pages have stale bounds until SetVisible brings them forward -- they re-bound at draw time.
func (*Stack) SetVisible ¶
SetVisible swaps the showing page. Names not in Pages are silently ignored so the caller can SetVisible blind.
func (*Stack) Visible ¶
func (s *Stack) Visible() *mvvm.Observable[string]
Visible names the currently-shown page as a shared mvvm.Observable: AddPage/SetVisible Set it, and Draw/OnEvent read it live. Lazily created, defaulting to no visible page (the empty name).
type Stat ¶ added in v0.9.0
type Stat struct {
Base
// Title is the static caption drawn above the metric (config).
Title string
// contains filtered or unexported fields
}
Stat is a compact KPI card — a small dim Title on top, a large Value in the middle drawn with a one-pixel horizontal thickening pass to fake a bold weight, and an optional Change indicator at the bottom coloured by Trend. Modelled on DaisyUI's `<div class="stat">` block: three vertically stacked text rows on a bordered Surface panel.
Stat is passive display only — the parent view supplies any interaction (a click-through link, a tooltip) as a separate widget on top. HitTest / OnEvent stay as Base defaults.
The reactive metric is MVVM-only: the current Value, Change and Trend each live in an unexported mvvm.Observable exposed via the like-named accessor. A host updates the tile at runtime with s.Value().Set("…") / s.Change().Set("…") / s.Trend().Set(StatUp); subscribers are notified. Only the static Title stays a plain config field.
func NewStat ¶ added in v0.9.0
NewStat constructs a Stat with the given title + value. Change defaults to "" (no change row painted) and Trend defaults to StatFlat; a host enables the bottom row via s.Change().Set(…) and s.Trend().Set(…).
func (*Stat) A11y ¶ added in v0.40.0
A11y reports the Stat as a group named by its title, carrying its headline value.
func (*Stat) Change ¶ added in v0.9.0
func (s *Stat) Change() *mvvm.Observable[string]
Change is the optional bottom-row indicator ("+12%", "-4%") as a shared mvvm.Observable. An empty string paints no change row; a host mutates it via Set.
func (*Stat) Draw ¶ added in v0.9.0
Draw paints the surface fill, the three text rows and finally the outer border stroke. Draw order matches Card (fill, decorations, border last) so the 1-px border always sits on top and clips overlapping ink.
The Value row is drawn TWICE — once at (x, y) and again at (x+1, y) — to fake a bold weight. Since the 5x7 bitmap font ships one stroke width, the double-draw thickens each column by one pixel so the Value visually outweighs the surrounding Title + Change rows without a second glyph table.
func (*Stat) Trend ¶ added in v0.9.0
func (s *Stat) Trend() *mvvm.Observable[StatTrend]
Trend selects the Change row's semantic direction (StatFlat / StatUp / StatDown) as a shared mvvm.Observable. A host mutates it via Set; Draw reads it via Get to colour the change ink.
func (*Stat) Value ¶ added in v0.9.0
func (s *Stat) Value() *mvvm.Observable[string]
Value is the headline metric as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Value field. Draw and A11y read it via Get.
type StatTrend ¶ added in v0.9.0
type StatTrend int
StatTrend selects the semantic direction of a Stat's optional Change indicator. StatFlat renders the change text in the theme's dim border ink (the same "muted-ink" convention HeaderBar uses for its subtitle); StatUp and StatDown paint fixed green and red shades so the direction reads at a glance regardless of the app's accent choice. Widgets that want a neutral or accent-tinted change value use StatFlat and let the theme drive the tone.
const ( // StatFlat is the neutral "no direction" trend. Change ink comes // from Theme.Border so the value blends with the surrounding // muted labels. StatFlat StatTrend = iota // StatUp signals a positive change ("+12%", "revenue up"). Ink is // a fixed sea-green so up-trends read the same across every theme. StatUp // StatDown signals a negative change ("-4%", "errors up"). Ink is // a fixed brick-red so down-trends read the same across every theme. StatDown )
type StatusArea ¶ added in v0.80.0
type StatusArea struct {
Base
// Icons are laid out in order. Mutate through Add or set directly then call
// SetBounds to re-flow.
Icons []*StatusIcon
// Gap is the spacing between cells; zero selects StatusAreaGap. A negative
// value is clamped to 0 (flush icons).
Gap int
// IconSize is each cell's square dimension; zero selects StatusIconSize.
IconSize int
// Background, when its alpha is non-zero, is painted as a solid plate behind
// the whole icon row (the full area Bounds) before the icons draw — so a host
// gets a tray bar without drawing the plate itself. The zero value (A==0)
// keeps the original fully-transparent behaviour: only the icons paint.
Background RGBA
}
StatusArea is a tray container: it lays out N StatusIcons in a left-to-right row, each in a square IconSize cell centred vertically in the area, with Gap pixels between them — a mini Dock/HBox specialised for status indicators. It routes a pointer event to the icon whose cell contains it (in the icon's local space), so each StatusIcon's OnClick/OnRightClick fires correctly.
func NewStatusArea ¶ added in v0.80.0
func NewStatusArea(icons ...*StatusIcon) *StatusArea
NewStatusArea builds a StatusArea over the given icons (any number, including none). Call SetBounds to lay them out.
func (*StatusArea) A11y ¶ added in v0.130.0
func (a *StatusArea) A11y() A11yInfo
A11y reports the StatusArea as a toolbar of status icons.
func (*StatusArea) Add ¶ added in v0.80.0
func (a *StatusArea) Add(ic *StatusIcon)
Add appends an icon and re-flows the row against the current Bounds.
func (*StatusArea) Draw ¶ added in v0.80.0
func (a *StatusArea) Draw(p painter.Painter, theme *Theme)
Draw paints the optional Background plate behind the row (when its alpha is non-zero), then every icon in insertion order.
func (*StatusArea) OnEvent ¶ added in v0.80.0
func (a *StatusArea) OnEvent(ev Event)
OnEvent forwards to the first icon whose cell contains the point, translating the event into that icon's local space.
func (*StatusArea) SetBounds ¶ added in v0.80.0
func (a *StatusArea) SetBounds(r Rect)
SetBounds places each icon in a square cell along the row, vertically centred in the area's height.
type StatusIcon ¶ added in v0.80.0
type StatusIcon struct {
Base
// Icon paints the glyph when Pixels is not a valid image. May be nil.
Icon IconFunc
// Pixels is an optional RGBA image (IW*IH*4 bytes). When valid it is drawn
// instead of Icon, aspect-preserved and centred in the cell.
Pixels []byte
IW, IH int
// Ink overrides the icon colour; the zero RGBA (A==0) falls back to
// Theme.OnSurface.
Ink RGBA
// Badge, when non-nil, is painted in the top-right corner (an unread count,
// a status dot). It is auto-sized + positioned by Draw.
Badge *Badge
// Tooltip is the hover text the host surfaces (via a Tooltip widget). The
// StatusIcon only stores it; it does not pop the bubble itself.
Tooltip string
// OnClick fires on a primary EventClick; OnRightClick on a secondary one
// (Event.Code == StatusIconSecondary). Both are nil-safe.
OnClick func()
OnRightClick func()
}
StatusIcon is a small tray/status-area indicator: it paints one icon (an IconFunc vector glyph or an RGBA image) at a fixed cell, optionally overlays a Badge in the top-right corner, carries a Tooltip string the host shows on hover, and fires OnClick / OnRightClick when activated.
The cell has no background of its own, so whatever the tray sits on (a panel fill, a Wallpaper) shows through around the glyph — the least-surprising look for a status-area icon. A caller wanting a chip behind the glyph draws it under the StatusIcon.
Image vs icon: when Pixels is a valid RGBA buffer it is drawn (aspect- preserving, centred — a ScaleFit Image) and Icon is ignored; otherwise the Icon func is called with Ink (falling back to Theme.OnSurface). Either may be absent, in which case only the optional Badge paints.
Auto-sizing: if Bounds().W is zero the first Draw() resizes the widget to StatusIconSize x StatusIconSize (H preserved when already non-zero). A pre-sized Bounds is honoured verbatim so a fixed tray column doesn't shift.
func NewStatusIcon ¶ added in v0.80.0
func NewStatusIcon(icon IconFunc) *StatusIcon
NewStatusIcon builds a StatusIcon that paints the given vector icon. Bounds default to zero so the first Draw() auto-sizes the cell.
func NewStatusIconImage ¶ added in v0.80.0
func NewStatusIconImage(pixels []byte, w, h int) *StatusIcon
NewStatusIconImage builds a StatusIcon that paints the given RGBA image (length must equal w*h*4). The image is drawn aspect-preserved + centred.
func (*StatusIcon) A11y ¶ added in v0.130.0
func (i *StatusIcon) A11y() A11yInfo
A11y reports the StatusIcon as a status region. Its badge, when present, carries the count that makes the icon worth announcing at all.
func (*StatusIcon) Draw ¶ added in v0.80.0
func (s *StatusIcon) Draw(p painter.Painter, theme *Theme)
Draw paints the icon (image or vector) then the optional Badge overlay. If Bounds().W is zero the widget resizes itself to StatusIconSize square (H preserved when non-zero) before painting.
func (*StatusIcon) OnEvent ¶ added in v0.80.0
func (s *StatusIcon) OnEvent(ev Event)
OnEvent fires OnRightClick on a secondary EventClick (Code == StatusIconSecondary) and OnClick on any other EventClick; other event kinds are ignored. Both callbacks are nil-safe.
type Statusbar ¶
type Statusbar struct {
Base
Segments []string
// SegmentMinW is the minimum width any non-last segment takes. The
// last segment ALWAYS fills the rest of the bar.
SegmentMinW int // default StatusbarSegmentMinW
}
Statusbar is a thin horizontal strip at the bottom of a window that shows N text segments (e.g. "Line 12, Col 4" + "UTF-8" + "Plain text" in an editor). Segments paint left-to-right with a 1-pixel divider between them; the LAST segment expands to fill any remaining width so an empty Statusbar still looks deliberate.
Statusbar is the natural pairing for MenuBar + Toolbar above and a document area in the middle — together they assemble the "stock GTK" window frame.
func NewStatusbar ¶
NewStatusbar builds a Statusbar with the given segments. SegmentMinW is left at zero — "use the default" — so the default minimum resolves through scaled at draw time and grows with HiDPI and touch density; a caller that wants a fixed minimum sets SegmentMinW explicitly (honoured verbatim). At compact/1x the resolved default is StatusbarSegmentMinW, byte-identical to before.
func (*Statusbar) A11y ¶ added in v0.40.0
A11y reports the Statusbar as a status region carrying its segments joined into one string.
func (*Statusbar) SetSegment ¶
SetSegment replaces the i-th segment in place. Indexes out of range are appended (filling intermediate slots with "") so callers can grow the bar lazily.
type Steps ¶ added in v0.7.0
type Steps struct {
Base
Labels []string
// Orientation lays the badges out left-to-right (Horizontal, the zero
// value — a wizard strip) or top-to-bottom (Vertical — a side
// checklist). Vertical draws its connectors as vertical lines and
// renders each caption to the right of its badge instead of below it.
Orientation Orientation
// contains filtered or unexported fields
}
Steps is a horizontal step indicator — [1]—[2]—[3]—[4] — for multi-step flows (a wizard, an on-boarding tour, a checkout page). Each entry is drawn as a small square badge carrying its 1-based index number, with a 1-px connector line between successive badges. A Labels entry that is not "" renders below its badge as caption text in Theme.OnBackground.
Current is the 0-indexed cursor into Labels; badges up to AND including Current fill with Theme.Accent (the "done / active" colour), later badges fill with Theme.SurfaceAlt (the "pending" colour). A Current outside [0, len(Labels)) means either "no step active yet" (Current < 0 -> every badge is pending) or "all done" (Current >= len -> every badge is filled).
A click on a badge jumps to that step: OnEvent hit-tests the same badge layout Draw paints and Sets the Current Observable to the clicked index, notifying its subscribers. A host binds Current to react to the jump; there is no click callback.
The reactive cursor is MVVM-only: it lives in an unexported Observable exposed via Steps.Current. Labels and Orientation are set-once layout config and stay plain fields.
func NewSteps ¶ added in v0.7.0
NewSteps constructs a Steps indicator with the given labels + the initial current-step cursor.
func (*Steps) A11y ¶ added in v0.40.0
A11y reports the Steps strip as a group carrying its current step's label.
func (*Steps) Current ¶ added in v0.7.0
func (s *Steps) Current() *mvvm.Observable[int]
Current is the 0-indexed cursor into Labels as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Current field. A click on a badge Sets it to that index; subscribers are notified. A value outside [0, len(Labels)) means either "no step active yet" (Current < 0 -> every badge is pending) or "all done" (Current >= len -> every badge is filled). The Observable lazy-inits to 0 on first access so a zero-value &Steps{} is usable.
func (*Steps) Draw ¶ added in v0.7.0
Draw paints each badge, its connector to the previous badge (if any) and the optional caption below it. The badge fill switches from Accent (index <= Current) to SurfaceAlt (index > Current); the number ink inverts accordingly so it stays legible.
func (*Steps) OnEvent ¶ added in v0.104.0
OnEvent jumps to a clicked step: it hit-tests each badge against the same layout Draw paints (badge i advances by StepBoxW/StepBoxH plus one StepConnectorW per gap along the layout axis; the cross axis is the pinned badge column, vertically centred in a tall bar for the horizontal case), and on a hit Sets the Current Observable to that index (subscribers are notified; an unchanged index is a no-op per mvvm.Observable). Only the badge box is sensitive -- a click on a caption or a connector is ignored. Coordinates are widget-local, so the first badge's top-left is (0, cross-offset).
type Surface ¶ added in v0.147.0
type Surface struct {
Base
// Frame is asked for the buffer to show, once per Draw. It returns the
// RGBA pixels and their dimensions; a nil Frame, or one returning a buffer
// too short for w*h*4, paints nothing rather than guessing.
//
// It is a function rather than a field so the application can hand over
// whatever it has this frame without copying it anywhere first.
Frame func() (pix []byte, w, h int)
// Elements, when set, is asked what the surface is currently showing, in
// reading order. Rects are in the BUFFER's own pixel coordinates — the same
// space Frame's pixels and OnInput's events use — and Surface offsets them
// onto the surface, because that is the one space the application and this
// widget already agree on.
Elements func() []SurfaceElement
// OnInput receives events with coordinates translated into the buffer's
// space. A nil OnInput drops them.
OnInput func(Event)
}
Surface shows a framebuffer the application renders itself.
Most applications describe what they want and let the toolkit paint it. Some cannot: a game, a video player, a browser engine, a news reader with its own scene and hit-testing. They produce finished pixels, and what they need from a widget set is somewhere to put them, input in the coordinates those pixels use, and a way to still be readable by a screen reader.
Historically such an application had to reach past the painter for the raw buffer, which is exactly what stopped it being hosted by a back-end that hands out a Painter and nothing else — a recording painter, a damage-tracked one, a remote one. Surface is the seam that removes the excuse: it blits through the painter's image primitive and degrades to a per-pixel loop on a back-end without one.
The buffer is drawn 1:1 at the widget's bounds, and no scaling is invented: the application is told the size it has (through Resize on its own side) and renders at it. Anything else would resample pixels that were composed for a specific size.
Accessibility ¶
A surface is otherwise opaque — one rectangle of pixels, which is what a screen reader would be told, and useless. An application that can say what it is showing sets Elements, and each entry becomes a child the accessibility walk reads in order. That is what keeps WalkA11y and the platform bridges working for an application whose widgets the toolkit never sees.
func NewSurface ¶ added in v0.147.0
NewSurface returns a Surface fed by frame.
func (*Surface) A11y ¶ added in v0.147.0
A11y reports the surface itself as presentation: it is a container of what the application describes, not a thing in its own right, and announcing it would put an unnamed group between the reader and the content.
func (*Surface) Children ¶ added in v0.147.0
Children returns one proxy widget per element the application reports, with the element's rectangle moved onto the surface.
The proxies are built fresh on every call and never drawn. That is deliberate: what the application is showing changes as it renders, and a cached child would describe the frame before last. The accessibility walk is not a per-frame path, so building a handful of small structs when a screen reader asks costs nothing worth keeping stale answers for.
type SurfaceElement ¶ added in v0.147.0
type SurfaceElement struct {
Role Role
Name string
Value string
// X, Y, W and H are in the buffer's pixel coordinates.
X, Y, W, H int
}
SurfaceElement is one thing a Surface is showing: what it is, what it says, and where it sits in the buffer.
type SwipeAction ¶ added in v0.187.0
type SwipeAction struct {
// Label is the action's caption, drawn centred when Icon is nil and used as
// the accessible name.
Label string
// Icon, when set, draws the action's glyph into lane rect r in colour ink —
// the same callback shape as [Button.Icon]. Optional.
Icon func(p painter.Painter, r Rect, ink RGBA)
// Color is the lane's background fill. A zero (fully-transparent) value falls
// back to a danger red for a Destructive action, else the theme Accent.
Color RGBA
// Ink is the label/icon colour. A zero (fully-transparent) value falls back
// to white, which reads on both the accent and danger fills.
Ink RGBA
// Destructive marks the set's primary action — the one a full swipe fires and
// the one whose colour fills the lane during the destructive-primed drag. When
// no action in a set is flagged, the set's edge action (nearest the screen
// edge) is treated as primary.
Destructive bool
// OnInvoke is the command body, run once when the action is tapped or invoked.
// Nil is a no-op.
OnInvoke func()
}
SwipeAction is one revealed action: a coloured lane carrying a label or icon that fires OnInvoke when tapped (or invoked through the accessibility tree).
type SwipeActions ¶ added in v0.187.0
type SwipeActions struct {
Base
// Content is the row body slid to reveal the actions. May be nil (an empty
// row still reveals its actions).
Content Widget
// Leading is the action set revealed by a rightward swipe (left edge);
// Trailing is the set revealed by a leftward swipe (right edge). Either may
// be empty, in which case a swipe that way rubber-bands against a closed row.
Leading []SwipeAction
Trailing []SwipeAction
// ActionWidth is the base (logical-pixel) width of one action lane, routed
// through [Scaled] and clamped up to the density's [MinHitTarget]. NewSwipeActions
// sets it to a finger-friendly default.
ActionWidth int
// DestructiveFull enables the full-swipe shortcut: a drag past the destructive
// threshold fires the swiped set's primary action directly and closes. When
// false, a far drag simply settles open like any other.
DestructiveFull bool
// DestructiveNum / DestructiveDen express the destructive threshold as a
// fraction of the row width (DestructiveNum/DestructiveDen). NewSwipeActions
// sets 3/4: the finger must cross three-quarters of the row to trigger.
DestructiveNum, DestructiveDen int
// Projection is how many seconds of the release velocity are added to the
// reveal offset before the snap decision, so a fast flick opens (or triggers)
// from further back. 0 makes the snap depend on the resting offset alone.
Projection float64
// contains filtered or unexported fields
}
SwipeActions is the mobile "swipe a list row to reveal actions" wrapper. It holds one row of SwipeActions.Content plus a leading and a trailing set of [SwipeAction]s, and turns a horizontal drag into the familiar reveal: the content slides sideways to uncover the actions pinned to the row's edge, snaps open or shut at a threshold, and — dragged far enough — fires the set's primary (destructive) action outright.
What it is made of ¶
- Content is the row body (a Label, an ActionRow, any Widget). It is drawn shifted by the live reveal offset and clipped to the row, so it appears to glide over the actions beneath it.
- Leading / Trailing are the two action sets. Swiping RIGHT (content moves right, offset positive) reveals the leading set at the row's left edge; swiping LEFT (content moves left, offset negative) reveals the trailing set at the right edge — the iOS/Material convention.
- Each action is surfaced as a real child Button (see SwipeActions.Children), so it lands in the accessibility tree with a button role and its label: a screen reader can read and INVOKE it (via the button's own click path, which routes to SwipeActions.InvokeTrailing / SwipeActions.InvokeLeading) without ever performing the drag gesture. That is the a11y-invoke path.
The reveal, the snap and the destructive full-swipe ¶
A drag tracks the finger one-for-one through the pure-logic Momentum engine (so a pull past the far edge rubber-bands and a release settles with the same deterministic spring the rest of the toolkit uses). On release the widget SNAPS, choosing a target from the reveal magnitude (optionally projected by the release velocity so a fast flick opens from further out):
- past the destructive threshold (a large fraction of the row width) AND SwipeActions.DestructiveFull is on: the set's primary action fires once, immediately, and the row settles shut — the "full swipe to delete" shortcut;
- else past the open threshold (half the set's revealed width): it settles OPEN, resting exactly on the set's full width;
- else: it settles shut, resting exactly on 0.
Every rest lands on an exact offset (0, +leadingWidth or -trailingWidth) — the Momentum spring snaps onto its bound rather than drifting — so the open/closed state machine is crisp and testable to the pixel.
Determinism and the frame loop ¶
Like GestureRecognizer, Momentum and Animator, SwipeActions owns no clock and no goroutine. A drag is driven by the input events a host already routes (touch or mouse); the settle is advanced by an explicit SwipeActions.Tick whose dt (seconds) the host supplies each frame, exactly like Momentum.Tick. Given the same events and dt sequence it produces the same offsets everywhere.
func NewSwipeActions ¶ added in v0.187.0
func NewSwipeActions(content Widget) *SwipeActions
NewSwipeActions wraps content in a shut SwipeActions with the finger-friendly defaults (72-logical-pixel lanes, full-swipe-to-primary on at 3/4 of the row, a 0.05 s flick projection). Add actions by appending to Leading / Trailing.
func (*SwipeActions) A11y ¶ added in v0.187.0
func (sa *SwipeActions) A11y() A11yInfo
A11y reports the row as a group whose value names the reveal state, so a screen reader can tell an open row from a shut one; its child action buttons carry the individual invocable semantics.
func (*SwipeActions) Children ¶ added in v0.187.0
func (sa *SwipeActions) Children() []Widget
Children yields the content then every action button (leading, then trailing), so a generic accessibility walk reaches the row body AND each action — the screen reader can announce and invoke an action without the swipe gesture. Hidden actions are still exposed, like Carousel's off-screen slides.
func (*SwipeActions) Close ¶ added in v0.187.0
func (sa *SwipeActions) Close()
Close settles the row shut.
func (*SwipeActions) Draw ¶ added in v0.187.0
func (sa *SwipeActions) Draw(p painter.Painter, theme *Theme)
Draw paints the revealed action lanes and then the content shifted by the live offset over them, all clipped to the row. When shut (offset 0) only the content is visible and the render is that of the bare content. While a destructive full-swipe is primed, the primary action's colour fills the whole revealed strip instead of the individual lanes, the way a "release to delete" row flashes.
func (*SwipeActions) InvokeLeading ¶ added in v0.187.0
func (sa *SwipeActions) InvokeLeading(i int)
InvokeLeading fires leading action i's command exactly once and closes the row. Out-of-range i is ignored.
func (*SwipeActions) InvokeTrailing ¶ added in v0.187.0
func (sa *SwipeActions) InvokeTrailing(i int)
InvokeTrailing fires trailing action i's command exactly once and closes the row. Out-of-range i is ignored. This is the path a revealed-lane tap and an accessibility-tree button click both take.
func (*SwipeActions) IsOpen ¶ added in v0.187.0
func (sa *SwipeActions) IsOpen() bool
IsOpen reports whether either action set is (heading to be) revealed.
func (*SwipeActions) Offset ¶ added in v0.187.0
func (sa *SwipeActions) Offset() int
Offset returns the live content offset in device pixels: >0 leading revealed, <0 trailing revealed, 0 shut. It is the value Draw paints the content shifted by, and the exact snap target once a settle finishes.
func (*SwipeActions) OnEvent ¶ added in v0.187.0
func (sa *SwipeActions) OnEvent(ev Event)
OnEvent drives the reveal from the pointer stream a host already routes. A press (EventClick / EventTouchStart) begins a drag; moves (EventMouseDrag / EventTouchMove) slide the content and lock the gesture to the horizontal axis (a dominantly vertical drag is disowned so a list can scroll through it); a release (EventMouseUp / EventTouchEnd) either snaps (a real drag) or resolves a tap. Escape closes an open row. Events the shut row does not use are forwarded to the content.
func (*SwipeActions) Open ¶ added in v0.187.0
func (sa *SwipeActions) Open() *mvvm.Observable[SwipeOpenState]
Open exposes the observable open state so an app view-model can bind to it (subscribe for a repaint, or drive it) through the go-widgets MVVM layer. It is lazily allocated, so a zero-value SwipeActions literal still works.
func (*SwipeActions) OpenLeading ¶ added in v0.187.0
func (sa *SwipeActions) OpenLeading()
OpenLeading reveals the leading set (settling to its full width), or does nothing when the set is empty.
func (*SwipeActions) OpenTrailing ¶ added in v0.187.0
func (sa *SwipeActions) OpenTrailing()
OpenTrailing reveals the trailing set (settling to its full width), or does nothing when the set is empty.
func (*SwipeActions) Settling ¶ added in v0.187.0
func (sa *SwipeActions) Settling() bool
Settling reports whether a settle is still in flight — a host schedules another frame (and calls Tick) while this is true, mirroring Momentum.Settling.
func (*SwipeActions) State ¶ added in v0.187.0
func (sa *SwipeActions) State() SwipeOpenState
State reports the current open/closed state.
func (*SwipeActions) Tick ¶ added in v0.187.0
func (sa *SwipeActions) Tick(dt float64) bool
Tick advances an in-flight settle by dt seconds, updating the live offset, and returns whether it is still settling. It is a no-op (returns false) when no settle is running. On the final tick the offset snaps to the exact target.
type SwipeDir ¶ added in v0.39.0
type SwipeDir int
SwipeDir is the dominant-axis direction of a recognized swipe gesture.
const ( // SwipeLeft is a horizontal swipe whose net motion is negative in X. SwipeLeft SwipeDir = iota // SwipeRight is a horizontal swipe whose net motion is positive in X. SwipeRight // SwipeUp is a vertical swipe whose net motion is negative in Y. SwipeUp // SwipeDown is a vertical swipe whose net motion is positive in Y. SwipeDown )
type SwipeOpenState ¶ added in v0.187.0
type SwipeOpenState int
SwipeOpenState is the SwipeActions open/closed state machine position.
const ( // SwipeClosed is the row at rest, content covering both action sets. SwipeClosed SwipeOpenState = iota // SwipeLeadingOpen is the leading set fully revealed at the left edge. SwipeLeadingOpen // SwipeTrailingOpen is the trailing set fully revealed at the right edge. SwipeTrailingOpen )
type Switch ¶ added in v0.7.0
type Switch struct {
Base
// contains filtered or unexported fields
}
Switch is a compact iOS-style toggle: a wide horizontal track with a small square knob that sits on the left when Off and on the right when On. Distinct from ToggleButton in shape + intent: ToggleButton is a full-face button whose entire body flips colour with state, so it reads as "an action that stays pressed"; Switch is decorative chrome — a settings-row indicator whose knob position is the entire affordance ("is this feature on?").
Track fill flips between SurfaceAlt (Off) and Accent (On) so the on-state stands out at a glance; the knob is drawn in Surface with a Border stroke so it stays visible against either track colour.
A click flips the On Observable. Non-click events are ignored.
func (*Switch) Draw ¶ added in v0.7.0
Draw paints the track + knob. Track colour is picked by On; the knob slides between left + right edges by rewriting knobX in the On branch. Zero-height or extremely narrow Bounds degrade to a no-op via fillRect's own dimension guard.
func (*Switch) Focused ¶ added in v0.101.0
func (f *Switch) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*Switch) HitRect ¶ added in v0.184.0
HitRect is the worked example of TouchTarget: the switch's interactive rectangle, clamped up to the density minimum on each axis and centred over the drawn Widget.Bounds. Under the default DensityCompact the minimum is 0, so the returned rect equals Bounds byte-for-byte; under DensityTouch a small switch grows a finger-sized hit area around its unchanged pixels. Only the hit region grows — Draw is untouched — so the switch looks the same and is easier to press.
func (*Switch) HitTest ¶ added in v0.187.0
HitTest reports whether a surface point falls on the switch's (touch-clamped) hit rect — the worked Switch.HitRect area. Under DensityCompact the clamp is a pass-through so this equals the default Bounds().Contains; under DensityTouch the small switch's reachable area grows to the finger floor around its unchanged pixels.
func (*Switch) On ¶ added in v0.7.0
func (s *Switch) On() *mvvm.Observable[bool]
On is the current toggle state as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable On field. A click or a Space/Enter key press flips it, notifying subscribers.
func (*Switch) OnEvent ¶ added in v0.7.0
OnEvent flips the On Observable on click. All other event kinds pass through without effect (matches ToggleButton / CheckButton).
func (*Switch) SetFocused ¶ added in v0.101.0
func (f *Switch) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
type TabBar ¶ added in v0.187.0
type TabBar struct {
Base
// Items are the navigation destinations, laid out left→right in slice
// order. Each carries an Icon glyph, an optional Label, and an optional
// Badge counter string.
Items []TabItem
// swipe advances to the next item, a right swipe returns to the previous
// one (both clamped at the ends). It is false by default, so a plain
// TabBar ignores swipes and only a caller that wants the gesture pays for
// it — the "optional" in the widget's contract.
SwipeNavigation bool
// Gestures is the recognizer that turns the touch stream into taps and
// swipes. NewTabBar wires it; a caller may retune its thresholds
// (TapSlop, SwipeMinDist, ...) or leave the defaults.
Gestures *GestureRecognizer
// contains filtered or unexported fields
}
TabBar is a mobile bottom-navigation bar: a strip of equal-width items, each an icon over an optional label, pinned to the bottom edge of a phone or tablet surface with exactly one item Selected. It is the touch-first counterpart of the desktop ViewSwitcher / SegmentedBar — same "pick one of N views" job, laid out for a fingertip instead of a mouse — and is a separate widget precisely so the desktop pickers keep their compact, horizontal-strip look untouched.
Each item spans the FULL height of the bar and 1/N of its width, so the whole bar is one uninterrupted band of touch targets with no dead gaps between items. The bar's own height auto-sizes (when Bounds().H is zero) to TabBarHeight routed through TouchTarget, guaranteeing a hit height at or above the 44-logical-pixel finger floor under DensityTouch while staying byte-identical to a fixed 56-px bar under the desktop default DensityCompact. Per-item hit rectangles are available via TabBar.ItemHitRect, the worked TouchTarget example mirroring Switch.HitRect: a narrow item grows a finger-sized hit area around its unchanged pixels.
The Selected item is highlighted two ways: its icon + label paint in Theme.Accent (unselected items paint in Theme.OnSurface), and a TabBarIndicatorH-thick accent indicator bar runs along the TOP edge of the selected item's column. A tap anywhere in an item's column selects it and fires OnSelect exactly once; optional horizontal swipe navigation (gated by SwipeNavigation) steps the selection one item per swipe.
A TabBar with no Items paints only its background band + the accent-free top border; every input is a no-op. This lets a caller assemble the widget before it knows which destinations the app will surface.
func NewTabBar ¶ added in v0.187.0
NewTabBar builds a TabBar over items with the initial selection at selected. selected is clamped into [0, len(items)-1], or forced to 0 when items is empty, so the widget never holds an out-of-range selection. The gesture recognizer is wired with the package defaults; horizontal-swipe navigation stays off until the caller sets SwipeNavigation.
func (*TabBar) A11y ¶ added in v0.187.0
A11y reports the TabBar as a tablist named by its selected item's label (falling back to that item's icon when it has no label). Each item is published as an individual RoleTab node via Children, so a screen reader announces the bar as a tablist containing N tabs with one selected.
func (*TabBar) Children ¶ added in v0.187.0
Children exposes each item as a synthetic, non-drawing tab node so the generic accessibility walk (WalkA11y) descends the bar into a tablist → tab structure, each tab carrying its own name, selected state and surface rectangle. The nodes are rebuilt on demand from the current geometry, so a resize or a selection change is reflected without any retained child state. These nodes are for the a11y/geometry walk only — the TabBar paints every item itself in Draw.
func (*TabBar) Draw ¶ added in v0.187.0
Draw paints the background band, a 1-pixel accent-free TOP border (so the bar reads as a discrete band beneath the scrolled content above it), each item's icon/label (+ optional badge), and the accent top indicator over the selected item. A zero height is auto-filled first.
func (*TabBar) Focused ¶ added in v0.187.0
func (f *TabBar) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*TabBar) ItemHitRect ¶ added in v0.187.0
ItemHitRect returns item i's interactive rectangle: its visual TabBar.ItemRect with each axis clamped UP to MinHitTarget and centred over the drawn column, the same construction Switch.HitRect uses. Under the default DensityCompact the minimum is 0, so the hit rect equals the visual rect byte-for-byte; under DensityTouch a narrow item grows a finger-sized hit area around its unchanged pixels. Only the hit region grows — Draw is untouched. An out-of-range i returns the zero Rect.
func (*TabBar) ItemRect ¶ added in v0.187.0
ItemRect returns the VISUAL (drawn) rectangle of item i in surface coordinates: its column of the bar, full bar height. An out-of-range i (or a zero-width bar) returns the zero Rect, so a caller can range over len(Items) and skip empties without a bounds check.
func (*TabBar) OnEvent ¶ added in v0.187.0
OnEvent selects an item on a tap/click, steps the selection on an arrow key, and feeds the touch stream to the gesture recognizer (whose OnTap selects and OnSwipe navigates when SwipeNavigation is on). A Disabled bar ignores every kind.
func (*TabBar) Selected ¶ added in v0.187.0
func (t *TabBar) Selected() *mvvm.Observable[int]
Selected is the highlighted item index as a shared mvvm.Observable: a host binds it (or subscribes for the old OnSelect notification) instead of reading a field, and a tap / swipe / arrow key Sets it. Lazily created so a bare &TabBar{} works.
func (*TabBar) SetFocused ¶ added in v0.187.0
func (f *TabBar) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
type TabItem ¶ added in v0.187.0
TabItem is one destination in a TabBar: a short Icon glyph (rendered in the toolkit's bitmap font, e.g. a symbol or a 1–2 letter stand-in), an optional Label beneath it, and an optional Badge — a short counter/indicator string ("3", "9+") drawn as a pill hanging off the icon's top-right corner via the same body as the standalone Badge widget. An empty Badge draws nothing.
type TabSide ¶ added in v0.24.1
type TabSide int
TabSide selects which edge of the Notebook hosts the tab strip.
const ( // TabTop places the strip along the top edge (the default). TabTop TabSide = iota // TabBottom places the strip along the bottom edge. TabBottom // TabLeft places the strip down the left edge (tabs stacked vertically). TabLeft // TabRight places the strip down the right edge (tabs stacked vertically). TabRight )
type Table ¶ added in v0.7.0
type Table struct {
Base
// Columns are the header cells (title + optional pixel width).
// A zero Width means "auto" -- the column claims an equal share of
// whatever pixel budget is left after the fixed-Width columns.
Columns []TableColumn
// Rows is the body content. Each inner slice SHOULD have
// len == len(Columns); rows shorter than that render only the
// cells they carry (missing trailing cells are drawn as blank
// space, the row background still paints edge-to-edge).
Rows [][]string
// RowIcon, when non-nil, supplies an optional leading icon for each
// body row -- the missing piece for file-list-style views (a
// per-row file-type glyph before the name) that previously forced a
// host to hand-compose custom rows instead of using Table directly.
// It is called with a 0-indexed body row and returns the icon's
// painter (any of the stock DrawIconXxx functions in icons.go, or a
// caller's own of the same TableIconFunc signature) plus an ok flag;
// returning ok == false (or a nil painter) means "this row has no
// icon" while still reserving the gutter, so text stays aligned down
// the first column whether or not a given row carries a glyph.
//
// When RowIcon is set, Draw reserves a fixed leading gutter
// (TableCellPadX + scaled(TableIconSize)) inside the FIRST column, paints the
// row's icon there in the row's own ink (accent-inverted on the
// selected row, OnSurface otherwise), and shifts that column's text
// right by the gutter. Column boundaries, separators, sort, scroll
// and row/column hit-testing are all unchanged -- the gutter is
// carved out of column 0's interior, so a click on the icon still
// resolves to column 0 and to its row exactly as a click on the text
// would.
//
// The zero value (nil) is the original, pre-feature behaviour: no
// gutter is reserved and Draw renders byte-for-byte identically to
// before this field existed.
RowIcon func(row int) (draw TableIconFunc, ok bool)
// MultiSelect switches body-row clicks (handled by OnEvent) from
// inert to selection-driving: a plain click selects only that row
// (clearing any other selection, moving the Selected anchor to
// it); a Ctrl-click toggles that row's membership without
// disturbing the anchor; a Shift-click selects the inclusive
// range between the anchor (Selected) and the clicked row,
// likewise leaving the anchor in place so repeated Shift-clicks
// keep ranging from the same origin. Header-row clicks (sort) and
// separator drags (resize) are unaffected either way.
//
// The zero value (false) is the original passive-viewer
// behaviour: OnEvent never touches Selected or any selection
// state for a body-row click, and Draw highlights only Selected --
// byte-for-byte the same as before this field existed.
MultiSelect bool
// FrozenColumns is the number of leading columns pinned in place while
// the rest scroll horizontally (see ScrollX). Clamped into
// [0, len(Columns)]. Meaningful only when the columns are all
// fixed-width and overflow the viewport (hScrollable); with any
// auto-width column the table fits to width and never scrolls
// horizontally, so this is inert. The zero value (0) pins nothing --
// byte-identical to before this field existed.
FrozenColumns int
// OnSort fires when a Sortable header cell is clicked. col is the
// clicked column; ascending is the NEW direction after the click
// (clicking the already-active column toggles it, clicking a new
// column resets to ascending). The Table updates SortColumn/SortAsc
// itself before firing so the very next Draw shows the indicator;
// the host is responsible for re-sorting Rows and handing them back.
OnSort func(col int, ascending bool)
// OnColumnResize fires whenever a separator drag (or a direct
// SetColumnWidth call) changes a column's width. newWidth is the
// clamped pixel width now in effect.
OnColumnResize func(col, newWidth int)
// OnCellEdit fires when an inline cell edit is committed (Enter): the
// Table has already written the new value into Rows[row][col]. Nil is
// safe. Only cells in a column with Editable set can be edited.
OnCellEdit func(row, col int, value string)
// EditActivation selects how a click begins an inline edit on an Editable
// cell (see TableEditActivation): the zero value EditOnSingleClick keeps
// the original "one click edits" behaviour, EditOnDoubleClick makes a
// single click select and a double-click (or Enter on the cursor row) edit
// -- the desktop details-view idiom -- and EditManual disables click/key
// activation so only BeginEdit opens an editor.
EditActivation TableEditActivation
// OnCellEditRejected fires when a committed edit fails its column's
// Validate rule: the Table has NOT written the value and leaves the editor
// open. row/col name the cell, value is the rejected text, err is the
// rule's error (its Error() is the message to surface). Nil is safe.
OnCellEditRejected func(row, col int, value string, err error)
// SelfSort opts the Table into sorting its own Rows on a Sortable header
// click: it calls SortByColumn (reordering Rows in place through the
// column's Comparator) before firing OnSort, so a host need not re-sort
// and hand the data back. The zero value (false) keeps the original
// content-only behaviour -- a header click only updates the indicator and
// fires OnSort, never touching Rows.
SelfSort bool
// ShowSummary, when true, appends a grand-total footer line (a distinct
// SurfaceAlt band, drawn as the LAST visual line of the body) that shows
// each column's aggregate over every row -- blank for a column whose
// Aggregate is AggregateNone. When GroupBy is also active, a per-group
// summary line is additionally emitted after each (expanded) group's rows,
// aggregating just that group's members. The footer folds into the same
// visual-line model group headers use, so lineCount/rowAt/scrollbar stay
// consistent. The zero value (false) is the original, pre-feature
// behaviour: no summary line is emitted and, ungrouped, the body renders
// byte-for-byte as it did before this field existed.
ShowSummary bool
// GroupBy, when in [0,len(Columns)), turns on row grouping: consecutive
// rows sharing that column's value are gathered under a clickable group
// header (value + member count + a disclosure triangle) that collapses
// its members. -1 (the default, seeded by NewTable) is ungrouped -- the
// body then renders byte-identically to a Table that never had this
// field. Grouping assumes Rows are already ordered by the group column
// (consecutive runs); it does not sort. Drag-to-reorder is suppressed
// while grouped (a cross-group move has no well-defined meaning).
GroupBy int
// Reorderable opts the Table into drag-to-reorder BODY rows: it makes
// the Table both a DragSource (a press on a body row becomes a
// draggable "tablerow:<index>" payload -- see DragData) and a
// DropTarget for that same payload (see AcceptsDrop). The zero value
// (false) is the original, pre-feature behaviour: DragData always
// returns "", AcceptsDrop always returns false, and every drag event
// (EventDragMove / EventDragLeave / EventDrop) is a no-op -- Draw and
// OnEvent render/behave byte-identically to before this field
// existed. Header-cell sort clicks and separator-drag resizes never
// start a row drag regardless of this flag -- only a press that lands
// on a BODY row does.
Reorderable bool
// OnReorder fires after a successful drop reorders Rows in place:
// from is the row's index BEFORE the move, to is where it now sits.
// Nil-guarded -- a host that doesn't care to be notified simply
// leaves it unset.
OnReorder func(from, to int)
// RowDetail, when non-nil, opts each body row into an expander: a leading
// disclosure ▶/▼ appears at the left of column 0, and clicking it toggles
// that row's expansion. An expanded row shows a detail line (one
// TableRowHeight-tall band, its text supplied by RowDetail(row), indented)
// inserted right after the row via the same visual-line model group
// headers use, so all geometry (rowAt/visualIndex/scrollbar/cellRect)
// keeps working. Clicking the disclosure toggles expansion; a click on the
// cell itself still selects/edits as before. The zero value (nil) is the
// original, pre-feature behaviour: no gutter is reserved, no row can
// expand, and Draw renders byte-for-byte as before this field existed.
RowDetail func(row int) string
// contains filtered or unexported fields
}
Table renders a structured data grid: a fixed header row of column titles above a body of text rows. The widget is the missing piece vs GTK's ColumnView + DaisyUI's Table -- the toolkit's ListBox + TreeView give a single column of items, whereas Table lays cells out horizontally under labelled columns.
Visual (per row):
+----------------+--------+-------------+ | Header A | Hdr B | Header C | <- scaled(TableHeaderHeight), SurfaceAlt +----------------+--------+-------------+ | row 0 cell 0 | 0.1 | 0.2 | <- TableRowHeight, Surface | row 1 cell 0 | 1.1 | 1.2 | <- TableRowHeight, Background | ... +----------------+--------+-------------+
Selected row (if 0 <= Selected < len(Rows)) paints in Theme.Accent with the accent-inverted ink -- theme.Extra["OnAccent"] when the GTK loader supplied one, otherwise theme.Background (the same fallback the Button + ListBox + TreeView selected states already use, so the visual reads consistent across widgets). When MultiSelect is true every row in the multi-row selection set paints the same way, not just Selected (which keeps acting as the anchor for Shift-range clicks) -- see MultiSelect + SelectedRows.
The widget is content-only: it never reorders Rows itself. Header clicks + separator drags are surfaced through OnSort/OnColumnResize so the host (which owns the data model) can re-sort Rows or persist a new column width, then hand the Table back its updated state.
func NewTable ¶ added in v0.7.0
func NewTable(cols []TableColumn, rows [][]string) *Table
NewTable builds a Table with the given columns + rows. Selected starts at -1 (no row selected) so a freshly constructed Table renders with plain zebra striping.
func (*Table) A11y ¶ added in v0.40.0
A11y reports the Table as a grid. Value names the selected row in single-select mode, or a "N rows selected" count while MultiSelect is on.
func (*Table) AcceptsDrop ¶ added in v0.37.0
AcceptsDrop reports whether payload is one of this Table's own "tablerow:" drags -- true only while Reorderable is true AND the payload parses as a well-formed tablerow payload. A foreign payload (a different scheme, or garbage) is always rejected, including while Reorderable is false.
func (*Table) ArrangeGroups ¶ added in v0.150.0
func (t *Table) ArrangeGroups()
ArrangeGroups reorders Rows so the GroupBy column's groups become contiguous and are themselves ordered by that column's Comparator (applied to the group keys, honouring GroupKey), the prerequisite the grouped line model needs (it gathers CONSECUTIVE runs of a shared key and does not sort). The sort is stable, so rows within a group keep their prior order, and Selected / selection / expansion follow their rows. It is a no-op unless grouping is active (GroupBy names a real column). Call it after loading or mutating Rows out of group order; a host that already delivers grouped-ordered rows need not.
func (*Table) BeginEdit ¶ added in v0.150.0
BeginEdit opens an inline editor over cell (row,col) programmatically -- the command-style entry point a view model (or a host in EditManual mode) uses to start an edit without a click. It applies the same guards as a click-driven edit: an out-of-range cell or a column that is not Editable is a no-op.
func (*Table) CancelEdit ¶ added in v0.150.0
func (t *Table) CancelEdit()
CancelEdit is the public trigger for discarding the open edit (Escape's path) -- a command a view model binds to. It is a no-op when no edit is in progress.
func (*Table) ClearRowSelection ¶ added in v0.37.0
func (t *Table) ClearRowSelection()
ClearRowSelection empties the multi-row selection set.
func (*Table) ColumnSeparatorAt ¶ added in v0.36.0
ColumnSeparatorAt returns the 0-based index of the separator under localX (a Table-local x coordinate) -- the separator between column i and column i+1 -- within tableSeparatorHitTolerance pixels, or -1 if localX is not near any separator. A single-column (or empty) Table has no separators and always returns -1.
func (*Table) CommitEdit ¶ added in v0.150.0
func (t *Table) CommitEdit()
CommitEdit is the public trigger for committing the open edit (Enter's path) -- a command a view model binds to. It is a no-op when no edit is in progress, and honours the column's Validate rule exactly like an Enter commit.
func (*Table) DragData ¶ added in v0.37.0
DragData reports the drag payload for the body row currently pressed (see dragRow) -- "tablerow:<index>" -- while Reorderable is true and a body row was actually the most recent press; otherwise "". A stale dragRow left over after Rows shrinks collapses to "" the same defensive way Draw collapses an out-of-range Selected.
func (*Table) Draw ¶ added in v0.7.0
Draw paints the header + body + column separators through p using theme's palette. Widths for auto columns are computed here, so resizing the widget's Bounds() between frames re-flows the columns automatically.
func (*Table) EditError ¶ added in v0.150.0
EditError returns the validation error of the open edit's last rejected commit, or nil when the pending value is valid (or no edit is in progress). It is what surfaces the reason an edit would not commit.
func (*Table) Editing ¶ added in v0.150.0
Editing reports the cell whose inline editor is currently open. editing is false (and row/col are -1) when no edit is in progress -- the observable getter a view model reads to reflect edit state.
func (*Table) Focused ¶ added in v0.101.0
func (f *Table) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*Table) IsRowSelected ¶ added in v0.37.0
IsRowSelected reports whether row i is a member of the multi-row selection set. A negative i is always false -- mirrors how every other row/column index in this file collapses an invalid value instead of indexing into (or panicking on) the underlying map/slice. It answers from the raw set regardless of MultiSelect; only Draw and OnEvent gate their use of it on MultiSelect being true.
func (*Table) OnEvent ¶ added in v0.36.0
OnEvent implements header-click sorting, separator drag-resize, and (while MultiSelect is true) body-row multi-selection. The toolkit's event model is click-only (see Paned): a resize drag begins on an EventClick that lands on a separator (ColumnSeparatorAt), is driven tick-by-tick by EventMouseDrag while the button stays down, and ends on EventMouseUp -- the same grab/move/release state machine RangeSlider uses for its thumbs. A click that lands on a header cell instead of a separator sorts that column (if Sortable).
A click below the header row is a header/sort/resize no-op -- it falls through to the body-row branch instead. With MultiSelect false that branch is itself a no-op (the original, selection-free behaviour); with MultiSelect true a plain click selects only that row and moves the Selected anchor to it, Ctrl toggles the row without moving the anchor, and Shift selects the inclusive range between the anchor and the clicked row (also without moving the anchor, so repeated Shift-clicks keep ranging from the same origin). A click past the last row (rowAt returns -1) is ignored.
func (*Table) RowAt ¶ added in v0.85.0
rowAt returns the body row index whose vertical band contains localY (a Table-local y coordinate, i.e. relative to the widget's own top edge and therefore still including the header offset), or -1 if localY lands in/above the header or at/past the last row -- the same "collapse to -1 outside the valid range" idiom columnAt and ColumnSeparatorAt already use for x coordinates. The offset within the body is added to clampScrollRow() (not raw ScrollRow) so a click always resolves to whatever row Draw actually painted at that y, even with an out-of-range ScrollRow. RowAt returns the data-row index under widget-local (x, y), or -1 for the header band, a group-header/summary line, or empty space past the last row. Exposed so a host can hit-test a right-click and build a context menu for the row under the cursor (x is accepted for signature symmetry with the other widgets' hit helpers; the row is determined by y).
func (*Table) ScrollBy ¶ added in v0.37.0
ScrollBy adjusts ScrollRow by delta rows (positive scrolls down, negative scrolls up), clamped the same way as ScrollTo. A mouse wheel or arrow-key handler calls this directly.
func (*Table) ScrollRow ¶ added in v0.37.0
func (t *Table) ScrollRow() *mvvm.Observable[int]
ScrollRow is the 0-indexed body row painted at the top of the body (the header never scrolls) as a shared mvvm.Observable -- there is no settable ScrollRow field. Draw + rowAt read it through clampScrollRow, so an out-of-range value never windows past [0, maxScrollRow()]. ScrollTo / ScrollBy / scrollToSelected Set it (clamped). The zero value (0) starts the body at row 0.
func (*Table) ScrollTo ¶ added in v0.37.0
ScrollTo sets ScrollRow to row, clamped into [0, maxScrollRow()] -- the direct, host-callable entry point a scrollbar drag or a PageUp/PageDown key handler drives, mirroring how SetColumnWidth is the direct entry point a separator drag drives.
func (*Table) ScrollX ¶ added in v0.78.0
func (t *Table) ScrollX() *mvvm.Observable[int]
ScrollX is the horizontal pixel offset of the SCROLLABLE (non-frozen) columns as a shared mvvm.Observable -- there is no settable ScrollX field. Read through clampScrollX, so an out-of-range value never scrolls past the content. Inert unless hScrollable; ScrollXTo / ScrollXBy Set it (clamped). The zero value (0) shows the columns from their left edge.
func (*Table) ScrollXBy ¶ added in v0.78.0
ScrollXBy adjusts ScrollX by delta pixels (positive scrolls right), clamped the same way as ScrollXTo.
func (*Table) ScrollXTo ¶ added in v0.78.0
ScrollXTo sets ScrollX to px, clamped into [0, maxScrollX()] -- the direct entry point a horizontal scrollbar drag or a wheel handler drives.
func (*Table) SelectRowRange ¶ added in v0.37.0
SelectRowRange replaces the selection with the inclusive range between a and b -- callers may pass either endpoint first, matching how a Shift-click can land above OR below the anchor. A negative endpoint clamps to 0 (so an anchor of -1, "nothing selected yet", still yields a sane from-the-top range instead of an empty one); if both endpoints are negative the resulting selection is empty.
func (*Table) Selected ¶ added in v0.7.0
func (t *Table) Selected() *mvvm.Observable[int]
Selected is the 0-indexed highlighted row as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) -- there is no settable Selected field. -1 (or any out-of-range value) means "no selection" and the zebra stripe paints unmodified. While MultiSelect is true, Selected doubles as the anchor a Shift-click ranges from; it is still the ONLY row painted while MultiSelect is false. A keyboard cursor move or a MultiSelect anchor-moving click Sets it (subscribers notified on change). NewTable seeds it to -1; a bare Table{} lazily inits it to 0.
func (*Table) SelectedRows ¶ added in v0.37.0
SelectedRows returns every selected row index in ascending order, or nil if nothing is selected. The slice is a fresh copy -- mutating it has no effect on the Table's selection state.
func (*Table) SetColumnWidth ¶ added in v0.36.0
SetColumnWidth pins column col to a fixed pixel width w (clamped to tableMinColumnWidth), then fires OnColumnResize with the clamped value. Like a Paned's MoveHandle, this is the direct, host-callable entry point a drag handler (internal or external) drives; an out-of-range col is a no-op. Setting a width converts an "auto" column into a fixed one, exactly as dragging a Paned's handle turns its 50/50 default into an explicit Position.
func (*Table) SetFocused ¶ added in v0.101.0
func (f *Table) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
func (*Table) SetRowSelection ¶ added in v0.37.0
SetRowSelection replaces the current selection with exactly rows. Negative entries are dropped; calling it with no arguments (or with only negative ones) clears the selection, same end state as ClearRowSelection.
func (*Table) SortAsc ¶ added in v0.36.0
func (t *Table) SortAsc() *mvvm.Observable[bool]
SortAsc is the direction of SortColumn as a shared mvvm.Observable -- there is no settable SortAsc field. true draws ▲ (ascending), false draws ▼ (descending); meaningless while SortColumn is out of range. A header click Sets it. The zero value (false) is descending.
func (*Table) SortByColumn ¶ added in v0.150.0
SortByColumn reorders Rows in place by column col, ascending or descending, using the column's Comparator (or defaultCellCompare when it has none), and records the result in SortColumn/SortAsc so the header shows the indicator. The sort is stable, so rows equal under the comparator keep their prior order. Selected, the multi-row selection set and the expanded-row set all follow their rows to the new positions, and ScrollRow is re-clamped. An out-of-range col is a no-op. This is the opt-in counterpart to the content-only OnSort path -- a host or view model calls it (or sets SelfSort to have header clicks call it) to let the Table own its ordering.
func (*Table) SortColumn ¶ added in v0.36.0
func (t *Table) SortColumn() *mvvm.Observable[int]
SortColumn is the 0-indexed sorted column, or -1 (or any out-of-range value) for "no sort", as a shared mvvm.Observable -- there is no settable SortColumn field. Draw skips the ▲/▼ indicator while it is out of range; a Sortable header click Sets it (together with SortAsc). The Table never reorders Rows itself unless SelfSort is on. NewTable seeds it to -1; a bare Table{} lazily inits it to 0.
func (*Table) ToggleRowSelect ¶ added in v0.37.0
ToggleRowSelect flips row i's membership in the selection set -- selecting it if absent, deselecting it if present. A negative i is a no-op.
type TableAggregate ¶ added in v0.82.0
type TableAggregate int
TableAggregate names the reduction applied to a column's cells on a summary line (see TableColumn.Aggregate + Table.ShowSummary). The zero value AggregateNone means "no aggregate" -- the column stays blank on summary lines.
const ( // AggregateNone is the default: the column contributes nothing to a // summary line (it renders blank there). AggregateNone TableAggregate = iota // AggregateSum totals the column's numeric cells (non-numeric cells are // skipped; strconv.ParseFloat decides what parses). AggregateSum // AggregateAvg is the mean of the column's numeric cells. AggregateAvg // AggregateCount is the number of rows covered by the summary line, // regardless of whether their cells are numeric. AggregateCount // AggregateMin is the smallest of the column's numeric cells. AggregateMin // AggregateMax is the largest of the column's numeric cells. AggregateMax )
type TableColumn ¶ added in v0.7.0
type TableColumn struct {
Title string
Width int // pixels; 0 = auto (equal share of remaining space)
// Align controls horizontal placement of BOTH the header title and
// every body cell in this column. The zero value (AlignLeft) keeps
// the original left-justified behaviour; AlignRight is the natural
// choice for numeric columns, AlignCenter for short status flags.
Align Align
// Sortable opts this column into header-click sorting. The zero
// value (false) makes a header click a no-op, so existing callers
// that never set it keep the original passive-viewer behaviour.
Sortable bool
// Editable opts this column's body cells into inline editing: a click
// on such a cell opens a text editor over it (see Table.OnCellEdit).
// The zero value (false) keeps the original read-only behaviour.
Editable bool
// Aggregate selects how this column's cells are reduced to a single
// value on a summary line (see Table.ShowSummary). The zero value
// (AggregateNone) leaves the column blank on every summary line, so a
// column that opts out is byte-identical to before this field existed.
Aggregate TableAggregate
// Editor is the per-column editor seam: when set, beginEdit calls it to
// build the CellEditor overlaid on a cell of this column instead of the
// stock text field, so a column can edit through a numeric field, a
// drop-down, a date picker, ... The zero value (nil) uses the default
// text editor, byte-identical to before this field existed. See
// CellEditor.
Editor func() CellEditor
// Validate, when set, is run against an edit's proposed value at commit
// time (Enter). A non-nil error rejects the commit: the value is NOT
// written into Rows, the editor stays open for correction, EditError
// reports the error, and OnCellEditRejected fires. The zero value (nil)
// accepts every value, so a column that opts out commits exactly as
// before. It is the toolkit's own validation.Rule shape, so the stock
// rules (Required, MinLen, Pattern, All, ...) wire straight in.
Validate Rule
// Comparator orders two of this column's cell strings for SortByColumn
// (and, on the GroupBy column, for ArrangeGroups): it returns <0 when a
// sorts before b, 0 when equal, >0 when after. The zero value (nil) uses
// defaultCellCompare (numeric when both cells parse as numbers, else
// lexicographic), so a column that opts out sorts sensibly without any
// wiring.
Comparator func(a, b string) int
// GroupKey derives the grouping key from a cell value when this column is
// the GroupBy column: rows sharing a key fall in one group and the key is
// the group header's label (e.g. a first-letter or date-bucket key). The
// zero value (nil) groups by the raw cell value, byte-identical to before
// this field existed.
GroupKey func(cell string) string
// AggregateFunc is the custom-aggregate seam: when set, it reduces the
// column's cells over a summary line's row range to the displayed string,
// overriding the built-in Aggregate (so a column can show a median, a
// "min–max" span, a distinct count, ...). It receives one entry per row in
// range (ragged rows contribute ""). The zero value (nil) uses the
// built-in Aggregate, byte-identical to before this field existed.
AggregateFunc func(cells []string) string
}
TableColumn is one column definition: a header title + an optional fixed pixel Width. A Width of 0 marks the column as "auto" -- its width is computed at Draw time by evenly dividing the remaining pixel budget among all auto columns.
type TableEditActivation ¶ added in v0.150.0
type TableEditActivation int
TableEditActivation selects how a click (and Enter) begins an inline edit on an Editable cell. It only governs how an edit STARTS; commit/cancel and the editor itself are unchanged across the modes.
const ( // EditOnSingleClick is the default: a single click on an Editable cell // opens its editor immediately, byte-identical to before this field // existed. EditOnSingleClick TableEditActivation = iota // EditOnDoubleClick makes a single click select the cell's row and a // double-click (an EventClick tagged Code == TableDoubleClick) open the // editor -- the desktop details-view rename idiom. In this mode Enter on // the cursor row also opens the first Editable column's editor. EditOnDoubleClick // EditManual disables click- and key-driven activation entirely: an // Editable cell edits only when the host calls BeginEdit. EditManual )
type TableIconFunc ¶ added in v0.74.0
TableIconFunc paints a Table's optional per-row leading icon into the square rect r using ink. It is deliberately the exact signature of the stock DrawIconXxx painters in icons.go (DrawIconOpen, DrawIconNew, ...), so a caller wires any of those straight into Table.RowIcon -- e.g. `func(row int) (toolkit.TableIconFunc, bool) { return toolkit.DrawIconOpen, true }` -- or supplies its own painter of the same shape for a file-type glyph the stock set doesn't cover.
type TableInfo ¶ added in v0.180.0
type TableInfo struct {
Name string
IsView bool
Columns []ColumnInfo
}
TableInfo is one table or view and its columns.
type TagField ¶ added in v0.70.0
type TagField struct {
Base
// Placeholder is the muted hint drawn when there are no tags and no text.
Placeholder string
// contains filtered or unexported fields
}
TagField is a token / multi-tag text input: the user types into an in-progress buffer (Text) and each committed value becomes an inline removable pill (a Chip). Tokens flow left-to-right and wrap to a new row when the next one would overflow the widget's width; after the last token the in-progress Text is drawn with a caret, or -- when there are no tags and no text -- the muted Placeholder hint.
Editing mirrors the toolkit's other text widgets: EventChar appends a rune to Text; Enter (or a comma) commits strings.TrimSpace(Text) as a new tag, skipping blank + duplicate values; Backspace on an empty Text removes the last tag; and a click on a token's "x" close slot removes that specific tag. Every change to the tag set Sets the Tags() mvvm.Observable, so hosts subscribe / bind to it instead of a callback.
Each token is rendered by reusing the Chip widget (Closable: true) so the pill body, label + close "x" all match a standalone Chip exactly; hit-testing routes the click through the very same Chip.OnEvent against a rectangle computed the same way Draw lays it out, so the visible "x" and its click target never drift apart.
func NewTagField ¶ added in v0.70.0
NewTagField builds a TagField seeded with the given tags (a nil / empty slice is fine) and an empty in-progress Text.
func (*TagField) A11y ¶ added in v0.105.0
A11y reports the TagField as a group carrying its tag labels joined into one string -- the tokens are plain strings, not independent widgets, so the field as a whole is the accessible unit.
func (*TagField) Draw ¶ added in v0.70.0
Draw flows each tag as a closable Chip, then draws the in-progress Text with a caret, or the muted Placeholder when the field is entirely empty. It renders through the widget's effective font (Base.Font).
func (*TagField) Focused ¶ added in v0.70.0
func (f *TagField) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*TagField) HitRect ¶ added in v0.187.0
HitRect is the TagField's field-level tap target: Bounds clamped up to the touch minimum on each axis and centred, byte-identical to Bounds at DensityCompact. The per-token close "x" slots are drawn and hit-tested by the reused Chip (see OnEvent), so their own touch behaviour follows Chip's scaling in lockstep rather than being re-derived here — keeping the token pills and their hit rects consistent with a standalone Chip at every density.
func (*TagField) OnEvent ¶ added in v0.70.0
OnEvent applies keyboard editing + click-to-remove. Character input appends to Text (a bare comma is swallowed because it is the commit key); Enter and comma commit the trimmed Text; Backspace on empty Text drops the last tag; and a click routes through the token's Chip so its "x" close slot removes that tag. Event coordinates are widget-local per the toolkit convention, so hit-testing lays the tokens out from (0, 0).
func (*TagField) SetFocused ¶ added in v0.101.0
func (f *TagField) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
func (*TagField) Tags ¶ added in v0.70.0
func (t *TagField) Tags() *mvvm.Observable[[]string]
Tags is the committed token set (insertion order) as a shared mvvm.Observable: a host binds it two-way (or subscribes) instead of touching a field, and every commit / backspace / close Sets it — so a Set is the only way to change the tag set and there is no separate change callback. The Observable dedups with slices.Equal, so Setting the current value notifies nobody. Lazily created so a bare &TagField{} works.
func (*TagField) Text ¶ added in v0.70.0
func (t *TagField) Text() *mvvm.Observable[string]
Text is the in-progress input as a shared mvvm.Observable: character input and commits go through it, so a Set is the only way to change it. Lazily created so a bare &TagField{} works.
type TermCell ¶ added in v0.68.0
TermCell is one character cell in a TerminalView grid: a single rune plus its foreground and background colours. A zero Rune (or a space) paints no glyph — only the background fill. A colour left at its zero value (RGBA with A==0) is "unset" and falls back at paint time to the view's DefaultFG / DefaultBG (and, failing those, to the theme). Keeping colour per cell is what a console needs for coloured prompts, error text and ANSI-styled output.
type TerminalView ¶ added in v0.68.0
type TerminalView struct {
Base
// Cols and Rows are the grid dimensions. Cells is row-major: the
// cell at (col, row) lives at Cells[row*Cols+col].
Cols, Rows int
Cells []TermCell
// CursorCol / CursorRow are the block cursor's cell position, and
// the next write position for Write / Put. CursorVisible gates
// whether Draw paints the (inverted) block cursor.
CursorCol, CursorRow int
CursorVisible bool
// DefaultFG / DefaultBG are the pen colours Write / Put stamp into
// new cells and the fallback Draw uses for cells whose own colour is
// unset. When they too are unset (A==0) Draw falls back to the
// theme's OnSurface / Surface.
DefaultFG, DefaultBG RGBA
// OnKey, when non-nil, receives every event delivered to OnEvent
// (key down/up, char, click, …) so the host can feed a shell.
OnKey func(ev Event)
// CellW / CellH, when positive, pin an explicit per-cell pixel size
// that OVERRIDES the font-derived metrics — the escape hatch a host
// needs to lock a fixed cell geometry (e.g. a terminal that sizes its
// grid to an exact character box) without swapping in a metrics-only
// font shim. A zero value on an axis means "derive that axis from the
// active font", so leaving both unset reproduces the original
// font-metric behaviour exactly. Set them via SetCellSize (which
// recomputes immediately when bounds are already established) or by
// assigning the fields before the first SetBounds.
CellW, CellH int
// contains filtered or unexported fields
}
TerminalView is a fixed Cols×Rows grid of character cells — the reusable building block a terminal, console or REPL renders into. It owns the cell buffer, a block cursor and the scroll/wrap bookkeeping a shell needs; it does NOT parse escape sequences or run a PTY (that belongs to the host). SetBounds derives the cell size from the active font's metrics, Draw blits each cell (background fill + glyph in the foreground), and OnEvent forwards input to OnKey so the host can drive a shell.
func NewTerminalView ¶ added in v0.68.0
func NewTerminalView(cols, rows int) *TerminalView
NewTerminalView allocates a blank cols×rows grid with the cursor homed at (0, 0). A non-positive dimension panics: a zero-sized grid cannot render usefully and a silent fallback would hide the bug.
func (*TerminalView) A11y ¶ added in v0.105.0
func (t *TerminalView) A11y() A11yInfo
A11y reports the TerminalView as a textbox carrying its visible cell text, rows joined with newlines and trailing blank cells/rows trimmed.
func (*TerminalView) Cell ¶ added in v0.68.0
func (t *TerminalView) Cell(col, row int) TermCell
Cell returns the cell at (col, row), or a zero TermCell when the position is out of range.
func (*TerminalView) CellHeight ¶ added in v0.68.0
func (t *TerminalView) CellHeight() int
CellHeight is the pixel height of one cell (0 before SetBounds).
func (*TerminalView) CellWidth ¶ added in v0.68.0
func (t *TerminalView) CellWidth() int
CellWidth is the pixel width of one cell (0 before SetBounds).
func (*TerminalView) Draw ¶ added in v0.68.0
func (t *TerminalView) Draw(p painter.Painter, theme *Theme)
Draw blits the grid: a single span-fill clears the whole bounds to the default background (covering every default-background cell and any margin the grid does not tile), then each cell whose resolved background DIFFERS from that clear colour is over-filled with a rect span and every non-blank glyph is stamped. When CursorVisible the cursor cell is drawn inverted (foreground/background swapped), which reads as a block cursor over both empty and occupied cells.
This is one background pass, not two: the earlier form redundantly span-filled every cell on top of an already-painted whole-bounds clear, doubling the background cost for the common all-default grid. Skipping cells that match the clear colour is pixel-identical — those pixels already hold that exact colour — while non-default cells still get their own span so the final image is unchanged. Draw is a no-op until SetBounds has established a positive cell size.
func (*TerminalView) OnEvent ¶ added in v0.68.0
func (t *TerminalView) OnEvent(ev Event)
OnEvent forwards the event to OnKey when one is set, letting the host drive a shell from the grid's key and character input.
func (*TerminalView) Put ¶ added in v0.68.0
func (t *TerminalView) Put(col, row int, ru rune)
Put writes ru at (col, row) using the view's pen colours (DefaultFG / DefaultBG). It is the convenience form of SetCell for output that shares one colour. Out-of-range is a no-op.
func (*TerminalView) Resize ¶ added in v0.68.0
func (t *TerminalView) Resize(newCols, newRows int)
Resize reshapes the grid to newCols×newRows, preserving the top-left rectangle that still fits and blanking any newly exposed cells. The cursor is clamped into the new bounds. A non-positive dimension panics, matching NewTerminalView.
func (*TerminalView) ScrollUp ¶ added in v0.68.0
func (t *TerminalView) ScrollUp(n int)
ScrollUp shifts the grid up by n rows: the top n rows are discarded, the rest move up, and the bottom n rows are blanked. n<=0 is a no-op; n>=Rows clears the whole grid.
func (*TerminalView) SetBounds ¶ added in v0.68.0
func (t *TerminalView) SetBounds(r Rect)
SetBounds records the placement and recomputes the per-cell pixel size: the CellW / CellH override on each axis where it is positive, otherwise the active font's metrics (advance × height), so the grid tracks a font change while honouring any pinned geometry.
func (*TerminalView) SetCell ¶ added in v0.68.0
func (t *TerminalView) SetCell(col, row int, ru rune, fg, bg RGBA)
SetCell writes ru with explicit fg/bg at (col, row). An out-of-range position is a no-op, so callers need not bounds-check every write.
func (*TerminalView) SetCellSize ¶ added in v0.73.0
func (t *TerminalView) SetCellSize(w, h int)
SetCellSize pins an explicit per-cell pixel size, overriding the font-derived metrics. A non-positive value on an axis clears that axis's override, restoring derivation from the active font. When bounds are already established the resolved size updates immediately (so CellWidth / CellHeight and the next Draw reflect it at once); otherwise it takes effect at the first SetBounds.
func (*TerminalView) Write ¶ added in v0.68.0
func (t *TerminalView) Write(s string)
Write stamps s at the cursor using the pen colours, advancing the cursor cell by cell. '\n' returns the cursor to column 0 of the next row (scrolling when it overflows the bottom); '\r' returns it to column 0 of the current row; every other rune is written and the cursor advances, wrapping to the next row at end-of-line. It is the terminal-style output helper a shell's stdout feeds.
type TextDirection ¶ added in v0.43.0
type TextDirection int
TextDirection selects the base paragraph direction the toolkit uses when it reorders logical text into visual order before laying glyphs strictly left-to-right (see visualText). The zero value, DirLTR, is the default, and under it pure left-to-right text (Latin, digits, CJK) is reordered as a no-op — the visual order equals the logical order byte-for-byte, so existing rendering is unchanged.
const ( // DirLTR forces a left-to-right base level. All-LTR text is unchanged. DirLTR TextDirection = iota // DirRTL forces a right-to-left base level, so neutral runs and trailing // whitespace resolve towards the right. DirRTL // DirAuto derives the base level from the first strong character of the // text (Unicode rules P2/P3), defaulting to left-to-right. DirAuto )
func CurrentTextDirection ¶ added in v0.43.0
func CurrentTextDirection() TextDirection
CurrentTextDirection returns the active base text direction.
type TextRun ¶ added in v0.122.0
TextRun is one contiguous piece of drawn text at an absolute pixel position. Bounds is the run's rectangle in the same coordinate space the pointer events use (screen / surface pixels). Font measures per-character widths so a click resolves to a character boundary within the run.
func CollectRuns ¶ added in v0.122.0
CollectRuns gathers the [TextRun]s of every widget in the tree rooted at w that implements SelectableText, descending into any widget that exposes its children via [childContainer] (Container / HBox / VBox / …). It is the bridge a host uses to feed a widget tree into a TextSelection.
type TextSelection ¶ added in v0.122.0
type TextSelection struct {
// contains filtered or unexported fields
}
TextSelection is a screen-space, multi-run text selection driven by pointer drag. A host sets the current runs each frame (SetRuns), then routes press/drag/release (Begin/Drag/End) in the same coordinate space as the runs' Bounds. SelectedText returns the covered text; Draw paints the highlight.
The zero value is an empty, inactive selection.
func (*TextSelection) Begin ¶ added in v0.122.0
func (s *TextSelection) Begin(x, y int)
Begin starts a selection at the caret nearest (x, y): it sets the anchor and collapses the cursor onto it (an empty selection until the pointer drags).
func (*TextSelection) Clear ¶ added in v0.122.0
func (s *TextSelection) Clear()
Clear discards the selection entirely.
func (*TextSelection) CopySelection ¶ added in v0.122.0
func (s *TextSelection) CopySelection() string
CopySelection writes the selected text to the toolkit-wide clipboard (when non-empty) and returns it, so a host's copy chord is a one-liner. An empty selection leaves the clipboard untouched.
func (*TextSelection) Drag ¶ added in v0.122.0
func (s *TextSelection) Drag(x, y int)
Drag extends the selection to the caret nearest (x, y). Ignored unless a drag is in progress (between Begin and End).
func (*TextSelection) Draw ¶ added in v0.122.0
func (s *TextSelection) Draw(p painter.Painter, col RGBA)
Draw paints the selection highlight (a filled rect per covered run span) in col. Nothing is drawn for an empty selection. Runs are highlighted from their start-char x to their end-char x, full run height.
func (*TextSelection) End ¶ added in v0.122.0
func (s *TextSelection) End()
End finishes the drag, leaving the selection in place (so SelectedText / Draw keep working until the next Begin or Clear).
func (*TextSelection) IsEmpty ¶ added in v0.122.0
func (s *TextSelection) IsEmpty() bool
IsEmpty reports whether the selection covers zero characters (no drag, or the cursor still on the anchor).
func (*TextSelection) SelectedText ¶ added in v0.122.0
func (s *TextSelection) SelectedText() string
SelectedText returns the text the selection covers. Runs on the same visual line (equal Bounds.Y) are joined with a single space when the boundary is not already whitespace; a change of line inserts a newline. An empty selection returns "".
func (*TextSelection) SetRuns ¶ added in v0.122.0
func (s *TextSelection) SetRuns(runs []TextRun)
SetRuns replaces the selectable runs, sorted into document order (top-to- bottom, then left-to-right). Any run with empty Text or a zero-area rect is dropped. An active selection's endpoints are clamped to the new run set so a relayout (e.g. a resize that re-wraps text) can't leave them dangling.
type TextSpan ¶ added in v0.68.0
TextSpan is a coloured run within a single line, in half-open rune coordinates [Start, End). A TextView.Highlighter returns a slice of these to paint syntax-highlighted source: any rune not covered by a span keeps the default ink. Spans may overlap — a later span in the slice wins for the runes it covers. Start/End are clamped to the line's rune length at paint time, so a highlighter need not worry about off-by-one bounds. Color reuses the toolkit's RGBA (a painter colour), so a highlighter composes theme colours directly.
func SQLHighlight ¶ added in v0.180.0
SQLHighlight is a TextView.Highlighter that colours one line of SQL: line comments (-- to end of line), single-quoted string literals, numeric literals, and reserved keywords. Any run it does not classify keeps the default ink. It is line-local (no multi-line string / block-comment state), which matches the TextView's per-line Highlighter contract.
type TextView ¶
type TextView struct {
Base
// Decorations are remote co-editors' carets + selections in a shared
// buffer, each painted in its own colour with a name tag (see the
// Decoration type). Empty for a solo editor; the host keeps it in sync
// with the collaboration session. Painted on top of the local selection
// so a co-editor's presence is always visible.
Decorations []Decoration
// Highlighter, when non-nil, turns each line into coloured runs at
// paint time: Draw calls Highlighter(lineIndex, line) and paints
// the returned TextSpans in their colours, with any uncovered runes
// falling back to the default ink (theme.OnSurface). When nil (the
// zero value) Draw paints every line in a single ink exactly as it
// always has — a host adds syntax highlighting by setting this hook
// without the widget growing a lexer of its own.
Highlighter func(lineIndex int, line string) []TextSpan
// ShowLineNumbers, when true, reserves a left gutter sized to the
// widest line number and paints right-aligned 1-based line numbers
// there; the text, caret and selection all shift right by the
// gutter width. When false (the zero value) there is no gutter and
// layout is byte-identical to before this field existed.
ShowLineNumbers bool
// GutterColor is the ink for the line-number gutter. The zero value
// (a fully-transparent RGBA, A==0) means "unset": Draw then falls
// back to a muted tone (dimInk) that reads on any theme.
GutterColor RGBA
// RowBackground, when non-nil, is consulted once per visible buffer
// line to paint a full-width background band behind that line — over
// the Surface fill, under the gutter number, the ink and the caret.
// It returns (colour, true) to paint the band in colour, or
// (_, false) to leave the row on the plain Surface. This is the seam
// a CodeEditor uses for its current-line highlight, a search UI for
// match rows, or a diff view for added / removed rows, without
// TextView growing any of those concerns. When nil (the zero value)
// no band is painted and rendering is byte-identical to before this
// field existed.
RowBackground func(lineIndex int) (RGBA, bool)
// contains filtered or unexported fields
}
TextView is the multi-line cousin of Entry. Its reactive state is MVVM-only: the committed contents live on the Text() Observable (a host binds or subscribes to it, and every edit Sets it — there is no OnChange callback), and the caret line/col, the vertical scroll offset, the selection range and the focus flag are each exposed through their own Observable accessor so a host can bind or drive them without touching a field. The line buffer and the in-flight IME preview are internal editing state.
This is the foundation a native wasmdesk editor builds on top of: syntax highlighting, search/replace and find can live above TextView without it growing those concerns.
func NewTextView ¶
NewTextView builds a TextView pre-loaded with initial text (split on "\n"). Empty initial text creates a single empty line so the cursor always has a row to live on.
func (*TextView) A11y ¶ added in v0.40.0
A11y reports the TextView as a textbox carrying its full buffer text.
func (*TextView) CaretPixel ¶ added in v0.207.0
CaretPixel returns the top-left device pixel of the caret cell for a 0-based (line, col) position, using the SAME layout Draw and caretAt share (textLeftInset + the gutter, and the line pitch) — so a host or a test harness can place/probe the caret without duplicating the gutter/advance math (which drifts the moment the gutter padding or font changes). It is the inverse of caretAt: caretAt(CaretPixel(l,c)) == (l,c) for an in-range cell.
func (*TextView) ClearSelection ¶
func (t *TextView) ClearSelection()
ClearSelection collapses the selection to (CursorLine, CursorCol).
func (*TextView) CopySelection ¶
CopySelection returns the selected text and, when non-empty, writes it to the toolkit's global Clipboard (see clipboard.go) so it can be pasted into any other text widget. Leaves the buffer untouched. An empty selection is a no-op on the clipboard (mirrors a Ctrl+C-with-nothing-selected not clobbering whatever was copied before).
func (*TextView) CursorCol ¶
func (t *TextView) CursorCol() *mvvm.Observable[int]
CursorCol is the caret's 0-based rune column on its line, bindable.
func (*TextView) CursorLine ¶
func (t *TextView) CursorLine() *mvvm.Observable[int]
CursorLine is the caret's 0-based buffer line as a bindable Observable.
func (*TextView) CutSelection ¶
CutSelection returns the selected text, writes it to the global Clipboard (when non-empty) + removes it from the buffer.
func (*TextView) DeleteSelection ¶
func (t *TextView) DeleteSelection()
DeleteSelection removes the selected text + parks the cursor at the deletion point. No-op when the selection is empty.
func (*TextView) Draw ¶
Draw paints border + fill + every visible line + (when Focused) a 1-px vertical cursor stroke at the cursor's screen position.
Lines that would render past the bottom of the bounds are painted-but-clipped by the raster helpers; wrap in a ScrollView for proper scrollable behaviour.
func (*TextView) Focused ¶
func (t *TextView) Focused() *mvvm.Observable[bool]
Focused reports (and drives) keyboard focus as a bindable Observable: Draw paints the accent border + caret while it is true.
func (*TextView) HasSelection ¶
HasSelection reports whether the TextView's selection covers > 0 characters.
func (*TextView) Paste ¶
Paste inserts text at the cursor (after first deleting the selection if any). "\n" splits lines. The whole operation -- selection removal + insertion -- is a single undo step. Callers that want to paste the toolkit's global Clipboard contents pass ClipboardText() (this is what the Ctrl+V key path does); Paste itself stays a plain "insert this text" primitive so callers can also use it to insert arbitrary programmatic text.
func (*TextView) Redo ¶ added in v0.38.0
func (t *TextView) Redo()
Redo re-applies the most recently undone mutation, pushing the current state back onto the undo stack. No-op when there is nothing to redo.
func (*TextView) ScrollLine ¶ added in v0.106.0
func (t *TextView) ScrollLine() *mvvm.Observable[int]
ScrollLine is the buffer line painted at the top of the viewport — the vertical scroll offset — as a bindable Observable. Draw windows from here, the wheel shifts it, and every cursor move scrolls it to keep the caret visible. Reads clamp on the fly (clampedScrollLine), so a stale value after the buffer shrank is harmless.
func (*TextView) SelectAll ¶
func (t *TextView) SelectAll()
SelectAll selects the entire buffer + parks the cursor at its end.
func (*TextView) Selection ¶
func (t *TextView) Selection() *mvvm.Observable[Selection]
Selection is the (start, end) range the host paints highlighted + range-deletes via DeleteSelection / cut+paste via CopySelection / CutSelection / Paste, as a bindable Observable. An empty selection (Start == End) means "no selection"; HasSelection() is the convenience predicate.
func (*TextView) SelectionText ¶
SelectionText returns the selected substring, or "".
func (*TextView) SetSelection ¶
SetSelection records a new (start, end) selection without moving the cursor.
func (*TextView) SetText ¶
SetText replaces the entire buffer + parks the cursor at (0,0). It goes through the Text() Observable, so bindings/subscribers fire.
func (*TextView) Text ¶
func (t *TextView) Text() *mvvm.Observable[string]
Text is the committed contents as a shared mvvm.Observable: a host binds it two-way (or subscribes) instead of touching a field, and every edit Sets it — so a Set is the only way to change the text and there is no change callback. A host that Sets it directly (a VM→widget push) reloads the line buffer. Lazily created so a bare &TextView{} works.
type Theme ¶
type Theme struct {
Background RGBA
Surface RGBA
SurfaceAlt RGBA
OnBackground RGBA
OnSurface RGBA
Accent RGBA
Border RGBA
// StarFilled / StarEmpty are the two tones a star-rating strip paints:
// StarFilled is the warm gold of a scored star, StarEmpty the neutral grey
// of an un-scored one. They are palette colours so a rating reads the same
// as the rest of the theme (and re-tints in light vs dark) rather than
// hardcoding gold/grey. A theme that leaves them zero (A==0) — the GTK
// loader and the built-in GTK/Fluent/Adwaita palettes do — lets [Rating]
// fall back to a built-in gold and the theme's own Border grey, so every
// theme still shows stars without having to know the field exists.
StarFilled RGBA
StarEmpty RGBA
// Extra holds @define-color entries from GTK-source themes that don't
// map to one of the canonical fields above (headerbar_bg_color,
// success_color, ...). Populated by LoadGTKTheme; nil for code-built
// themes. A host that needs a custom colour (e.g. for its window-
// decoration painter) looks it up here without growing this struct
// for every GTK colour name in the wild.
Extra map[string]RGBA
}
Theme bundles every visual constant a widget needs to render itself. One Theme value cascades through every widget in an app, so swapping to a dark / Aqua / Fluxbox theme is a single assignment.
Field naming follows Material/Fluxbox conventions:
- Background = the surface a widget sits on (panel/window body)
- Surface = the widget's own filled body (button face, ...)
- SurfaceAlt = a contrasting tone (hovered button, alternating row)
- OnBackground / OnSurface = ink/text on those grounds
- Accent = focus rings, the active-tab underline, the link colour
- Border = a thin separator line drawn around or between surface regions
func AdwaitaDark ¶ added in v0.153.0
func AdwaitaDark() *Theme
AdwaitaDark returns the Adwaita dark palette as a Theme, the drop-in dark sibling of AdwaitaLight.
func AdwaitaLight ¶ added in v0.153.0
func AdwaitaLight() *Theme
AdwaitaLight returns the Adwaita light palette as a Theme. Like DefaultLight it never fails, so callers use it as a drop-in.
func DefaultDark ¶
func DefaultDark() *Theme
DefaultDark is a low-contrast dark theme. Same shape as DefaultLight; used by themed wasmaqua apps + test coverage.
func DefaultLight ¶
func DefaultLight() *Theme
DefaultLight is a low-stakes light theme used by tests + as the fall-through when an app doesn't supply its own. Numbers are the Fluxbox Light palette wasmbox's dock already uses, so a widget dropped into the dock without an explicit theme renders cleanly.
func FluentDark ¶ added in v0.153.0
func FluentDark() *Theme
FluentDark returns the Fluent dark palette as a Theme, the drop-in dark sibling of FluentLight. Its accent (#4CC2FF) is a bright cyan, so on-accent ink is BLACK for contrast.
func FluentLight ¶ added in v0.153.0
func FluentLight() *Theme
FluentLight returns the Fluent light palette as a Theme. Like DefaultLight it never fails, so callers use it as a drop-in.
func LoadGTKTheme ¶
LoadGTKTheme parses a GTK theme source (the gtk.css or gtk-3.0/gtk.css or gtk-4.0/gtk.css that ships with a libadwaita / GTK3 theme) and returns a Theme that mirrors the theme's palette.
We recognise BOTH the GTK3 names (theme_bg_color / theme_fg_color / …) AND the libadwaita / GTK4 names (window_bg_color / accent_bg_color / …); when both are present the GTK4 name wins because it is the newer convention and a theme that defines both intends the GTK4 name as canonical. Unknown @define-color declarations are kept in the returned Theme's Extra map so themes that ship custom color names (e.g. "headerbar_bg_color" for a window-decoration painter) can still be looked up by a host without growing the canonical Theme struct.
Anything beyond @define-color (selectors, properties, gradients, image references) is ignored — the toolkit is a flat-paint compositor that only consumes solid RGBA values. We do not implement a full CSS parser for the same reason.
The mapping from GTK names to toolkit Theme fields:
GTK4 (preferred) | GTK3 (fallback) | Theme field -------------------------|--------------------------|-------------- window_bg_color | theme_bg_color | Background window_fg_color | theme_fg_color | OnBackground view_bg_color | theme_base_color | Surface view_fg_color | theme_text_color | OnSurface card_bg_color | insensitive_bg_color | SurfaceAlt accent_bg_color | theme_selected_bg_color | Accent borders | borders | Border
Returns an error only if the input is empty (defensively) — malformed declarations are skipped, not fatal, so a real-world gtk.css with a stray syntax error still yields the rest of its palette.
func WhiteSurDark ¶ added in v0.9.3
func WhiteSurDark() *Theme
WhiteSurDark returns the WhiteSur dark palette as a Theme, the drop-in dark sibling of WhiteSurLight.
func WhiteSurLight ¶ added in v0.9.3
func WhiteSurLight() *Theme
WhiteSurLight returns the WhiteSur light palette as a Theme. It never fails (the embedded CSS is non-empty and well-formed), so unlike LoadGTKTheme it has no error return -- callers use it as a drop-in for DefaultLight.
type Thumbnail ¶ added in v0.80.0
type Thumbnail struct {
Base
// Pixels is the source RGBA image (IW*IH*4 bytes); an invalid buffer paints
// just the frame + label.
Pixels []byte
IW, IH int
// Label is an optional caption drawn in a strip along the bottom; empty
// gives the whole cell to the image.
Label string
// Alt is the tile's accessible name when it should differ from the visible
// Label — a filename shown as the caption, say, while the picture itself is
// worth describing. Empty falls back to Label.
Alt string
// Area selects the box-averaging downscale over nearest-neighbour.
Area bool
// OnClick fires on EventClick (nil-safe) so a container can select the tile.
OnClick func()
// contains filtered or unexported fields
}
Thumbnail renders a source RGBA buffer scaled down (aspect-preserved, centred) into its bounds, with an optional caption strip and a selected/hover border. It is the window-preview tile an Exposé grid, an Alt-Tab switcher, or a dock-hover peek is built from: give it the client's framebuffer + a title, size it into a grid cell, and it paints a shrunk snapshot with a label under it.
Downscale quality: Nearest (the zero value) is one sample per destination pixel — fast, fine for a live-updating peek. Area averages the source region each destination pixel covers, so a large snapshot shrunk to a small tile stays legible instead of shimmering; use it for static previews.
The average comes from go-images (images.Area, the same box filter as PIL's Image.BOX and OpenCV's INTER_AREA) rather than from a loop written here: it is an image-processing operation and go-images is where those live. It is also weighted by fractional coverage where the toolkit's own loop truncated the box to whole source pixels, so an uneven ratio now averages what it actually covers.
Because a scaled image depends only on the source and the target size, it is computed once and kept. A caller that overwrites the CONTENTS of Pixels in place must call Invalidate; assigning a new buffer through SetPixels does it for them. This is the static-preview case by construction — a live-updating peek wants Nearest, which keeps no cache and reads Pixels every frame.
Selection: Selected draws a 2-px Accent border (the switcher's current choice); Hover draws a 1-px Accent border (the pointer is over the tile). Selected wins when both are set. With neither, a plain 1-px Border frames the image. OnClick fires on EventClick so a grid can select the tile.
func NewThumbnail ¶ added in v0.80.0
NewThumbnail wraps a source image (length must equal w*h*4) in a nearest- neighbour thumbnail with no caption.
func (*Thumbnail) A11y ¶ added in v0.130.0
A11y reports the Thumbnail as an img named by its Alt text, falling back to the caption it already shows.
func (*Thumbnail) Draw ¶ added in v0.80.0
Draw paints the (downscaled) image into the image area, the optional caption strip, then the selection/hover/plain border over the whole cell. An empty rectangle paints nothing.
func (*Thumbnail) Hover ¶ added in v0.80.0
func (t *Thumbnail) Hover() *mvvm.Observable[bool]
Hover is the tile's reactive hover state as a shared mvvm.Observable: true draws the 1-px Accent border (the pointer is over the tile). Lazily created, defaulting to false.
func (*Thumbnail) Invalidate ¶ added in v0.143.0
func (t *Thumbnail) Invalidate()
Invalidate drops the cached Area downscale. Call it after overwriting the contents of Pixels in place; SetPixels already does.
func (*Thumbnail) OnEvent ¶ added in v0.80.0
OnEvent fires OnClick on EventClick; other event kinds are ignored. OnClick is nil-safe.
func (*Thumbnail) Selected ¶ added in v0.80.0
func (t *Thumbnail) Selected() *mvvm.Observable[bool]
Selected is the tile's reactive selection state as a shared mvvm.Observable: true draws the 2-px Accent border (a switcher's current choice). Lazily created, defaulting to false.
type TimePicker ¶ added in v0.75.0
type TimePicker struct {
Base
MinuteStep int // increment per minute step (default 1; e.g. 5 or 15)
Use12h bool // display as 12-hour with an AM/PM segment
// contains filtered or unexported fields
}
TimePicker is a stepper-based time-of-day picker — the pixel sibling of the calendar-oriented DatePicker. It shows two spinners laid left-to-right, one for the hour and one for the minute, each with a ▲ (up) and ▼ (down) affordance the user clicks to increment / decrement. A ":" separates them. When Use12h is set an extra AM/PM toggle cell is drawn on the right and the hour reads as a 12-hour clock, while the stored hour stays 0..23.
The widget never reads the wall clock: the initial hour+minute are supplied by the caller (NewTimePicker), so it stays deterministic and host-agnostic.
The reactive state is MVVM-only: the current hour and minute each live in an unexported mvvm.Observable exposed via TimePicker.Hour and TimePicker.Minute. A host binds either (Set / Subscribe / two-way) — there are no settable Hour / Minute fields and no change callback.
Minute wrap policy: StepMinute wraps the minute within 0..59 WITHOUT carrying into the hour — stepping past :59 rolls back to the low end of the same hour. This keeps each spinner independent (the hour only ever changes via the hour spinner or the AM/PM toggle), matching how a native two-field time stepper behaves.
func NewTimePicker ¶ added in v0.75.0
func NewTimePicker(hour, minute int) *TimePicker
NewTimePicker builds a TimePicker initialised to (hour, minute). The inputs are normalised into range (hour into 0..23, minute into 0..59) so an out-of-range caller value can never desync the display, and MinuteStep defaults to 1.
func (*TimePicker) A11y ¶ added in v0.105.0
func (t *TimePicker) A11y() A11yInfo
A11y reports the TimePicker as a group carrying its selected time as a 24-hour "HH:MM" string (the picker always stores 24-hour internally).
func (*TimePicker) Draw ¶ added in v0.75.0
func (tp *TimePicker) Draw(p painter.Painter, theme *Theme)
Draw paints the frame, the two spinner cells (value text + ▲/▼ buttons), the ":" separator and, when Use12h, the AM/PM toggle cell.
func (*TimePicker) HitRect ¶ added in v0.187.0
func (tp *TimePicker) HitRect() Rect
HitRect is the TimePicker's field-level tap target: Bounds clamped up to the touch minimum on each axis and centred, byte-identical to Bounds at DensityCompact. The two ▲/▼ steppers are stacked within each spinner cell, so they cannot each grow to the 44px floor without overlapping; the field clamp guarantees the control as a whole meets the touch height, while the cells (scaled(tpCellW) wide) grow with density so the steppers stay legible.
func (*TimePicker) Hour ¶ added in v0.75.0
func (tp *TimePicker) Hour() *mvvm.Observable[int]
Hour is the current hour (0..23, always 24-hour) as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Hour field. A spin or an AM/PM toggle Sets it (wrapped into 0..23); subscribers are notified.
func (*TimePicker) Minute ¶ added in v0.75.0
func (tp *TimePicker) Minute() *mvvm.Observable[int]
Minute is the current minute (0..59) as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Minute field. A spin Sets it (wrapped into 0..59); subscribers are notified.
func (*TimePicker) OnEvent ¶ added in v0.75.0
func (tp *TimePicker) OnEvent(ev Event)
OnEvent handles EventClick: a click on an ▲/▼ button steps the matching field, a click on the AM/PM cell toggles the meridiem, and a click anywhere else (the value text, the ":" separator, outside a button) does nothing. The hit regions are computed from the same layout() Draw uses, so clicks always line up with what is painted.
func (*TimePicker) StepHour ¶ added in v0.75.0
func (tp *TimePicker) StepHour(delta int)
StepHour adjusts the hour by delta (typically +1 / -1), wrapping 0..23 in both directions, then Sets the Hour Observable.
func (*TimePicker) StepMinute ¶ added in v0.75.0
func (tp *TimePicker) StepMinute(delta int)
StepMinute adjusts the minute by delta*MinuteStep (delta is a direction, +1 / -1), wrapping within 0..59 without carrying into the hour, then Sets the Minute Observable. A non-positive MinuteStep is treated as 1 so a click is never a silent no-op.
func (*TimePicker) String ¶ added in v0.75.0
func (tp *TimePicker) String() string
String is the formatted time: "15:04" (24-hour, zero-padded) by default, or "3:04 PM" when Use12h. Midnight (0) and noon (12) both render as 12 in 12-hour form, as AM and PM respectively.
func (*TimePicker) ToggleAmPm ¶ added in v0.75.0
func (tp *TimePicker) ToggleAmPm()
ToggleAmPm flips between AM and PM by shifting the hour ±12, keeping the stored value in 0..23, then Sets the Hour Observable.
type Timeline ¶ added in v0.9.0
type Timeline struct {
Base
Events []TimelineEvent
// Horizontal runs the rail left-to-right (a process ribbon) instead of
// top-to-bottom. The zero value (false) keeps the original vertical
// activity-stream layout. A bool rather than the shared Orientation enum
// because Timeline's natural default is vertical, whereas that enum's
// zero value is Horizontal — a plain flag keeps the non-breaking default
// unambiguous.
Horizontal bool
// contains filtered or unexported fields
}
Timeline is a vertical event log — think a GitHub PR activity stream or a Discord message list. The widget draws a 1-px vertical rail on the left, one filled square marker per event on that rail, and the event's Title (+ optional Detail) rendered to the right of the marker.
A vertical Timeline scrolls: the mouse wheel (EventScroll) shifts the event window up/down, clamped at both ends, and the events are clipped to Bounds so a long log never bleeds past the widget's box. EventAt maps a point to the event under it through the same offset, so a caller who wants click-to-focus can hit-test the scrolled list without redoing the layout math. A horizontal Timeline stays a fixed left-to-right ribbon (no scroll).
func NewTimeline ¶ added in v0.9.0
func NewTimeline(events []TimelineEvent) *Timeline
NewTimeline constructs a Timeline carrying the given events. A nil events slice is normalised to a non-nil empty slice so downstream code (range loops, len() checks) never has to guard for nil separately.
func (*Timeline) A11y ¶ added in v0.40.0
A11y reports the Timeline as a list carrying its event count.
func (*Timeline) Draw ¶ added in v0.9.0
Draw paints the surface fill, the vertical rail line, one marker per event and each event's Title (+ optional Detail). The rail is painted BEFORE the markers so a marker overwrites the rail pixel where they intersect, giving the marker its full square silhouette without a separate clipping pass.
func (*Timeline) EventAt ¶ added in v0.108.0
EventAt maps a widget-local (x, y) to the index of the vertical-timeline event under it, accounting for the scroll offset, or -1 for the padding bands, a point outside the widget's width, or empty space below the last event. A horizontal timeline always returns -1 (its ribbon layout is hit-tested by the caller). It is the offset-aware inverse of Draw's row walk, so a click after scrolling resolves to the event actually shown.
func (*Timeline) OnEvent ¶ added in v0.108.0
OnEvent handles the mouse wheel: a vertical timeline scrolls its event list by EventScroll.Delta rows (clamped at both ends by ScrollBy) so a long log stays reachable. Every other event -- and any event on a horizontal timeline -- is ignored, preserving Timeline's otherwise passive-display contract.
type TimelineEvent ¶ added in v0.9.0
type TimelineEvent struct {
Title string
Detail string
Kind TimelineKind
}
TimelineEvent is one row in a Timeline's Events slice. Title is the always-visible headline; Detail is an optional second line rendered underneath in the dim Border ink (matching HeaderBar's subtitle convention). Kind drives the marker colour.
type TimelineKind ¶ added in v0.9.0
type TimelineKind int
TimelineKind selects the semantic colour of a timeline event's marker square. TimelineDefault reuses the theme's Accent so a neutral event matches the app's palette; the other three carry fixed shades — green for success, amber for warning, red for error — reusing the exact RGB tuples Alert already ships, so an Alert banner and a Timeline row read as the same colour language.
const ( // TimelineDefault is a neutral event. Marker fill = Theme.Accent. TimelineDefault TimelineKind = iota // TimelineSuccess flags a completed step ("Deploy OK"). Green. TimelineSuccess // TimelineWarning flags a non-fatal event ("High latency"). Amber. TimelineWarning // TimelineError flags a failure ("Build failed"). Red. TimelineError )
type Toast ¶ added in v0.8.0
type Toast struct {
Base
Text string
Kind ToastKind
// ActionLabel, when non-empty, arms a small action button rendered
// right-aligned inside the pill (e.g. "Undo") and makes OnEvent
// route clicks landing in that button to Action. Empty (the zero
// value) means "no action" -- Draw + AnchorIn behave exactly as a
// pre-action Toast. Superseded by Actions when that slice is non-empty.
ActionLabel string
// Action is invoked when the action button is clicked. Nil-safe:
// clicking the button still dismisses the toast when Action is nil.
Action func()
// Lines, when non-empty, supplies the message as distinct rows (a title
// line plus one or more body lines) stacked top-to-bottom, instead of the
// single joined Text. The zero value (nil/empty) falls back to Text, so a
// one-line toast is unchanged.
Lines []string
// Actions, when non-empty, supplies several action buttons (superseding
// the single ActionLabel/Action pair). Buttons are laid out along the
// right edge in slice order, each with its own divider + label. The zero
// value (nil/empty) falls back to the ActionLabel/Action pair.
Actions []ToastAction
// Icon paints a vector glyph to the left of the text when Pixels is not a
// valid image. May be nil (no icon).
Icon IconFunc
// Pixels is an optional RGBA image (IW*IH*4 bytes) drawn to the left of the
// text instead of Icon, aspect-preserved + centred. IW/IH are its source
// dimensions.
Pixels []byte
IW, IH int
// contains filtered or unexported fields
}
Toast is a short-lived, self-dismissing pill that slides in over the app's normal frame, holds for a few ticks, then hides itself. Distinct from Notification in three ways:
- Toast carries a Kind (like Alert) so the pill's fill colour conveys severity at a glance; Notification is always Accent.
- Toast's Life = 0 sentinel means "sticky" (do not auto-hide), letting a host post a persistent pill without a matching Life-budget assignment.
- Toast is designed to STACK: several Toast values can share the same host, each Bounds()'d to its own row; the host drives each toast's Visible + Life observables and iterates Tick over the collection.
The host drives Life via Tick() from its own animation loop (typically a rAF tick).
A Toast may also carry a single action ("Copied — Undo"): set ActionLabel + Action to render a small button inside the pill's right edge. Leaving ActionLabel empty (the zero value) opts out -- the pill renders + sizes exactly as a plain message toast.
Three optional enrichments layer on top without disturbing the plain path (Icon nil, Lines empty, Actions empty renders byte-identically to the original single-line / single-action Toast):
- Icon: an IconFunc vector glyph or an RGBA image ([Pixels]/[IW]/[IH]) painted, vertically centred, to the LEFT of the text.
- Lines: distinct message rows (e.g. a bold-reading title line plus a body line) stacked instead of a single joined Text.
- Actions: a slice of (ToastAction) buttons (each a label + callback) laid out right-to-left along the pill's right edge, superseding the single ActionLabel/Action pair.
func NewToast ¶ added in v0.8.0
NewToast builds a hidden Toast with the given text + kind. The host Sets Visible().Set(true) (typically via a Show helper it wraps around the widget) + Sets Life to arm the auto-dismiss countdown.
func (*Toast) A11y ¶ added in v0.40.0
A11y reports the Toast as a status region named by its message.
func (*Toast) AnchorIn ¶ added in v0.33.0
AnchorIn sizes the toast to its content (icon + text lines + action buttons, each present) and positions it at corner of host, stacked at row index (0 = the row nearest the docked edge). Top corners stack downward, bottom corners upward, so a host can lay out a column of toasts by calling AnchorIn once per visible toast with an increasing index.
func (*Toast) ButtonRects ¶ added in v0.114.0
ButtonRects returns the laid-out rectangle of each action button in the toast's local (painted) coordinate space: X measured from the pill's LEFT edge and Y from its TOP (independent of the toast's current Bounds() origin), each rect spanning the full pill height. The i-th rect is the click target for the i-th action -- the Actions slice in order, else the single button synthesised from the legacy ActionLabel/Action pair. It returns nil when the toast carries no actions.
The rects use the toast's current Bounds() width + height, so call it AFTER sizing the pill (AnchorIn, or a direct SetBounds). A host that hit-tests a click itself -- rather than routing it through OnEvent -- maps the click into the toast's local space (click minus the pill's top-left) and finds the button whose rect contains it; OnEvent hit-tests against these very rects, so the two paths can never disagree.
func (*Toast) Draw ¶ added in v0.8.0
Draw paints the pill when Visible. Filled Kind-coloured panel with a 1-px Border stroke; the icon (when set) at the left, then the message line(s) in the accent-inverted ink so they stay legible against every Kind's face. Each action button is a 1-px Border divider plus its label, laid out along the right edge in Actions order. Nothing drawn when hidden.
func (*Toast) Life ¶ added in v0.8.0
func (t *Toast) Life() *mvvm.Observable[int]
Life is the number of Tick() calls remaining before the toast auto-hides, as a shared mvvm.Observable — there is no settable Life field. The zero value is a sentinel meaning "sticky": Tick is a no-op until the host Sets a positive Life. When Life is positive, each Tick decrements it; when the countdown reaches zero Visible is cleared.
func (*Toast) OnEvent ¶ added in v0.36.0
OnEvent runs the clicked button's Callback + hides the toast when a click lands inside an action button; a click anywhere else in the pill (or when there are no actions) is a no-op. ev.X/ev.Y are widget-local. The Callback is nil-checked, so an action-less button still dismisses the toast on click. The hit-test runs against Toast.ButtonRects, the same geometry a host reads to route a click itself.
func (*Toast) Tick ¶ added in v0.8.0
func (t *Toast) Tick()
Tick decrements Life by 1 when Life is positive. When the countdown reaches 0 the toast auto-hides. Life == 0 is a sticky sentinel and leaves Visible untouched, so a host may post a persistent toast by leaving Life at its zero value.
func (*Toast) Visible ¶ added in v0.8.0
func (t *Toast) Visible() *mvvm.Observable[bool]
Visible is the toast's show/hide state as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Visible field. Draw paints the pill exactly while it is true. The zero value is false, so a bare &Toast{} starts hidden.
type ToastAction ¶ added in v0.86.0
type ToastAction struct {
Label string
Callback func()
}
ToastAction is one button in a multi-action Toast: a Label the user clicks and a Callback run on click. Callback is nil-safe (the toast still dismisses when it is nil), matching the legacy single-Action contract.
type ToastKind ¶ added in v0.8.0
type ToastKind int
ToastKind selects the semantic colour of a Toast pill. ToastInfo reuses the theme's Accent (the same tint used by focus rings + the Notification banner); the other three carry hard-coded shades tuned for meaning -- green for success, amber for warning, red for error -- mirroring AlertKind so a Toast and an Alert with the same kind read as visual siblings.
const ( // ToastInfo is a neutral heads-up ("Copied to clipboard"). Rendered // in Theme.Accent so it matches the app's own accent colour. ToastInfo ToastKind = iota // ToastSuccess signals a completed operation ("File uploaded"). Green. ToastSuccess // ToastWarning flags a non-fatal issue ("Battery low"). Amber. ToastWarning // ToastError signals a failure the user must address ("Network // unreachable"). Red. ToastError )
type ToggleButton ¶
type ToggleButton struct {
Base
// Label is the button caption (config).
Label string
// contains filtered or unexported fields
}
ToggleButton is a Button with a sticky on/off state. A click flips Pressed, notifying the Pressed Observable's subscribers. Pressed = Theme.Accent face, unpressed = Theme.Surface; the label is rendered centered in the button.
The reactive pressed state is MVVM-only: it lives in an unexported Observable exposed via ToggleButton.Pressed. There is no settable Pressed field and no OnToggle callback — a host binds Pressed() (Set / Subscribe / two-way).
func NewToggleButton ¶
func NewToggleButton(label string, pressed bool) *ToggleButton
NewToggleButton constructs a ToggleButton with the given label + initial state.
func (*ToggleButton) A11y ¶ added in v0.40.0
func (t *ToggleButton) A11y() A11yInfo
A11y reports the ToggleButton as a button carrying its pressed state.
func (*ToggleButton) Draw ¶
func (t *ToggleButton) Draw(p painter.Painter, theme *Theme)
Draw paints the face + border + centred label.
func (*ToggleButton) Focused ¶ added in v0.101.0
func (f *ToggleButton) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*ToggleButton) HitRect ¶ added in v0.187.0
func (t *ToggleButton) HitRect() Rect
HitRect is the toggle button's interactive rectangle: its drawn Bounds clamped up to the density hit-target and centred over them (see [touchHitRect]). Byte-identical to Bounds under DensityCompact; a finger-sized target under DensityTouch.
func (*ToggleButton) HitTest ¶ added in v0.187.0
func (t *ToggleButton) HitTest(px, py int) bool
HitTest reports whether a surface point falls on the toggle button's (touch-clamped) hit rect.
func (*ToggleButton) OnEvent ¶
func (t *ToggleButton) OnEvent(ev Event)
OnEvent: a click flips Pressed; a move tracks the hover face. A Disabled toggle ignores every kind.
func (*ToggleButton) Pressed ¶
func (t *ToggleButton) Pressed() *mvvm.Observable[bool]
Pressed is the sticky on/off state as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Pressed field. A click or a Space/Enter key press flips it; subscribers are notified. A bare ToggleButton (no NewToggleButton) lazily initialises to false on first access.
func (*ToggleButton) SetFocused ¶ added in v0.101.0
func (f *ToggleButton) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
type Toolbar ¶
type Toolbar struct {
Base
Items []ToolbarItem
ButtonW int // default ToolbarButtonW
ButtonH int // default ToolbarButtonH
// Orientation lays the buttons out left-to-right (Horizontal, the zero
// value) or top-to-bottom (Vertical). A vertical toolbar draws its
// separators as horizontal dividers, so the same Items slice works as a
// side rail without change.
Orientation Orientation
// contains filtered or unexported fields
}
Toolbar is a horizontal strip of square icon-buttons + optional separators. Each entry has a Label (used as the fallback glyph character), an optional Icon (drawn as an RGBA blit when non-empty), an OnClick callback + a Disabled flag.
Toolbar is the icon-strip that sits below a MenuBar; it composes cleanly with both Notebook + Statusbar so a "stock GTK" window can be assembled out of MenuBar + Toolbar + Notebook + Statusbar.
func NewToolbar ¶
func NewToolbar(items []ToolbarItem) *Toolbar
NewToolbar builds a Toolbar with the given items. ButtonW/ButtonH are left at zero — "use the default" — so the default square size resolves through scaled at draw time and grows with HiDPI and touch density; a caller that wants a fixed cell sets ButtonW/ButtonH explicitly (honoured verbatim, like CheckButton.Size). At compact/1x the resolved default is ToolbarButtonW × ToolbarButtonH, byte-identical to before.
func (*Toolbar) A11y ¶ added in v0.40.0
A11y reports the Toolbar as a toolbar carrying its item count.
type ToolbarItem ¶
type ToolbarItem struct {
Label string
Icon []byte // optional ButtonW x ButtonH RGBA; nil = draw Label initial
OnClick func()
Disabled bool
// Separator, when true, draws a 1-pixel vertical divider instead of
// a button. Label/Icon/OnClick are ignored.
Separator bool
}
ToolbarItem is one cell in a Toolbar.
type Tooltip ¶
type Tooltip struct {
Base
Text string
Placement TooltipPlacement
// contains filtered or unexported fields
}
Tooltip is a small text bubble shown near the cursor when the user hovers over a target widget. The host app drives the reactive visibility + anchor (typically toggled by a mouse-enter/leave handler with a 500 ms delay) through the Tooltip.Visible and Tooltip.Anchor Observables; the toolkit's role is the rendering geometry.
Auto-sized to the Text width + padding; positioned on the side of the anchor chosen by Placement (below by default). Text + Placement are set-once config; the reactive state (whether it is shown, and which rect it points at) is MVVM-only, unexported behind the Observable accessors.
func NewTooltip ¶
NewTooltip builds a hidden tooltip with the given text. Both reactive Observables are initialised (Visible false, Anchor the zero Rect).
func (*Tooltip) Anchor ¶
func (t *Tooltip) Anchor() *mvvm.Observable[Rect]
Anchor is the rect the tooltip points at as a shared mvvm.Observable: Tooltip.Show Sets it to the anchored widget's rect. There is no settable Anchor field. Lazily initialised to the zero Rect so a bare &Tooltip{} is usable.
func (*Tooltip) Visible ¶
func (t *Tooltip) Visible() *mvvm.Observable[bool]
Visible is the tooltip's shown/hidden state as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Visible field. Tooltip.Show Sets it true, Tooltip.Hide Sets it false; Draw reads it. Lazily initialised to false so a bare &Tooltip{} is usable.
type TooltipPlacement ¶ added in v0.27.0
type TooltipPlacement int
TooltipPlacement selects which side of the anchor the bubble sits on. Below is the zero value (the original behaviour).
const ( // PlaceBelow puts the bubble under the anchor (the default). PlaceBelow TooltipPlacement = iota // PlaceAbove puts the bubble over the anchor. PlaceAbove // PlaceLeft puts the bubble to the anchor's left. PlaceLeft // PlaceRight puts the bubble to the anchor's right. PlaceRight )
type TreeNode ¶
type TreeNode struct {
Label string
Expanded bool
Children []*TreeNode
// Anything the host wants to associate with this node (typically a
// path, an id, or the model object). The toolkit doesn't read it.
Data any
}
TreeNode is one entry in a TreeView. Children are nested arbitrarily deep; Expanded controls whether the children are rendered.
type TreeTable ¶ added in v0.42.0
type TreeTable struct {
Base
// Columns are the header cells. A zero Width means "auto" — the
// column claims an equal share of whatever pixel budget is left
// after the fixed-Width columns, same rule as Table.Columns.
Columns []TreeTableColumn
// Root holds the top-level nodes (a forest, not a single root, so a
// host can list multiple top-level entries without a synthetic
// invisible parent).
Root []*TreeTableNode
// contains filtered or unexported fields
}
TreeTable renders a Table-shaped grid whose body rows form a TREE: a fixed header row of column titles sits above body rows built from the visible (expand-aware) flattening of Root, exactly like TreeView flattens its single Root node. The first column carries the tree structure (indentation + a ▸/▾ disclosure glyph); the rest are plain cells.
Rendering is windowed (virtualized) the same way TreeView is: only the rows that fit inside Bounds().H (below the header) are ever painted, no matter how many nodes are visible in the flattened order. See ScrollRow.
Use for file managers, outline-grids, or anything that's "a Table, but the rows nest".
func NewTreeTable ¶ added in v0.42.0
func NewTreeTable(cols []TreeTableColumn, root []*TreeTableNode) *TreeTable
NewTreeTable builds a TreeTable with the given columns + forest of root nodes.
func (*TreeTable) A11y ¶ added in v0.105.0
A11y reports the TreeTable as a tree carrying the selected node's first-column label (the column that carries the tree structure), or "" with no selection.
func (*TreeTable) Draw ¶ added in v0.42.0
Draw paints the header, then the rows in the current scroll window: flattened nodes [ScrollRow, ScrollRow+bodyVisibleRows()). The first column is indented by depth + prefixed with a ▸/▾ disclosure glyph when the node has Children (identical shape to TreeView's chevron); the rest are plain cells, aligned per column exactly like Table. A right-edge scrollbar is painted only when the flattened list overflows the window.
func (*TreeTable) Focused ¶ added in v0.101.0
func (f *TreeTable) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*TreeTable) NodeAt ¶ added in v0.87.0
func (t *TreeTable) NodeAt(x, y int) *TreeTableNode
OnEvent: a click on the first column's disclosure glyph toggles that node's Expanded (re-clamping ScrollRow, since toggling can shrink or grow the visible row count out from under it); a click anywhere else on NodeAt returns the TreeTableNode at widget-local (x, y) in the current visible-flattened, scrolled body, or nil for the header band or empty space below the last row. It does not mutate ScrollRow (unlike OnEvent). Exposed so a host can hit-test a right-click and build a context menu for that node.
func (*TreeTable) OnEvent ¶ added in v0.42.0
a row selects the node. Y is mapped through ScrollRow back to the flattened index it targets, exactly like TreeView.OnEvent.
func (*TreeTable) Remove ¶ added in v0.87.0
func (t *TreeTable) Remove(n *TreeTableNode) bool
Remove detaches node n from the forest — from a top-level Root slot or from its parent's Children. It returns true when n was found and removed; false for a nil node or one not in the tree. Exposed so a host can implement a "delete node" menu action (TreeTableNode has no parent pointer).
func (*TreeTable) ScrollBy ¶ added in v0.42.0
ScrollBy adjusts ScrollRow by delta, with the same clamping as ScrollTo. Negative delta scrolls up.
func (*TreeTable) ScrollRow ¶ added in v0.42.0
func (t *TreeTable) ScrollRow() *mvvm.Observable[int]
ScrollRow is the index, into the current visible-flattened node list, of the top row Draw paints, as a shared mvvm.Observable. It's clamped on every Draw / OnEvent to [0, max(0, visibleCount-windowRows)], so it's always safe to Set directly; prefer ScrollTo/ScrollBy for arithmetic on it. When the whole tree fits in Bounds().H, ScrollRow==0 paints every row.
func (*TreeTable) ScrollTo ¶ added in v0.42.0
ScrollTo sets ScrollRow to row, clamped against the tree's current flattened shape + the widget's bounds.
func (*TreeTable) Selected ¶ added in v0.42.0
func (t *TreeTable) Selected() *mvvm.Observable[*TreeTableNode]
Selected is the highlighted node as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Selected field. A row click or a keyboard cursor move Sets it (nil for no selection); subscribers are notified on change. The node is painted with Theme.Accent when it is a currently-visible row.
func (*TreeTable) SetFocused ¶ added in v0.101.0
func (f *TreeTable) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
type TreeTableColumn ¶ added in v0.42.0
type TreeTableColumn struct {
Title string
Width int // pixels; 0 = auto (equal share of remaining space)
Align Align
}
TreeTableColumn is one column definition for a TreeTable header — the same shape as TableColumn (title + optional fixed pixel Width + Align), reused here so a host that already knows Table's column model doesn't have to learn a second one.
type TreeTableNode ¶ added in v0.42.0
type TreeTableNode struct {
Cells []string
Children []*TreeTableNode
Expanded bool
}
TreeTableNode is one row of a TreeTable. Cells[0] is rendered in the first (tree) column, indented by the node's depth and prefixed with a disclosure glyph when it has Children; Cells[1:] render as plain, column-aligned text in the remaining columns exactly like a Table row. A node shorter than len(Columns) renders blank trailing cells, mirroring Table.Rows' own "short row" tolerance.
type TreeView ¶
type TreeView struct {
Base
Root *TreeNode
OnActivate func(node *TreeNode)
RowHeight int // default 18
// RowRenderer, when non-nil, draws each row's CONTENT (right of the
// chevron) instead of the default node.Label text. contentRect is the
// row rectangle AFTER the chevron + indent, already inset for the
// scrollbar gutter; the TreeView still paints the selection background,
// the chevron, and owns scroll/hit-test/keyboard. selected reports
// whether the row is the current Selected node (or, in MultiSelect mode,
// a member of the selection set); ink is the resolved text colour
// (theme.OnSurface, or theme.Background when selected). This is the
// rich-row seam: a host can draw an icon/pastille + label + count badge +
// spinner. The zero value (nil) keeps the original one-line Label render,
// byte-identical to before this field existed.
RowRenderer func(p painter.Painter, theme *Theme, contentRect Rect, node *TreeNode, selected bool, ink RGBA)
// MultiSelect enables a multi-node selection set on top of the
// single-node Selected anchor. When false (the default), TreeView
// behaves exactly as before: only Selected is tracked/painted.
MultiSelect bool
// HideRoot omits the Root node's own row and renders its children as the
// top-level rows (at depth 0), turning the single-rooted tree into a forest.
// The Root still owns the children (its Expanded flag is ignored — its
// children are always shown), but it is never itself a visible row, so it is
// not selectable or hit-testable; a host that wants a "select everything" row
// makes it the first child instead of the root. Selection, keyboard and
// hit-testing operate on the visible children exactly as when the root is
// shown. The zero value (false) keeps the original root-visible behaviour.
HideRoot bool
// HideScrollbar suppresses the TreeView's own overflow scrollbar (track +
// thumb) while keeping every other behaviour — the gutter inset, row-based
// scrolling, keyboard and hit-testing — unchanged. It is for a host that draws
// its OWN scrollbar over the tree so every panel in its UI shares one bar
// style; that host reads ScrollExtent to size and position it. The zero value
// (false) draws the built-in scrollbar as before.
HideScrollbar bool
// contains filtered or unexported fields
}
TreeView renders a hierarchical TreeNode set as indented rows. Click on a row's ▶/▼ chevron toggles Expanded; click anywhere else on the row selects it + fires OnActivate with the clicked node.
Rendering is windowed (virtualized): only the rows that fit inside Bounds().H are ever painted, no matter how many nodes are visible in the flattened (expand-aware) order. See ScrollRow.
Use for file browsers, settings hierarchies, JSON inspectors, outline views.
func NewTreeView ¶
NewTreeView builds a TreeView rooted at root (which may be nil for an empty initial view).
func (*TreeView) A11y ¶ added in v0.40.0
A11y reports the TreeView as a tree. Value is the selected node's label in single-select mode, or a "N selected" count while MultiSelect is on.
func (*TreeView) ClearSelection ¶ added in v0.37.0
func (t *TreeView) ClearSelection()
ClearSelection empties the multi-select set. Selected (the anchor) is left untouched.
func (*TreeView) Draw ¶
Draw paints the rows in the current scroll window: flattened nodes [ScrollRow, ScrollRow+windowRows). When the whole tree fits inside Bounds().H, that window covers every row + ScrollRow clamps to 0, so painting is byte-identical to an unvirtualized TreeView. When it doesn't fit, a right-edge scrollbar track+thumb is painted too.
func (*TreeView) Focused ¶ added in v0.101.0
func (f *TreeView) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*TreeView) IsSelected ¶ added in v0.37.0
IsSelected reports whether n is part of the multi-select set. It only reflects MultiSelect state; when MultiSelect is false it always returns false (single-select uses Selected directly).
func (*TreeView) NodeAt ¶ added in v0.85.0
NodeAt returns the TreeNode at widget-local (x, y) in the current visible-flattened, scrolled layout, or nil for empty space below the last row. It does not mutate ScrollRow (unlike OnEvent). Exposed so a host can hit-test a right-click and build a context menu for that node.
func (*TreeView) OnEvent ¶
OnEvent: a click on the chevron toggles Expanded; a click anywhere else on the row selects the node + fires OnActivate. Y is mapped through ScrollRow back to the flattened index it targets.
func (*TreeView) Remove ¶ added in v0.85.0
Remove detaches node n from the tree, removing it from its parent's Children. It returns true when n was found and removed; false for a nil node, an empty tree, or an attempt to remove the Root (which has no parent). Exposed so a host can implement a "delete node" menu action without threading parent pointers (TreeNode has none).
func (*TreeView) RowContentWidth ¶ added in v0.160.0
RowContentWidth returns the pixel width RowRenderer's contentRect gets for a row at the given depth: the widget width, minus the scrollbar gutter when the tree currently overflows its window, minus the chevron column and this depth's indent. It uses the same windowing decision Draw does, so a host can lay out (measure/elide) rich content before painting. Never negative (clamped to 0).
func (*TreeView) ScrollBy ¶ added in v0.37.0
ScrollBy adjusts ScrollRow by delta, with the same clamping as ScrollTo. Negative delta scrolls up.
func (*TreeView) ScrollExtent ¶ added in v0.172.0
ScrollExtent reports the tree's vertical scroll state in ROW units: the clamped index of the first visible row, how many whole rows fit the window, and the total visible (expand-aware) row count. shown is false when the tree fits its window and no scrollbar is warranted. A host that suppresses the built-in bar (HideScrollbar) and draws its own reads this to size and position a matching one.
func (*TreeView) ScrollRow ¶ added in v0.37.0
func (t *TreeView) ScrollRow() *mvvm.Observable[int]
ScrollRow is the reactive index, into the current visible-flattened node list, of the top row Draw paints, as a shared mvvm.Observable. It's clamped on every Draw / OnEvent to [0, max(0, visibleCount-windowRows)]; prefer ScrollTo/ScrollBy for arithmetic on it. When the whole tree fits in Bounds().H it is 0.
func (*TreeView) ScrollTo ¶ added in v0.37.0
ScrollTo sets ScrollRow to row, clamped against the tree's current flattened shape + the widget's bounds.
func (*TreeView) SelectRange ¶ added in v0.37.0
SelectRange selects every node between a + b (inclusive) over the currently-visible flattened node order (collapsed subtrees are excluded, matching what the user can actually see). If either node isn't currently visible, SelectRange is a no-op.
func (*TreeView) Selected ¶
func (t *TreeView) Selected() *mvvm.Observable[*TreeNode]
Selected is the current single (anchor) selection as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Selected field. A click, an arrow-key move, or SetSelection Sets it (notifying subscribers); the zero value is nil.
func (*TreeView) SelectedNodes ¶ added in v0.37.0
SelectedNodes returns the multi-selected nodes in visible (pre-order, expanded-aware) traversal order. Empty when MultiSelect is false or nothing is selected.
func (*TreeView) SetFocused ¶ added in v0.101.0
func (f *TreeView) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
func (*TreeView) SetSelection ¶ added in v0.37.0
SetSelection replaces the multi-select set with nodes. The last node (if any) becomes the anchor (Selected).
func (*TreeView) ToggleSelect ¶ added in v0.37.0
ToggleSelect flips n's membership in the multi-select set.
type Tween ¶ added in v0.35.0
type Tween struct {
// From is the starting value.
From float64
// To is the ending value.
To float64
// Duration is the total number of ticks the tween takes to complete.
// A Duration <= 0 makes the tween immediately Done, at To.
Duration int
// Ease shapes the progress curve. A nil Ease behaves as Linear.
Ease Easing
// contains filtered or unexported fields
}
Tween animates a scalar value from From to To over Duration ticks, using Ease to shape the progress curve. Advance it once per frame/tick via Tick, or read the current value without advancing via Value.
func NewTween ¶ added in v0.35.0
NewTween creates a Tween animating from from to to over duration ticks using ease. A nil ease defaults to Linear. A duration <= 0 produces a Tween that is immediately Done, reporting to as its Value.
func (*Tween) Reset ¶ added in v0.35.0
func (tw *Tween) Reset()
Reset restarts the tween from its beginning (elapsed = 0).
type VAlign ¶ added in v0.69.0
type VAlign int
VAlign is a widget's vertical text alignment within its bounds height. The zero value VAuto preserves the label's original layout (centred when the bounds are taller than the text, else top-anchored), so existing labels are unchanged; VTop/VMiddle/VBottom force a specific edge.
const ( // VAuto keeps the original behaviour: vertically centred when Bounds.H // exceeds the glyph height, otherwise top-anchored. The default. VAuto VAlign = iota // VTop anchors text to the top edge. VTop // VMiddle centres text vertically within the bounds height. VMiddle // VBottom anchors text to the bottom edge. VBottom )
type VBox ¶
type VBox struct {
Base
// Spacing is the gap in pixels between adjacent children; same semantics as
// HBox.Spacing (NewVBox seeds DefaultBoxSpacing, honoured literally, negatives
// clamped to 0).
Spacing int
// Align positions each child on the cross (horizontal) axis; the zero value
// BoxStretch fills the width. Pack distributes leftover height when the
// children do not fill the box (no flex child). Same semantics as HBox.
Align BoxAlign
Pack BoxPack
// contains filtered or unexported fields
}
VBox is the vertical analogue of HBox: children stack top-to-bottom, each a flex share of the height or a fixed height, filling the box's width.
func NewVBox ¶
func NewVBox() *VBox
NewVBox constructs an empty VBox with Spacing seeded to DefaultBoxSpacing.
func (*VBox) AddFixed ¶ added in v0.50.0
AddFixed adds w with a fixed height in pixels (clamped to ≥0).
func (*VBox) AddFlex ¶ added in v0.50.0
AddFlex adds w with an explicit flex weight (clamped to ≥1).
func (*VBox) Children ¶ added in v0.123.0
Children yields the box's child widgets in insertion order, so generic tree walkers (e.g. CollectRuns) can descend without knowing the box type.
func (*VBox) OnEvent ¶
OnEvent forwards to the first child containing the event point. EventMouseMove is forwarded to every child instead, so hover-enter and hover-leave both propagate (see HBox.OnEvent). Keyboard events go through the focus system and a click also moves focus to the focusable it hits (see HBox.OnEvent).
type VelocityTracker ¶ added in v0.185.0
type VelocityTracker struct {
// Window is how many recent per-sample velocities are averaged (<= 0 means
// the default of 4). A larger window is smoother but lags a fast flick.
Window int
// contains filtered or unexported fields
}
VelocityTracker turns a stream of drag POSITION samples into a smoothed release velocity, so a consumer that only knows where the finger is each frame (not how fast it is moving) can still hand Momentum.Fling / Momentum.EndDrag a sensible flick speed.
Like everything else here it is clock-free: each Sample carries its own dt. It averages the most recent samples (a short window) so a single jittery final frame — common right at lift-off — cannot dominate the launch velocity the way a raw last-delta would. The zero value is an empty tracker ready to use; Window defaults to 4 samples when left 0.
func (*VelocityTracker) Reset ¶ added in v0.185.0
func (v *VelocityTracker) Reset()
Reset clears the tracked samples, so the next Sample starts a fresh drag. A consumer calls it on touch-down.
func (*VelocityTracker) Sample ¶ added in v0.185.0
func (v *VelocityTracker) Sample(delta, dt float64) float64
Sample records that the finger moved by delta (offset units) over dt seconds and returns the current smoothed velocity. A non-positive dt is ignored (it carries no velocity information) and returns the unchanged estimate.
func (*VelocityTracker) Velocity ¶ added in v0.185.0
func (v *VelocityTracker) Velocity() float64
Velocity returns the mean of the windowed per-sample velocities, or 0 when no sample has been recorded yet. This is the value to hand Fling/EndDrag on release.
type ViewController ¶ added in v0.62.0
type ViewController struct {
// contains filtered or unexported fields
}
ViewController builds a declarative Node tree once and then lets logic reach the widgets that matter by name, instead of threading pointers through the construction code. Tag nodes with Node.Ref("name"); the controller collects those widgets while building and exposes them via Lookup / the typed LookupAs. Event handlers are wired the Go-idiomatic way — look a widget up and assign its callback — rather than by resolving handler-name strings, so the compiler checks every wire.
vc := NewViewController(VBoxNode(
Leaf(list).Ref("list").Flexed(1),
Leaf(saveBtn).Ref("save").Sized(32),
))
if b, ok := LookupAs[*Button](vc, "save"); ok {
b.OnClick = onSave
}
vc.Root().SetBounds(screen)
func NewViewController ¶ added in v0.62.0
func NewViewController(root Node) *ViewController
NewViewController builds root and collects every Ref-tagged widget in the tree.
func (*ViewController) Lookup ¶ added in v0.62.0
func (vc *ViewController) Lookup(name string) Widget
Lookup returns the widget tagged with Ref(name), or nil if there is none.
func (*ViewController) RefAt ¶ added in v0.65.0
func (vc *ViewController) RefAt(px, py int) (name string, ok bool)
RefAt returns the name of the Ref-tagged widget whose Bounds contains the surface point (px,py) — hit-testing by reference. Refs are scanned in reverse build order, so a later (typically deeper or on-top) ref shadows an earlier one when they overlap. ok is false when no ref-tagged widget covers the point.
func (*ViewController) Root ¶ added in v0.62.0
func (vc *ViewController) Root() Widget
Root is the built root widget — call SetBounds/Draw/OnEvent on it.
type ViewSwitcher ¶ added in v0.8.0
type ViewSwitcher struct {
Base
// Views is the segment titles (config). The reactive selection is MVVM-only:
// the active index lives in an unexported Observable exposed via
// [ViewSwitcher.Current].
Views []string
// contains filtered or unexported fields
}
ViewSwitcher is a libadwaita/GTK-style horizontal segmented tab picker: an evenly-divided strip of same-width segments where exactly one is highlighted as the active view. Clicking a segment swaps the active view, notifying the Current Observable's subscribers.
The strip's background is Theme.SurfaceAlt; the active segment paints in Theme.Accent with the accent-inverted ink (theme.Extra["OnAccent"] with a Theme.Background fallback, matching what Button, ListBox, TreeView, and Table already do). A 1-pixel Theme.Border line sits along the strip's bottom edge so the switcher reads as a discrete band above the switched content.
A ViewSwitcher with no Views paints only the background + bottom border; clicks are ignored. This lets a caller assemble the widget before it knows which views the app will surface without tripping a nil-Views guard downstream.
func NewViewSwitcher ¶ added in v0.8.0
func NewViewSwitcher(views []string, current int) *ViewSwitcher
NewViewSwitcher constructs a ViewSwitcher over views with the initial highlighted segment at current. current is clamped into the [0, len(views)-1] range, or forced to 0 when views is empty, so the widget is never in a hard-to-reason "index out of range" state.
func (*ViewSwitcher) A11y ¶ added in v0.40.0
func (v *ViewSwitcher) A11y() A11yInfo
A11y reports the ViewSwitcher as a tablist named by its current view.
func (*ViewSwitcher) Current ¶ added in v0.8.0
func (v *ViewSwitcher) Current() *mvvm.Observable[int]
Current is the active segment index as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Current field. A click or a key move Sets it; subscribers are notified.
func (*ViewSwitcher) Draw ¶ added in v0.8.0
func (v *ViewSwitcher) Draw(p painter.Painter, theme *Theme)
Draw paints the strip background, then each segment with the active one highlighted in Theme.Accent, then the 1-pixel bottom border. Segments share the same width via integer division of Bounds.W by len(Views); any left-over pixel column on the right remains SurfaceAlt (this matches how HeaderBar's title strip tolerates non-integer central strips).
func (*ViewSwitcher) Focused ¶ added in v0.101.0
func (f *ViewSwitcher) Focused() bool
Focused reports whether this widget currently holds keyboard focus.
func (*ViewSwitcher) OnEvent ¶ added in v0.8.0
func (v *ViewSwitcher) OnEvent(ev Event)
OnEvent handles a click by locating which segment the X coordinate lands on and Setting the Current Observable. Non-click events, clicks with an empty Views slice, clicks on a zero-width strip and clicks that fall outside every segment are all no-ops.
func (*ViewSwitcher) SetFocused ¶ added in v0.101.0
func (f *ViewSwitcher) SetFocused(focused bool)
SetFocused records whether this widget holds keyboard focus.
type Viewport ¶ added in v0.148.0
type Viewport struct {
Base
// contains filtered or unexported fields
}
Viewport is the application root: a single widget that fills the whole surface a window hands it and parcels that surface out to five slots — a top bar, a bottom bar, a left bar, a right bar, and a centre that fills the rest. It is what a native window or a wasmbox client sets as its root and resizes to the drawable area, so the shell re-fills to the window on every resize.
The edges are carved in a FIXED precedence — top, then bottom, then left, then right — so the top and bottom bars span the full width and the side bars only take the height that remains between them (the five-region shell shape). This makes the layout independent of the order regions are assigned (unlike Dock, which carves in insertion order); the centre always fills whatever is left, even when it holds no widget.
Viewport is a Widget: SetBounds re-parcels every slot, Draw paints the centre then the edges, and OnEvent routes to the slot under the pointer, translating into that slot's local space. Any slot may be nil (that region simply contributes no bar and its space folds into the centre).
func NewViewport ¶ added in v0.148.0
func NewViewport() *Viewport
NewViewport builds an empty Viewport. Assign slots with Set before (or after) the first SetBounds; an unset Viewport lays out a single full-surface centre.
func (*Viewport) A11y ¶ added in v0.148.0
A11y marks the Viewport as a presentational shell: it holds no content of its own, so a screen reader announces its docked panels and its centre directly rather than the container. CollectA11y skips a RolePresentation node but still descends into its Children — the same treatment as Container, Border and Dock.
func (*Viewport) Children ¶ added in v0.148.0
Children yields the present slots in reading order: the edges clockwise from the top, then the centre — the order a screen reader announces a shell in, so a generic tree walk reaches every docked panel and the content area.
func (*Viewport) Draw ¶ added in v0.148.0
Draw paints the centre first, then the edge bars over it (they never overlap, so the order only decides which wins a shared seam).
func (*Viewport) OnEvent ¶ added in v0.148.0
OnEvent forwards to the first slot whose Bounds contains the surface point, translated into that slot's local space. The edges are tested before the centre so a bar wins any seam it shares with the content area.
func (*Viewport) RegionRect ¶ added in v0.148.0
func (v *Viewport) RegionRect(region ViewportRegion) Rect
RegionRect reports the surface-space rectangle currently allotted to a region (the zero Rect for an out-of-range region, or for an edge with no widget). The centre's rectangle is always the remainder, whether or not it holds a widget.
func (*Viewport) Set ¶ added in v0.148.0
func (v *Viewport) Set(region ViewportRegion, w Widget, size int)
Set places w in the given region with size pixels along that edge's axis (height for top/bottom, width for left/right; ignored for ViewportCenter, which always fills). A negative size clamps to 0; a nil w clears the slot. An out-of-range region is ignored. Re-lays out immediately so RegionRect is current.
func (*Viewport) SetBounds ¶ added in v0.148.0
SetBounds fills the surface: it carves each present edge off the available rectangle in the fixed precedence order, then gives the centre whatever remains. Every slot's rectangle is cached for RegionRect, and an absent edge gets a zero rect so a stale bar cannot linger.
type ViewportRegion ¶ added in v0.148.0
type ViewportRegion int
ViewportRegion names one of the five slots a Viewport fills: the four dockable edges plus the centre that takes whatever is left.
const ( // ViewportCenter is the flexible middle slot. It always fills the space the // edges leave behind, so it has no size of its own. ViewportCenter ViewportRegion = iota // ViewportTop docks a bar across the full width at the top, given height. ViewportTop // ViewportBottom docks a bar across the full width at the bottom, given height. ViewportBottom // ViewportLeft docks a bar down the left, given width, between the top and // bottom bars. ViewportLeft // ViewportRight docks a bar down the right, given width, between the top and // bottom bars. ViewportRight )
type Wallpaper ¶ added in v0.80.0
type Wallpaper struct {
Base
// Pixels is the RGBA image (IW*IH*4 bytes); nil/empty paints only the
// fallback.
Pixels []byte
IW, IH int
// Mode selects the image placement (default WallpaperFill / cover).
Mode WallpaperMode
// Top / Bottom are the fallback gradient stops (top → bottom). Zero Top
// (A==0) => Theme.Background; zero Bottom (A==0) => solid Top (no gradient).
Top, Bottom RGBA
// Interactive makes the Wallpaper catch pointer events; the zero value is
// event-transparent (clicks pass through to the content above it).
Interactive bool
}
Wallpaper is a full-bounds desktop backdrop: it paints an optional RGBA image scaled by Mode (fill / fit / center / tile) over a solid or vertical-gradient fallback. It complements Backdrop (a flat fill + optional grid) for the case a compositor actually wants a picture behind the scene.
Fallback: the ground under (and around) the image is a vertical gradient from Top to Bottom. A zero (A==0) Top falls back to Theme.Background; a zero Bottom makes the fill a solid Top (no gradient). So a bare Wallpaper with no colours set reads as the theme background, a single opaque Top is a solid colour, and Top+Bottom is a gradient — all without an image.
Like the corrected Backdrop, a Wallpaper is event-transparent by default: its HitTest returns false so clicks pass THROUGH to the widgets composited over it (a full-cover backdrop that reported hits would swallow every click). Set Interactive to opt back in (a picker preview that should catch clicks).
func NewWallpaper ¶ added in v0.80.0
func NewWallpaper(pixels []byte, w, h int, mode WallpaperMode) *Wallpaper
NewWallpaper builds an image Wallpaper (length must equal w*h*4) in the given mode. The fallback colours are left zero so uncovered margins read as the theme background.
func NewWallpaperGradient ¶ added in v0.80.0
NewWallpaperGradient builds an image-less Wallpaper that paints a vertical gradient from top to bottom (pass an equal pair for a solid colour).
func (*Wallpaper) A11y ¶ added in v0.130.0
A11y reports the Wallpaper as presentational — decoration by definition.
func (*Wallpaper) Draw ¶ added in v0.80.0
Draw paints the fallback ground then, if a valid image is present, the image placed per Mode. An empty rectangle paints nothing.
func (*Wallpaper) HitTest ¶ added in v0.80.0
HitTest returns false unless Interactive is set, so by default a full-cover wallpaper lets clicks pass through to the widgets composited over it (the Backdrop / Label pass-through idiom). When Interactive is set it hit-tests against its Bounds like any other widget.
type WallpaperMode ¶ added in v0.80.0
type WallpaperMode int
WallpaperMode selects how a Wallpaper's source image maps onto its bounds.
const ( // WallpaperFill ("cover") scales the image — preserving aspect — to the // smallest size that covers the whole bounds, centring it and cropping the // overflow. It is the zero value: the desktop-wallpaper default where the // picture fills the screen edge-to-edge. WallpaperFill WallpaperMode = iota // WallpaperFit ("contain") scales the image — preserving aspect — to the // largest size that fits entirely within the bounds and centres it; the // margin around it shows the fallback fill. WallpaperFit // WallpaperCenter paints the image 1:1 (no scaling) centred in the bounds; // an image smaller than the bounds shows the fallback around it, a larger // one is cropped to the bounds. WallpaperCenter // WallpaperTile repeats the image 1:1 from the top-left to cover the whole // bounds — the classic pattern backdrop. WallpaperTile )
type WheelPicker ¶ added in v0.187.0
type WheelPicker struct {
Base
// VisibleRows is how many rows the wheel shows at once, including the centred
// selection row. It is coerced to an odd number >= 1 by NewWheelPicker (and by
// the accessor) so there is always a single, unambiguous centre row. The
// default is wheelVisibleRows.
VisibleRows int
// OnChange fires whenever any column's selected index changes — while a spin
// crosses a row, when a snap settles, on a keyboard step, a wheel notch or a
// tap. It reports the zero-based column and the new selected row index. A nil
// OnChange is safe (the change is applied silently).
OnChange func(col, index int)
// contains filtered or unexported fields
}
WheelPicker is the iOS-style spinning wheel of discrete values: one or more COLUMNS, each a vertical strip of string values under a fixed, centred selection band. A flick spins a column with inertial deceleration and, once the coast dies, the strip SNAPS so a row sits exactly under the band — it can never rest half-way between two values. It generalises the specialised DatePicker / TimePicker steppers: a date wheel is three columns (day/month/year), a time wheel is two (hour/minute), and any enumerated field is one column of its labels.
Reusing the momentum engine for BOTH the spin and the snap ¶
Each column owns a single-axis Momentum engine — the same deterministic, clock-free fling+rubber-band engine MomentumScroller drives — so the spin physics are not reinvented here: a release hands the engine a velocity, the engine coasts under exponential deceleration, and a flick past the first/last row stretches against the rubber band and springs back, exactly as it does for a scroll view.
The SNAP-to-nearest-row reuses that same engine's spring rather than adding a second physics path. Momentum already knows how to spring an offset that sits PAST a bound back ONTO that bound and rest there exactly (see [Momentum.tickSpring]); a detent is just a bound. So when a coast comes to rest between two rows, the column momentarily sets the engine's clamp window to the degenerate span [D, D] at the nearest detent D and re-flings from rest: the current offset is now "past" that bound, the engine springs it onto D, and the instant it arrives it snaps to the exact detent and stops — the very same spring that lands a scroll view precisely on its edge, retargeted at a row boundary. When the snap finishes the real bounds are restored.
Row units, not pixels ¶
A column's offset is measured in ROWS, not pixels: offset 0 shows item 0 under the band, offset 1 shows item 1, and the selected index is simply the offset rounded to the nearest whole row. Keeping the physics in row units makes the widget independent of Density and MetricScale — the pixel row height only enters at Draw time and when converting a finger's pixel travel into rows — so a density flip never desynchronises the value under the band, and every detent is an exact integer the tests can assert to the bit.
Determinism ¶
Like the engine it composes, the widget reads no clock. The inertial path is driven by explicit calls — WheelPicker.TouchDown, WheelPicker.TouchMove (which carries its own dt), WheelPicker.TouchUp and a per-frame WheelPicker.Tick — mirroring MomentumScroller, so a host supplies the elapsed time and the same inputs always produce the same offsets. The discrete path — keyboard arrows, a wheel notch, a tap above/below the band — runs through WheelPicker.OnEvent and needs no clock at all.
func NewWheelPicker ¶ added in v0.187.0
func NewWheelPicker(columns ...[]string) *WheelPicker
NewWheelPicker builds a wheel with one column per string slice, each column initialised to select its first row (index 0). VisibleRows defaults to wheelVisibleRows. A caller may pass no columns (an empty wheel that draws its frame and ignores input) or a column with no items (a blank strip); neither is an error, so a data-driven caller never has to guard the degenerate cases.
func (*WheelPicker) A11y ¶ added in v0.187.0
func (w *WheelPicker) A11y() A11yInfo
A11y reports the wheel as a group whose Value is the selected value of every column joined by a space (e.g. "09 30" for a time wheel), so a screen reader announces the whole current selection. It mirrors how TimePicker reports its composite value as a group.
func (*WheelPicker) Draw ¶ added in v0.187.0
func (w *WheelPicker) Draw(p painter.Painter, theme *Theme)
Draw paints the wheel: a framed surface, then each column's strip of values scrolled to its offset and clipped to the column, with the row nearest the band inked in full OnSurface and the rest muted so the selection reads at a glance, and finally the centred selection band drawn as two Accent rules across the whole width. A disabled wheel paints a muted face.
func (*WheelPicker) Focus ¶ added in v0.187.0
func (w *WheelPicker) Focus() int
Focus is the column the keyboard currently acts on.
func (*WheelPicker) NumColumns ¶ added in v0.187.0
func (w *WheelPicker) NumColumns() int
NumColumns is the number of columns in the wheel.
func (*WheelPicker) OnEvent ¶ added in v0.187.0
func (w *WheelPicker) OnEvent(ev Event)
OnEvent handles the pointer/keyboard interactions that need no elapsed time: a mouse wheel notch (EventScroll) steps the column under the pointer; a tap (EventClick) above or below the band steps the tapped column toward the tapped row; and keyboard arrows move the selection or the focus. The inertial finger path is NOT here — it needs per-sample dt — and lives in TouchDown/TouchMove/ TouchUp/Tick instead. A disabled wheel ignores everything.
func (*WheelPicker) SelectedIndex ¶ added in v0.187.0
func (w *WheelPicker) SelectedIndex(col int) int
SelectedIndex is the selected row index of a column, or -1 for an out-of-range column so a caller can tell "no such column" from "row 0".
func (*WheelPicker) SelectedValue ¶ added in v0.187.0
func (w *WheelPicker) SelectedValue(col int) string
SelectedValue is the selected value string of a column, or "" for an out-of-range or empty column.
func (*WheelPicker) SetFocus ¶ added in v0.187.0
func (w *WheelPicker) SetFocus(col int)
SetFocus points the keyboard at a column, ignoring an out-of-range request so the focus never lands on a non-existent column.
func (*WheelPicker) SetIndex ¶ added in v0.187.0
func (w *WheelPicker) SetIndex(col, idx int)
SetIndex jumps a column to select idx, WITHOUT any fling or snap animation: the offset is set straight onto the detent and all motion halts, so a programmatic or keyboard selection is instantaneous. idx is clamped into range; OnChange fires if the index actually changed. An out-of-range or empty column is a no-op.
func (*WheelPicker) Settling ¶ added in v0.187.0
func (w *WheelPicker) Settling() bool
Settling reports whether any column still owes motion — a live coast or spring (Momentum.Settling), OR a column resting BETWEEN two rows that has yet to snap onto one. The second case matters because a release with a tiny velocity comes to rest immediately inside the momentum engine, so without it a host would stop ticking and leave the strip stranded mid-row; reporting the pending snap keeps the host calling Tick until every column sits exactly on a detent. A column under an active finger drag is excluded — the drag, not a snap, owns it — so a stray Tick during a drag never yanks the strip onto a row under the finger.
func (*WheelPicker) Step ¶ added in v0.187.0
func (w *WheelPicker) Step(delta int)
Step nudges the focused column's selection by delta rows (typically +1 / -1), clamped at the ends — the keyboard / wheel primitive. It routes through SetIndex, so it too is instantaneous and fires OnChange on a real change.
func (*WheelPicker) Tick ¶ added in v0.187.0
func (w *WheelPicker) Tick(dt float64) bool
Tick advances every column by dt seconds — driving both a live coast and the spring-to-detent snap that follows it — firing OnChange for any column whose selected index changed, and reports whether ANY column still owes motion so a host knows to schedule another frame. A non-positive dt or an all-rest wheel is effectively a no-op.
func (*WheelPicker) TouchDown ¶ added in v0.187.0
func (w *WheelPicker) TouchDown(ev Event)
TouchDown starts a finger drag on the column under ev (widget-local coords), stopping any coast or snap on that column first so the strip tracks the finger from where it actually sits. It arms the gesture; a following TouchMove pans it. A press outside every column is ignored.
func (*WheelPicker) TouchMove ¶ added in v0.187.0
func (w *WheelPicker) TouchMove(ev Event, dt float64)
TouchMove pans the active column by the finger's vertical travel since the previous sample, taken dt seconds ago. The strip follows the finger — dragging DOWN (increasing y) reveals earlier rows, a decreasing offset — so the row delta is the pixel travel negated and divided by the row height. It smooths the velocity for the eventual fling and fires OnChange as the drag crosses rows. A move with no armed drag, or a zero row height, is a no-op.
func (*WheelPicker) TouchUp ¶ added in v0.187.0
func (w *WheelPicker) TouchUp()
TouchUp releases the drag, flinging the active column at the velocity the tracker smoothed from the recent samples (a release while stretched past the first/last row springs home regardless of the flick). Harmless with no drag in progress. The coast + snap then play out under Tick.
type Widget ¶
type Widget interface {
// Bounds returns the widget's placement within its parent surface.
// Used by containers for hit-testing + relative-coordinate translation.
Bounds() Rect
// SetBounds updates the placement. Containers call this during
// layout to position children.
SetBounds(r Rect)
// Draw paints the widget onto the Painter using the supplied
// theme. The Painter's back-end decides whether the primitives
// land as pixels (browser canvas, native window, image file) or
// cells (terminal grid). Widgets MUST NOT draw outside their
// Bounds() rectangle.
Draw(p painter.Painter, theme *Theme)
// HitTest reports whether (px, py) (in surface coordinates) falls
// on a sensitive part of the widget. Most widgets just return
// Bounds().Contains(px, py); transparent or overlapping widgets
// may return false even within their bounds.
HitTest(px, py int) bool
// OnEvent delivers an input event whose X/Y are WIDGET-LOCAL.
// The widget mutates its internal state + may schedule a redraw
// (the caller is responsible for invoking Draw again).
OnEvent(ev Event)
}
Widget is the toolkit's single core abstraction. Every widget -- Button, Label, TextInput, HBox, ScrollView, ... -- implements it. Containers themselves are widgets too: a VBox passes Draw / OnEvent to its children after offsetting coordinates by the child's Rect.
type Window ¶ added in v0.70.0
type Window struct {
Base
Title string
Body Widget // optional; nil leaves the body area empty
// Tool flags enable the matching title-bar buttons; each fires its callback
// (when non-nil) on a click. A disabled tool is neither drawn nor hit-tested.
Closable, Minimizable, Maximizable bool
OnClose, OnMinimize, OnMaximize func()
// Resizable draws the bottom-right resize grip and makes HitRegion report
// WindowResize over it; the app drives the actual resize via ResizeTo.
Resizable bool
// contains filtered or unexported fields
}
Window is a draggable, resizable floating panel: a title-bar band carrying a left-aligned Title and a right-aligned cluster of window tools (close, minimize, maximize), above a body area that hosts an optional Body widget. Unlike Dialog — a fixed, centred, non-movable modal — a Window is meant to be moved and resized around the surface.
Following the toolkit's app-driven drag model (see Paned / Border), Window draws its chrome and exposes hit-testing plus explicit move/resize methods, but it does NOT run its own mouse-tracking loop. The host app owns pointer tracking: on mouse-down it calls HitRegion to learn whether the press landed on the title bar (start a move) or the resize grip (start a resize), then feeds the deltas to MoveBy / ResizeTo on each drag tick. Single clicks on the tools and forwarding into Body are handled by OnEvent as usual.
func NewWindow ¶ added in v0.70.0
NewWindow builds a Window with the given title and (optional) body, seeded with the default minimum size. Enable the tools / Resizable and wire the callbacks on the returned value.
func (*Window) Draw ¶ added in v0.70.0
Draw paints the title bar (band + title + enabled tools), the body area (fill + border + optional Body) and, when Resizable, the resize grip.
func (*Window) HitRegion ¶ added in v0.70.0
func (w *Window) HitRegion(px, py int) WindowRegion
HitRegion reports which part of the window the surface point (px, py) lands on. Tools win over the bare title bar; the grip (when Resizable) wins over the body it overlaps.
func (*Window) MoveBy ¶ added in v0.70.0
MoveBy shifts the window by (dx, dy) and re-lays out its body — the app calls this on each drag tick after a WindowTitleBar press.
func (*Window) OnEvent ¶ added in v0.70.0
OnEvent handles single clicks: a click on an enabled tool fires its callback; a click in the body forwards to Body in body-local coordinates. Title-bar and resize-grip DRAGS are not handled here — the app drives those via MoveBy / ResizeTo — so a press on either falls through silently.
type WindowDecoration ¶ added in v0.72.0
type WindowDecoration struct {
Base
// Title is the title-bar caption; TitleInk is its colour. Titlebar is the
// band rect (frame-local); TitleColor fills it. TitleCenter centres the
// caption horizontally (macOS style) instead of left-aligning it (the
// default). Hairline, when non-zero, paints a 1-unit line along the band's
// bottom edge (the macOS titlebar separator).
Title string
TitleInk RGBA
TitleColor RGBA
Titlebar Rect
TitleCenter bool
Hairline RGBA
// Border is the full frame extent (frame-local); BorderColor strokes its
// 1-unit outline (zero = no border). Shadow, when non-zero, paints a 1-unit
// faux drop shadow one unit past the border's right and bottom edges.
Border Rect
BorderColor RGBA
Shadow RGBA
// Grip is the bottom-right resize handle rect; GripColor draws its two
// diagonal rules. ShowGrip gates the whole grip (a shaded/undecorated window
// has none).
Grip Rect
ShowGrip bool
GripColor RGBA
// Buttons is the ordered title-bar button cluster (close/minimize/maximize).
// Each carries its own rect + colours, so the host mixes box buttons and
// traffic-lights freely.
Buttons []DecoButton
}
WindowDecoration paints a window's frame chrome — a title-bar band (fill, title text, optional bottom hairline), a cluster of title-bar buttons (rectangular close/minimize/maximize boxes OR round "traffic-light" dots), a frame border, an optional faux drop shadow and an optional bottom-right resize grip — with EXPLICIT colours and EXPLICIT frame-local geometry.
Unlike Window (a self-contained draggable panel that reads the theme and owns its own hit-testing + body), WindowDecoration is a pure painter for a host compositor that already owns the window model: the host passes the exact rects it hit-tests against (title-bar, each button, border, grip — all in the decoration's own coordinate space) plus the exact palette its style demands, and the widget just paints them. This keeps geometry a SINGLE source of truth on the host side (the same rects drive both hit-testing and paint) while the pixels are produced by the toolkit painter — so a compositor can drop its hand-rolled canvas draws and blit a rendered buffer instead, and every style (a red Openbox bar, a macOS traffic-light bar, any themed palette) keeps its own colours rather than collapsing to a shared theme.
The BODY region between the title bar and the bottom border is never touched: a decoration rendered into a zeroed RGBA buffer leaves the body fully transparent (A=0), so a host composites the decoration OVER the live window body (source-over) and the body shows through the hole.
Every colour follows the toolkit convention that a zero-value RGBA (A=0) means "absent": a zero Hairline / Border / Shadow paints nothing, and a zero button Outline draws no outline. TitleColor / TitleInk / GripColor are painted as given (a host that wants them absent gives the enclosing rect a zero size).
func NewWindowDecoration ¶ added in v0.72.0
func NewWindowDecoration() *WindowDecoration
NewWindowDecoration builds an empty decoration; set the exported fields (or use the ruby-widgets Decoration binding) before rendering.
func (*WindowDecoration) A11y ¶ added in v0.130.0
func (d *WindowDecoration) A11y() A11yInfo
A11y reports the WindowDecoration as a banner named by its title: it is the titlebar region, not the window itself.
func (*WindowDecoration) AddButton ¶ added in v0.72.0
func (d *WindowDecoration) AddButton(b DecoButton) *WindowDecoration
AddButton appends a button to the cluster and returns the decoration for fluent construction.
func (*WindowDecoration) Draw ¶ added in v0.72.0
func (d *WindowDecoration) Draw(p painter.Painter, theme *Theme)
Draw paints the decoration: title-bar band + hairline + caption, the button cluster, then (over the body area, which stays untouched) the resize grip, the faux shadow and the border last so it sits on top of the body edges.
type WindowRegion ¶ added in v0.70.0
type WindowRegion int
WindowRegion names the part of a Window a surface point falls on, as reported by HitRegion. The app uses it on mouse-down to decide whether to begin a move (WindowTitleBar), begin a resize (WindowResize), or leave the press to OnEvent (the tools / body).
const ( // WindowNone is returned for a point outside the window entirely. WindowNone WindowRegion = iota // WindowTitleBar is the title band excluding the tool buttons — the app // starts a move drag here. WindowTitleBar // WindowClose / WindowMinimize / WindowMaximize are the tool buttons. WindowClose WindowMinimize WindowMaximize // WindowResize is the bottom-right grip (only when Resizable) — the app // starts a resize drag here. WindowResize // WindowBody is the content area below the title bar. WindowBody )
type WindowsDockStyle ¶ added in v0.179.0
type WindowsDockStyle struct{}
WindowsDockStyle is the taskbar look: a flat SurfaceAlt bar and flat, rectangular buttons — transparent at rest, a subtle highlight when running, an accent-tinted highlight when active — each running/active button carrying a centred accent underline (short for a background app, wider for the current one), the Windows 10/11 running marker.
func (WindowsDockStyle) DrawFace ¶ added in v0.179.0
func (WindowsDockStyle) DrawFace(p painter.Painter, theme *Theme, r Rect, st DockItemState) RGBA
func (WindowsDockStyle) DrawGround ¶ added in v0.179.0
func (WindowsDockStyle) DrawGround(p painter.Painter, theme *Theme, r Rect)
type Wizard ¶ added in v0.35.0
type Wizard struct {
Base
// Steps is the ordered step list (config). The reactive step index is
// MVVM-only: the current step lives in an unexported Observable exposed via
// [Wizard.Current].
Steps []WizardStep
OnFinish func()
// PressFeedback shows the pressed face on the Back / Next button while it is
// held (EventClick → EventMouseUp). NewWizard enables it; set false to opt
// out.
PressFeedback bool
// contains filtered or unexported fields
}
Wizard is a multi-step "Assistant" flow: a Steps strip across the top tracks progress through Steps, the current step's Body fills the middle, and a Back / Next-or-Finish button row sits at the bottom. Next is disabled (a no-op) whenever the current step's CanAdvance reports false; Back is disabled on the first step. Advancing past the last step swaps the Next label to "Finish" and invokes OnFinish instead of moving further.
func NewWizard ¶ added in v0.35.0
func NewWizard(steps []WizardStep) *Wizard
NewWizard constructs a Wizard over the given steps, starting on the first one (Current == 0).
func (*Wizard) A11y ¶ added in v0.40.0
A11y reports the Wizard as a group carrying its current step's title.
func (*Wizard) Back ¶ added in v0.35.0
func (w *Wizard) Back()
Back moves to the previous step, clamped at 0 (a no-op on the first step).
func (*Wizard) Current ¶ added in v0.35.0
func (w *Wizard) Current() *mvvm.Observable[int]
Current is the active step index as a shared mvvm.Observable: a host binds it (Set / Subscribe / two-way) — there is no settable Current field. Back / Next Set it (clamped to [0, len(Steps)-1]); subscribers are notified.
func (*Wizard) Draw ¶ added in v0.35.0
Draw paints the Steps strip, the active step's Body, and the Back/Next-or-Finish button row. A Wizard with no Steps paints nothing (there is nothing to show progress through). Back renders in ButtonSecondary tone (dimmed) on the first step; Next/Finish renders dimmed whenever the active step's CanAdvance forbids moving on.
func (*Wizard) Next ¶ added in v0.35.0
func (w *Wizard) Next()
Next advances to the following step when the current one's CanAdvance allows it. On the last step it instead invokes OnFinish (if set) and leaves Current unchanged — Next() is the "Finish" action once there is nowhere further to advance to.
func (*Wizard) OnEvent ¶ added in v0.35.0
OnEvent routes a click on the Back button to Back(), a click on the Next/Finish button to Next() (both no-ops when disabled — Current == 0 for Back, a failing CanAdvance for Next/Finish), and any other click that lands in the body area — or any non-click event — to the active step's Body, translated into its local coordinate space (the same pattern Notebook.OnEvent uses in notebook.go). A click that lands in neither the buttons nor the body (e.g. the empty strip band) is ignored.
type WizardStep ¶ added in v0.35.0
WizardStep is one page of a Wizard: a Title shown in the top Steps strip, a Body widget shown in the content area while the step is active, and an optional CanAdvance gate. CanAdvance is consulted before the Wizard lets the user move past this step; a nil CanAdvance means "always allowed" (the common case — a step with no validation).
Source Files
¶
- a11y.go
- a11y_more.go
- a11y_wave5.go
- a11y_wave6.go
- a11ywalk.go
- accelerator.go
- accordion.go
- action.go
- actionrow.go
- actionsheet.go
- addressbar.go
- agenda.go
- agenda_editor.go
- agenda_sidebar.go
- alert.go
- animator.go
- appdock.go
- areachart.go
- articlecard.go
- avatar.go
- backdrop.go
- badge.go
- banner.go
- barchart.go
- bidi.go
- border.go
- breadcrumbs.go
- browser.go
- builder.go
- button.go
- buttongroup.go
- calendar.go
- card.go
- cardframe.go
- cardmeta.go
- carousel.go
- charts_hover.go
- chatbubble.go
- check.go
- children.go
- chip.go
- clipboard.go
- codeeditor.go
- codeminimap.go
- collabsheet.go
- collabtext.go
- colorchooser.go
- colorpicker.go
- columnbrowser.go
- combobox.go
- commandpalette.go
- completion.go
- container.go
- contextmenu.go
- corner.go
- cyclebutton.go
- databaseeditor.go
- datepicker.go
- daterangepicker.go
- defaulttext.go
- density.go
- dialog.go
- diff.go
- dnd.go
- dock.go
- dockstyle.go
- dropdown.go
- dropzone.go
- easing.go
- entry.go
- expander.go
- export.go
- fab.go
- fallback.go
- filechooser.go
- focus.go
- foldertabs.go
- font.go
- fontchooser.go
- formfield.go
- galleryview.go
- gantt.go
- gauge.go
- gesture.go
- gesture_multitouch.go
- groupcard.go
- headerbar.go
- iconbutton.go
- icongrid.go
- icons.go
- image.go
- isoclip.go
- isocrdt.go
- isodiagram.go
- isodoc.go
- isoicon.go
- isoicon_anim.go
- isojson.go
- isolayer.go
- isopalette.go
- isorotate.go
- isoselect.go
- isotext.go
- isoview.go
- isozone.go
- kanban.go
- kbd.go
- keymap.go
- label.go
- layout.go
- linechart.go
- linkcard.go
- list.go
- loadmask.go
- logview.go
- markdowneditor.go
- markdownview.go
- material.go
- mediacard.go
- menu.go
- metrics.go
- momentum.go
- momentumscroll.go
- notebook.go
- notification.go
- orientation.go
- overlay.go
- pagedview.go
- pagination.go
- paging_toolbar.go
- paned.go
- piechart.go
- popover.go
- postcard.go
- progress.go
- progresscircle.go
- propertygrid.go
- pulltorefresh.go
- radarchart.go
- radio.go
- rangeslider.go
- raster.go
- rating.go
- richeditor.go
- richeditor_edit.go
- richeditor_layout.go
- richeditor_model.go
- richeditor_query.go
- richeditor_toolbar.go
- scale.go
- scatterchart.go
- scroll.go
- scrollbar.go
- scrolldrag.go
- scrollinput.go
- searchentry.go
- segmentedbar.go
- selection.go
- size.go
- skeleton.go
- sourcelist.go
- sparkline.go
- spinbutton.go
- spinner.go
- splitbutton.go
- spreadsheet.go
- spreadsheet_draw.go
- spreadsheet_input.go
- stack.go
- stat.go
- statusbar.go
- statusicon.go
- steps.go
- surface.go
- swipeactions.go
- switch.go
- syntheticbold.go
- syntheticitalic.go
- tabbar.go
- table.go
- tagfield.go
- terminalview.go
- textselection.go
- textview.go
- theme.go
- theme_adwaita.go
- theme_fluent.go
- theme_gtk.go
- theme_whitesur.go
- thumbnail.go
- timeline.go
- timepicker.go
- toast.go
- toggle.go
- toolbar.go
- tooltip.go
- treetable.go
- treeview.go
- truetype.go
- validation.go
- viewcontroller.go
- viewport.go
- viewswitcher.go
- wallpaper.go
- wheelpicker.go
- widget.go
- window.go
- windowdecoration.go
- wizard.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package anim is a backend-agnostic timeline driver for the go-widgets toolkit.
|
Package anim is a backend-agnostic timeline driver for the go-widgets toolkit. |
|
examples
|
|
|
isodemo
Package isodemo is a runnable, self-checking showcase of the toolkit's isometric diagram widget (toolkit.IsoDiagram) and its collaborative CRDT backing store (toolkit.IsoCRDTDocument).
|
Package isodemo is a runnable, self-checking showcase of the toolkit's isometric diagram widget (toolkit.IsoDiagram) and its collaborative CRDT backing store (toolkit.IsoCRDTDocument). |
|
isodemo/cmd/isodemo
command
Command isodemo renders the toolkit's isometric-diagram showcase to PNG files.
|
Command isodemo renders the toolkit's isometric-diagram showcase to PNG files. |
|
internal
|
|
|
formula
Package formula is the spreadsheet formula engine behind the toolkit's Spreadsheet widget: it lexes, parses and evaluates "=" expressions over an A1-addressed grid of cells, and maintains a dependency graph so an edit recomputes exactly the cells that (transitively) depend on it, with cycle detection that yields a #CIRC! error value instead of looping forever.
|
Package formula is the spreadsheet formula engine behind the toolkit's Spreadsheet widget: it lexes, parses and evaluates "=" expressions over an A1-addressed grid of cells, and maintains a dependency graph so an edit recomputes exactly the cells that (transitively) depend on it, with cycle detection that yields a #CIRC! error value instead of looping forever. |
|
Package isointerop is an OPTIONAL, SEPARATE interoperability layer for the isometric diagram widget (toolkit.IsoDiagram).
|
Package isointerop is an OPTIONAL, SEPARATE interoperability layer for the isometric diagram widget (toolkit.IsoDiagram). |
|
rougelex
module
|
|
|
Package scene adds an OPT-IN Evas-style damage / scene layer on top of the immediate-mode go-widgets/toolkit widget set.
|
Package scene adds an OPT-IN Evas-style damage / scene layer on top of the immediate-mode go-widgets/toolkit widget set. |
|
Package virtual adds live-data list virtualization on top of the go-widgets/toolkit widget set.
|
Package virtual adds live-data list virtualization on top of the go-widgets/toolkit widget set. |