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
- func DaysInMonth(year, month int) int
- func DeleteSelection(lines []string, sel Selection) []string
- 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 SelectionText(lines []string, sel Selection) string
- func TextWidth(text string) int
- func WeekdayOfFirst(year, month int) int
- type ActionRow
- type Alert
- type AlertKind
- type Avatar
- type Badge
- type Banner
- type BarChart
- type Base
- type Breadcrumbs
- type Button
- type ButtonStyle
- type Calendar
- type Card
- type ChatBubble
- type ChatSender
- type CheckButton
- type Chip
- type ColorChooser
- type DatePicker
- type Dialog
- type Diff
- type DiffKind
- type DiffLine
- type DropDown
- type DropZone
- type Entry
- type Event
- type EventKind
- type Expander
- type FileChooser
- type FormField
- type Frame
- type Grid
- type HBox
- type HeaderBar
- type IconButton
- type Image
- type Kbd
- type Label
- type LevelBar
- type LineChart
- type ListBox
- type MarkdownView
- type Menu
- type MenuBar
- 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 Notebook
- type NotebookTab
- type Notification
- type Pagination
- type Paned
- type PieChart
- type Popover
- type ProgressBar
- type ProgressCircle
- type RGBA
- type RadioButton
- type RadioGroup
- type RangeSlider
- type Rating
- type Rect
- type Scale
- type ScrollView
- type SearchEntry
- type Selection
- type Skeleton
- type SkeletonKind
- type SpinButton
- type Spinner
- type SplitButton
- type Stack
- type Stat
- type StatTrend
- type Statusbar
- type Steps
- type Switch
- type Table
- type TableColumn
- type TextView
- func (t *TextView) ClearSelection()
- func (t *TextView) CopySelection() string
- func (t *TextView) CutSelection() string
- func (t *TextView) DeleteSelection()
- func (t *TextView) Draw(p painter.Painter, theme *Theme)
- func (t *TextView) HasSelection() bool
- func (t *TextView) OnEvent(ev Event)
- func (t *TextView) Paste(text string)
- func (t *TextView) SelectAll()
- func (t *TextView) SelectionText() string
- func (t *TextView) SetSelection(sel Selection)
- func (t *TextView) SetText(s string)
- func (t *TextView) Text() string
- type Theme
- type Timeline
- type TimelineEvent
- type TimelineKind
- type Toast
- type ToastKind
- type ToggleButton
- type Toolbar
- type ToolbarItem
- type Tooltip
- type TreeNode
- type TreeView
- type VBox
- type ViewSwitcher
- type Widget
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 ( 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 ( 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 ( CalendarHeaderH = 22 CalendarCellW = 24 CalendarCellH = 18 )
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 // CardHeaderH is the height of the header strip when Title != "". CardHeaderH = GlyphHeight + 2*CardPadY CardFooterH = GlyphHeight + 2*CardPadY // 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 ( // 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 )
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 ( ColorChooserChannelH = 22 ColorChooserPreviewH = 36 ColorChooserPadX = 8 ColorChooserChannelPadY = 4 )
Sizing.
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 ( FileChooserTreeRatio = 35 // % of width for the tree pane FileChooserButtonStripH = 32 FileChooserPathH = 24 )
Sizing constants.
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.
const ( NotificationPadX = 12 NotificationPadY = 8 NotificationLife = 180 )
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 ( // 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 )
Rating sizing constants. Cells are square so the strip reads as a row of tiles; the small gap keeps them visually distinct without eating layout width.
const ( // SkeletonLineH is the pixel height of a single SkeletonText bar. SkeletonLineH = 10 // SkeletonLineGap is the vertical gap between two SkeletonText 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 )
Skeleton sizing constants. Values chosen to line up with the toolkit's GlyphHeight so a SkeletonText row visually replaces a row of body text without shifting the surrounding layout.
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 ( // 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 // 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. TimelineEventH = GlyphHeight + 4 // 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 )
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 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 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 DatePickerFieldH = GlyphHeight + 10
DatePickerFieldH is the pixel height of the closed field.
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 DiffLineH = GlyphHeight + 2
DiffLineH is the vertical stride between successive lines: one glyph tall plus two pixels of separation.
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 ExpanderHeaderH = 24
ExpanderHeaderH is the pixel height of the clickable header row.
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 FormFieldLabelH = GlyphHeight + 2
FormFieldLabelH is the height in pixels of the label row drawn at the top of a FormField. One glyph row plus 2px of breathing space keeps the label snug against the input beneath it without touching the glyph's descender pixels.
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 GlyphAdvance = 6
GlyphAdvance is the horizontal step from one glyph's origin to the next (5px glyph body + 1px inter-glyph spacing). All glyphs share the same advance so layout stays grid-aligned even with proportional shapes.
const GlyphHeight = 7
GlyphHeight is the per-glyph vertical extent in pixels.
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 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 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 PanedHandleW = 6
PanedHandleW is the pixel thickness of the splitter handle.
const PopoverMaxRows = 12
PopoverMaxRows caps the dropdown popover height; longer option lists can wrap in a ScrollView the caller supplies.
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 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 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 TableCellPadX = 4
TableCellPadX is the left/right pixel padding applied inside every header + body cell before its text lands.
const TableHeaderHeight = 24
TableHeaderHeight is the pixel height of the header row.
const TableRowHeight = 22
TableRowHeight is the pixel height of one body row.
const TreeChevronW = 14
TreeChevronW is the pixel column the chevron lives in.
const TreeIndentW = 16
TreeIndentW is the per-depth pixel indent.
Variables ¶
This section is empty.
Functions ¶
func DaysInMonth ¶
DaysInMonth returns the day count for (year, month).
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 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. Each glyph is rendered into a 5x7 cell whose origin is (x + k*GlyphAdvance, y) for the k-th rune. Unknown characters render as a blank but still advance the cursor so column alignment is preserved.
On a *painter.PixelPainter (the WUI + GUI back-end path) DrawText uses toolkit's own 60+ glyph 5x7 bitmap font by writing one pixel per lit bit. On any other painter (a *painter.CellPainter for a TUI, an SvgPainter for vector output) DrawText delegates to the painter's own Text primitive — a CellPainter maps one rune per cell + gets terminal-native text, an SvgPainter emits <text>.
Pixels are written as opaque ink (alpha forced to 0xFF unless the caller's ink already carries an alpha) with per-pixel clipping so glyphs that overflow the painter degrade gracefully.
func SelectionText ¶
SelectionText returns the substring covered by sel in lines (a TextView's Lines slice). Empty selection returns "".
func TextWidth ¶
TextWidth returns the pixel width that DrawText would occupy if it rendered text. Every character (known or unknown) consumes one GlyphAdvance slot so callers can pre-size text containers from len(text) alone -- this matches the dock's textWidth helper.
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 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) 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 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.
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 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 Badge ¶ added in v0.7.0
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 Theme.Accent with the ink in Theme.Background 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.
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 shape is approximated by clipping the four corner pixels: the body fills the full rectangle minus a one-pixel bite off each corner, which reads as "rounded" against the low-resolution 5x7 glyph aesthetic without touching the painter's curve primitives.
type Banner ¶ added in v0.8.0
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.
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) Draw ¶ added in v0.8.0
Draw paints the accent-filled strip + the Text ink. 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.
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
}
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.
func NewBarChart ¶ added in v0.13.0
NewBarChart builds a BarChart over the given values with an auto Y max.
type Base ¶
type Base struct {
// 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.
type Breadcrumbs ¶ added in v0.7.0
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.
The widget is passive display only: it computes per-segment X positions from TextWidth so a caller wanting hit-testing (e.g. clicking "Docs" to navigate up two levels) can walk the same offset table externally. HitTest / OnEvent stay as Base defaults.
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.
type Button ¶
type Button struct {
Base
Label string
OnClick func()
Style ButtonStyle // resting appearance; default is ButtonDefault
// 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) 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) OnEvent ¶
OnEvent dispatches click events to the OnClick callback. Other event kinds are ignored at this level (the parent container may have already pre-filtered).
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 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 )
type Calendar ¶
type Calendar struct {
Base
Year int
Month int // 1..12
Day int // selected day in [1, daysInMonth]
TodayY int
TodayM int
TodayD int
OnSelect func(y, m, d int)
}
Calendar renders a month grid (Mon..Sun columns, up to 6 rows) for a given (Year, Month). The currently-selected day is highlighted; click on a day-cell selects it + fires OnSelect with the absolute (Y, M, D) triple.
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.
func NewCalendar ¶
NewCalendar builds a Calendar for the given (year, month, day).
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 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) 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 ¶
CheckButton is a square checkbox + a label. Click toggles Checked + fires OnToggle. 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) Draw ¶
func (c *CheckButton) Draw(p painter.Painter, theme *Theme)
Draw paints the box + checkmark + label.
func (*CheckButton) OnEvent ¶
func (c *CheckButton) OnEvent(ev Event)
OnEvent flips Checked + fires OnToggle on click.
type Chip ¶ added in v0.9.0
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) Draw ¶ added in v0.9.0
Draw paints the pill body + text + optional close affordance. Auto- sizes Bounds when W is zero. The pill body is a filled SurfaceAlt rectangle stroked with a Border outline; the Text is 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 ColorChooser ¶
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 OnChange callback fires with the new RGBA whenever any channel moves.
The widget owns the RGBA value; the host reads .Color() to get the current pick + may also stash a hex string via SetHex if there is a sibling Entry the user can type into.
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) Draw ¶
func (c *ColorChooser) Draw(p painter.Painter, theme *Theme)
Draw paints the 3 sliders + preview swatch + hex label.
func (*ColorChooser) OnEvent ¶
func (c *ColorChooser) OnEvent(ev Event)
OnEvent handles clicks on the 3 tracks to move the channel knob.
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 DatePicker ¶ added in v0.11.0
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, closes the popup, and fires OnChange.
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) and renders the popup itself when Open, so it works standalone; a host that composites overlays on a separate surface can instead read Open + PopoverBounds and draw Cal there.
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) 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) 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 OnSelect closes the popup and fires OnChange.
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 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.
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.
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 DropDown ¶
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 + fires OnSelect.
Like Dialog, the popover's rendering surface is owned by the host app; the toolkit exposes Open + Selected so the host knows what to draw. 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) 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) OnEvent ¶
OnEvent toggles Open on click. Selection happens via Select() which the host wires to its popover ListBox's OnActivate.
func (*DropDown) PopoverBounds ¶
PopoverBounds returns the Rect the host should give to its popover ListBox: same X+W as the widget, positioned just below, height proportional to the option count (clamped to PopoverMaxRows rows).
type DropZone ¶ added in v0.9.0
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. The host is expected to flip Hover based on native dragenter / dragleave events; the widget itself does not synthesise its own drag+drop events. To keep the OnEvent surface testable, EventClick flips Hover in place (a lightweight simulator hook) and EventChar delivers a single path via ev.Code -- fired to OnDrop only when Hover is true, so the host can gate deliveries on the drag state.
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) 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) OnEvent ¶ added in v0.9.0
OnEvent implements the two host-driven behaviours: EventClick flips Hover (the simulator hook: real hosts flip Hover directly on dragenter / dragleave), and EventChar with Hover = true fires OnDrop with a one-element slice containing ev.Code (the delivered path). All other event kinds are ignored so a keyboard event bound for a sibling widget does not accidentally trigger a drop.
type Entry ¶
type Entry struct {
Base
Text string
Cursor int // rune index in [0, len(runes)]
Focused bool
OnChange func(text string)
OnSubmit func(text string)
}
Entry is a single-line text input. Receives focus on click, edits 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 widget treats 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.
type Event ¶
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.
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 )
type Expander ¶
type Expander struct {
Base
Label string
Expanded bool
Content Widget
OnExpand func(expanded bool)
}
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 Expanded + fires OnExpand.
When Expanded, Content occupies the remaining bounds below the header. When collapsed, only the header is drawn.
func NewExpander ¶
NewExpander builds an Expander with a label + initial content widget (may be nil to render header-only).
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) 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 FormField ¶ added in v0.9.0
type FormField struct {
Base
Label string
Help string // optional dim caption below the child
Error string // optional error caption below the child (takes precedence)
Child Widget // the actual input; may be nil
}
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) 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) 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.
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
// 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).
type Grid ¶
type Grid struct {
Base
// contains filtered or unexported fields
}
Grid lays children out in a fixed cols x rows table. Each cell is the same size (container W/cols, H/rows). Children are placed via Attach(child, col, row); a cell with no attached child stays empty.
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) 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.
type HBox ¶
type HBox struct {
Base
// Spacing is the gap in pixels between adjacent children. Defaults
// to 4 when left at zero (set to a negative value to truly disable
// the gap; negative values are clamped to zero at layout time).
Spacing int
// contains filtered or unexported fields
}
HBox is a horizontal flow container. Children are laid out left-to- right + share the container's width equally (minus Spacing gaps between adjacent children). 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 before forwarding.
func NewHBox ¶
func NewHBox() *HBox
NewHBox constructs an empty HBox. Callers add children via Append + then call SetBounds to trigger layout.
func (*HBox) Append ¶
Append adds w to the right of any existing children. Re-runs layout so the new child is positioned immediately + the caller doesn't have to remember to re-call SetBounds.
func (*HBox) Draw ¶
Draw paints every child in append order. Children render directly into the surface using their own Bounds; the HBox itself draws no background or border (it's a pure layout container).
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 does not intercept events itself; children receive events via the parent container's usual dispatch after HeaderBar has positioned them (Draw does the layout side-effect).
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) Draw ¶ added in v0.7.0
Draw paints the bar body, 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.
Positioning side effect: every Start / End widget's Bounds is updated to reflect its position inside the bar. The widget's original Bounds.W is preserved; Bounds.H is fitted to the bar's inner height (bar.H - HeaderBarPad). This mirrors GTK's HdyHeaderBar pattern — children carry their own preferred width but let the bar decide vertical placement.
type IconButton ¶ added in v0.9.0
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) 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) OnEvent ¶ added in v0.9.0
func (i *IconButton) OnEvent(ev Event)
OnEvent fires OnClick on EventClick; other event kinds are ignored. OnClick is nil-safe.
type Image ¶
type Image struct {
Base
Pixels []byte // RGBA bytes, W*H*4 in length
W, H int // source dimensions
}
Image paints a caller-supplied RGBA byte buffer into its bounds. If source dims == bounds, the blit is 1:1; otherwise the image is nearest-neighbour scaled to fit the bounds (no aspect-ratio preservation in v0.2).
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.
type Label ¶
Label is a passive widget that displays Text in the theme's OnSurface colour, drawn with the toolkit's 5x7 bitmap font. Left- aligned inside Bounds, vertically centred if Bounds.H exceeds GlyphHeight.
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.
type LevelBar ¶
LevelBar is the discrete cousin of ProgressBar: Max equal cells, the first Value cells filled in Accent + the rest in SurfaceAlt. Useful for battery / signal-strength style indicators.
func NewLevelBar ¶
NewLevelBar builds a LevelBar with the given Max (Value defaults to 0).
type LineChart ¶ added in v0.12.0
type LineChart struct {
Base
Series []float64
Min, Max float64 // Y bounds; when equal, taken from the data
}
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.
func NewLineChart ¶ added in v0.12.0
NewLineChart builds a LineChart over the given series with auto Y bounds.
type ListBox ¶
type ListBox struct {
Base
Items []string
Selected int // -1 = no selection
RowHeight int // pixels per row; default 18 via NewListBox
OnActivate func(idx int)
}
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.
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).
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.
func NewMarkdownView ¶ added in v0.15.0
func NewMarkdownView(source string) *MarkdownView
NewMarkdownView builds a MarkdownView over the given Markdown source.
type Menu ¶
type Menu struct {
Base
Items []MenuItem
Hover int // index of hovered row, -1 if none
OnClose func()
}
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) Draw ¶
Draw paints the menu's body + every row + a hover highlight on the currently-hovered row.
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) 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 ¶
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).
type Notebook ¶
type Notebook struct {
Base
Tabs []NotebookTab
Active int
OnTabChanged func(idx int)
}
Notebook is a tabbed container. The top NotebookTabStripH pixels host the tab strip; the rest is the active page's body. Clicking a tab swaps Active + fires OnTabChanged.
func NewNotebook ¶
func NewNotebook() *Notebook
NewNotebook returns an empty Notebook with no tabs + Active = 0.
func (*Notebook) AddTab ¶
AddTab appends a tab to the strip with label + the page widget shown when that tab is active.
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
Visible bool
// 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
}
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) 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.
type Pagination ¶ added in v0.8.0
Pagination is a page-navigator strip: a "<" prev button, a series of page-number buttons, and a ">" next button. Clicking a page number jumps Current to that page and fires OnChange; 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) 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) 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.
type Paned ¶
type Paned struct {
Base
First, Second Widget
Orientation int
Position int
OnPositionChanged func(pos int)
}
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) MoveHandle ¶
MoveHandle slides the splitter to pos (clamped) and re-lays out children. Fires OnPositionChanged with the new value.
type PieChart ¶ added in v0.14.0
type PieChart struct {
Base
Values []float64
Colors []RGBA // optional per-slice palette override; cycles by index
}
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.
func NewPieChart ¶ added in v0.14.0
NewPieChart builds a PieChart over the given values with the default palette.
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.
type ProgressBar ¶
ProgressBar is a horizontal bar with a filled portion proportional to Fraction in [0,1]. An optional Label is centred over the bar in Theme.OnSurface ink.
func NewProgressBar ¶
func NewProgressBar() *ProgressBar
NewProgressBar builds an empty (Fraction=0) ProgressBar with no label.
func (*ProgressBar) Draw ¶
func (pb *ProgressBar) Draw(p painter.Painter, theme *Theme)
Draw paints border + track + fill + optional centered label.
func (*ProgressBar) SetFraction ¶
func (p *ProgressBar) SetFraction(f float64)
SetFraction clamps + assigns Fraction. 0 = empty, 1 = full.
type ProgressCircle ¶ added in v0.9.0
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 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) 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 callers bypassing SetFraction still render a valid frame.
func (*ProgressCircle) SetFraction ¶ added in v0.9.0
func (pc *ProgressCircle) SetFraction(f float64)
SetFraction clamps + assigns Fraction. 0 = empty, 1 = full. Kept as a symmetrical helper to ProgressBar.SetFraction so both widgets present the same knob to callers.
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 RadioButton ¶
type RadioButton struct {
Base
Label string
Checked bool
OnToggle func(checked bool)
// contains filtered or unexported fields
}
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) 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) OnEvent ¶
func (r *RadioButton) OnEvent(ev Event)
OnEvent: on click, route through the group (if any) so siblings clear; otherwise toggle Checked locally.
type RadioGroup ¶
type RadioGroup struct {
Members []*RadioButton
Active int
}
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) 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
Low, High float64
OnChange func(low, high 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.
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) 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) 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 fires OnChange.
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.
type Rating ¶ added in v0.8.0
Rating is a horizontal star-rating strip: Max square cells drawn left-to-right, each carrying an ASCII asterisk overlay. Cells with index < Value fill in Theme.Accent (the "filled" state); cells with index >= Value fill in Theme.SurfaceAlt (the "empty" state). The star glyph itself is drawn as the ASCII "*" character because the toolkit's 5x7 bitmap font only covers ASCII — a Unicode "★" would render blank via DrawText's font5x7 lookup fall-through.
A click on cell index i sets Value = i+1 (so the leftmost cell yields 1, the rightmost Max) and fires OnChange when non-nil. Clicks outside the strip (Y outside the cell row, X to the right of the last cell) are ignored — the parent container already routes only hits inside Bounds() but a stray x >= Max*(RatingStarW+RatingStarGap) 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 cells than Max.
func (*Rating) Draw ¶ added in v0.8.0
Draw paints Max cells left-to-right. Filled cells use Theme.Accent + the accent-inverted ink; empty cells use Theme.SurfaceAlt + Theme.OnSurface. Every cell carries an ASCII "*" overlay so the row reads as stars even when the palette is monochrome.
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.
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.
type Scale ¶
Scale is a horizontal slider over a continuous Min..Max range. Click on the track jumps the thumb to that x-position + fires OnChange. 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.
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) 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.
type ScrollView ¶
type ScrollView struct {
Base
Child Widget
OffsetX, OffsetY int
// contains filtered or unexported fields
}
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 wide) is painted on the right edge in Theme.SurfaceAlt; a Theme.Accent thumb sized proportionally to the viewport/content ratio shows the current scroll position.
Wheel events scroll vertically; horizontal scrolling is supported via direct Scroll(dx, dy) calls (no horizontal scrollbar drawn in v0.2).
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) 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) 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) 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.
type SearchEntry ¶ added in v0.8.0
SearchEntry is a single-line text input decorated with a leading search-prefix glyph and, when 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 is intentionally passive about focus (no cursor, no caret) — it simply appends printable characters, deletes on Backspace, and clears on a click in the right-side X slot. Callers who need cursor navigation or IME support should reach for Entry / TextView instead.
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 run OnChange for the initial value so callers can wire the callback after construction without a spurious notification.
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 Text is non-empty) the trailing clear affordance.
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 Text is non-empty). Other events are ignored.
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 Skeleton ¶ added in v0.8.0
type Skeleton struct {
Base
Kind SkeletonKind
Lines int
}
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; there is no text or animation — the visual weight alone signals the pending state.
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 SkeletonAvatar / SkeletonBlock the value is stored verbatim but ignored by Draw.
type SkeletonKind ¶ added in v0.8.0
type SkeletonKind int
SkeletonKind selects the placeholder shape. Three kinds cover the dominant loading-state patterns: SkeletonText for a paragraph or a list row, SkeletonAvatar for an identity chip (paired with SkeletonText in a message row), SkeletonBlock for a media thumbnail or a card body.
const ( // SkeletonText draws N horizontal bars stacked vertically. The last // bar is 60% 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 shape — so a Skeleton row lines up pixel-for-pixel // with the real Avatar it will be swapped for once the data loads. SkeletonAvatar // SkeletonBlock draws one filled rectangle covering Bounds() inset // by SkeletonLinePad — the "media thumbnail loading" affordance. SkeletonBlock )
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.
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) 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) OnEvent ¶
func (s *SpinButton) OnEvent(ev Event)
OnEvent: click on the upper-right button increments; click on the lower-right button decrements.
type Spinner ¶
Spinner is an indeterminate loading indicator. When Active, Draw paints a "clock hand" from the centre of bounds rotated by Phase * 2π in Theme.Accent. Caller drives Phase via Tick(dt) so the animation cadence stays tied to the host's frame loop (no goroutine, no timer).
type SplitButton ¶ added in v0.9.0
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) 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) 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.
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) 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.
type Stat ¶ added in v0.9.0
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.
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; the caller assigns those fields directly to enable the bottom row.
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.
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 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.
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
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).
Steps is a passive display container: hit-testing / event routing are not implemented — a caller who needs a click-to-jump interaction walks the same layout math externally.
func NewSteps ¶ added in v0.7.0
NewSteps constructs a Steps indicator with the given labels + the initial current-step cursor.
type Switch ¶ added in v0.7.0
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.
Click flips On + fires OnToggle. Non-click events are ignored.
func NewSwitch ¶ added in v0.7.0
NewSwitch constructs a Switch with the given initial state. The OnToggle callback is nil by default; assign it after construction if the caller wants a click hook.
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
// Selected is the 0-indexed row highlighted with Theme.Accent;
// -1 (or any out-of-range value) means "no selection" and the
// zebra stripe pattern paints unmodified.
Selected int
}
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 | <- 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).
The widget is content-only: no per-cell events, no header sorting, no column drag-resize. A future revision layered on top may add them; the MVP is a passive spreadsheet-shaped viewer.
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.
type TableColumn ¶ added in v0.7.0
type TableColumn struct {
Title string
Width int // pixels; 0 = auto (equal share of remaining space)
}
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 TextView ¶
type TextView struct {
Base
Lines []string
CursorLine int
CursorCol int
Focused bool
OnChange func()
// Selection is the (start, end) range the host paints highlighted
// + range-deletes via DeleteSelection / cut+paste via
// CopySelection / CutSelection / Paste. An empty selection (Start
// == End) means "no selection"; HasSelection() is the convenience
// predicate.
Selection Selection
// Composition holds the in-progress IME preview string (dead-key
// output, CJK candidate, …). Non-empty while an IME composition
// is active; cleared on EventCompositionEnd. The Draw method
// paints it in a muted colour at the cursor position — the
// preview is NOT part of the buffer until the host commits via
// EventChar. Widgets that read Lines/Text() see only committed
// text, so downstream logic (search, syntax, autosave) never
// operates on half-formed input.
Composition string
}
TextView is the multi-line cousin of Entry. Lines are stored as a []string (one element per visible line); Cursor is a (line, col) position in rune coordinates. Wraps Entry's keyboard model with an added vertical axis (ArrowUp / ArrowDown / PageUp / PageDown).
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. v0.3 ships the raw buffer; v0.4 will add a SelectionStart/End pair for range ops.
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) ClearSelection ¶
func (t *TextView) ClearSelection()
ClearSelection collapses the selection to (CursorLine, CursorCol).
func (*TextView) CopySelection ¶
CopySelection returns the selected text + leaves the buffer untouched. Wired to a host clipboard via the host (the toolkit has no global clipboard).
func (*TextView) CutSelection ¶
CutSelection returns the selected text + 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) 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.
func (*TextView) SelectAll ¶
func (t *TextView) SelectAll()
SelectAll selects the entire buffer + parks the cursor at its end.
func (*TextView) SelectionText ¶
SelectionText returns the selected substring, or "".
func (*TextView) SetSelection ¶
SetSelection records a new (start, end) selection without moving the cursor.
type Theme ¶
type Theme struct {
Background RGBA
Surface RGBA
SurfaceAlt RGBA
OnBackground RGBA
OnSurface RGBA
Accent RGBA
Border 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 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 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 Timeline ¶ added in v0.9.0
type Timeline struct {
Base
Events []TimelineEvent
}
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.
Timeline is a passive display container — hit-testing / event routing are not implemented. A caller who wants click-to-focus walks the same layout math externally.
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) 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.
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
Visible bool
// Life is the number of Tick() calls remaining before the toast
// auto-hides. The zero value is a sentinel meaning "sticky": Tick
// is a no-op until the host assigns a positive Life. When Life is
// positive, each Tick decrements it; when the countdown reaches
// zero Visible is cleared.
Life int
}
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 mutates Visible + Life directly and iterates Tick over the collection.
The host drives Life via Tick() from its own animation loop (typically a rAF tick).
func NewToast ¶ added in v0.8.0
NewToast builds a hidden Toast with the given text + kind. The host sets Visible=true (typically via a Show helper it wraps around the widget) + assigns Life to arm the auto-dismiss countdown.
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 ¶
ToggleButton is a Button with a sticky on/off state. Click flips Pressed + fires OnToggle. Pressed = Theme.Accent face, unpressed = Theme.Surface; the label is rendered centered in the button.
func NewToggleButton ¶
func NewToggleButton(label string, pressed bool) *ToggleButton
NewToggleButton constructs a ToggleButton with the given label + initial state.
func (*ToggleButton) Draw ¶
func (t *ToggleButton) Draw(p painter.Painter, theme *Theme)
Draw paints the face + border + centred label.
func (*ToggleButton) OnEvent ¶
func (t *ToggleButton) OnEvent(ev Event)
OnEvent: click flips Pressed + fires OnToggle.
type Toolbar ¶
type Toolbar struct {
Base
Items []ToolbarItem
ButtonW int // default ToolbarButtonW
ButtonH int // default ToolbarButtonH
// 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.
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
Visible bool
Anchor Rect // widget the tooltip belongs to; positions below it
}
Tooltip is a small text bubble shown near the cursor when the user hovers over a target widget. The host app drives Visible + Anchor (typically toggled by a mouse-enter/leave handler with a 500 ms delay); the toolkit's role is the rendering geometry.
Auto-sized to the Text width + 8 px horizontal padding + 6 px vertical padding; appears just below/right of (Anchor.X, Anchor.Y).
func NewTooltip ¶
NewTooltip builds a hidden tooltip with the given text.
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 TreeView ¶
type TreeView struct {
Base
Root *TreeNode
Selected *TreeNode
OnActivate func(node *TreeNode)
RowHeight int // default 18
// 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.
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).
type VBox ¶
type VBox struct {
Base
// Spacing is the gap in pixels between adjacent children; same
// semantics as HBox.Spacing.
Spacing int
// contains filtered or unexported fields
}
VBox is the vertical analogue of HBox: children stack top-to-bottom, sharing the container's height + filling its width.
type ViewSwitcher ¶ added in v0.8.0
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 Current and fires OnChange with the new index.
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) 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) 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 updating Current + firing OnChange. 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.
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.
Source Files
¶
- actionrow.go
- alert.go
- avatar.go
- badge.go
- banner.go
- barchart.go
- breadcrumbs.go
- button.go
- calendar.go
- card.go
- chatbubble.go
- check.go
- chip.go
- colorchooser.go
- datepicker.go
- dialog.go
- diff.go
- dropdown.go
- dropzone.go
- entry.go
- expander.go
- filechooser.go
- font.go
- formfield.go
- headerbar.go
- iconbutton.go
- icons.go
- image.go
- kbd.go
- label.go
- layout.go
- linechart.go
- list.go
- markdownview.go
- menu.go
- notebook.go
- notification.go
- pagination.go
- paned.go
- piechart.go
- popover.go
- progress.go
- progresscircle.go
- radio.go
- rangeslider.go
- raster.go
- rating.go
- scale.go
- scroll.go
- searchentry.go
- selection.go
- skeleton.go
- spinbutton.go
- spinner.go
- splitbutton.go
- stack.go
- stat.go
- statusbar.go
- steps.go
- switch.go
- table.go
- textview.go
- theme.go
- theme_gtk.go
- theme_whitesur.go
- timeline.go
- toast.go
- toggle.go
- toolbar.go
- tooltip.go
- treeview.go
- viewswitcher.go
- widget.go